Skip to content
This repository has been archived by the owner on Dec 3, 2024. It is now read-only.

Commit

Permalink
Semi-stable pattern matching
Browse files Browse the repository at this point in the history
  • Loading branch information
AryaveerSR committed Sep 2, 2023
1 parent 0d6abab commit 021b4c8
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 16 deletions.
37 changes: 28 additions & 9 deletions src/pattern.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
//! Module containing pattern matching implementation.
use std::{ffi::OsStr, path::PathBuf};

/// An enum for different types of supported patterns.
pub enum Pattern {
/// If the query contains the value.
Contains(String),
/// If the query is in the directory.
Directory(String),
/// If the query has the extension.
Extension(String),
/// If the query's directory contains a keyword
DirectoryContains(String),
/// EVERYTHING!!!!
All,
}

impl Pattern {
// Takes relatvie paths.
/// Function that matches a `query` with its pattern.
pub fn matches(&self, query: &PathBuf) -> bool {
match self {
Pattern::Contains(string) => query
.file_name()
.unwrap()
.to_string_lossy()
.contains(string),
Pattern::Directory(string) => query.starts_with(string),
Pattern::Extension(string) => {
string
== &query
Expand All @@ -26,38 +28,55 @@ impl Pattern {
.to_string_lossy()
.to_string()
}
Pattern::Contains(string) => query
.file_name()
.unwrap_or(OsStr::new(""))
.to_string_lossy()
.contains(string),
Pattern::Directory(string) => query.starts_with(string),
Pattern::DirectoryContains(string) => query.display().to_string().contains(string),
Pattern::All => true,
}
}

/// Function that parses a vec of pattern strings into a vec of `Pattern`
pub fn parse(pattern_vec: &Vec<String>) -> Vec<Self> {
// Empty basically turns it into a `dir` command.
if pattern_vec.is_empty() {
return vec![Pattern::All];
}

let mut patterns: Vec<Pattern> = vec![];

for pattern in pattern_vec {
// If it has a '/', it involves directories.
if pattern.starts_with("/") && pattern.chars().nth(1).is_some() {
if pattern.chars().nth(1) == Some('*') {
// Patterns like "/*tar" should match anything in the directories "tar", "target", etc.
patterns.push(Pattern::DirectoryContains(pattern[2..].to_string()));
} else {
// Patterns like "/target" should match anything only in the "target" directory.
patterns.push(Pattern::Directory(pattern[1..].to_string()));
}
} else if pattern.starts_with("*") {
if pattern.chars().nth(1).is_some() {
if pattern[1..].starts_with(".") {
// Patterns like "*.exe" should match all files having ".exe" extension.
// Files with a extension, say ".execute", will not be matched.
patterns.push(Pattern::Extension(pattern[2..].to_string()))
} else {
// Patterns like "*hello" should match "hello", "hello.exe", etc.
// It also matches the entire name, so "*p.exe" would match "help.exe" file.
patterns.push(Pattern::Contains(pattern[1..].to_string()))
}
} else {
return vec![Pattern::All];
}
} else if pattern.starts_with(".") {
// Patterns like ".exe" should do the same thing as "*.exe", that is match any files
// with ".exe" extension. Like "*.exe", it won't match a file with, say ".execute" extension.
patterns.push(Pattern::Extension(pattern[1..].to_string()))
} else {
// The fallback case is same as "*something" pattern, that is matching any file
// with the pattern string in its name.
patterns.push(Pattern::Contains(pattern.to_string()));
}
}
Expand Down
18 changes: 11 additions & 7 deletions src/recursive_match.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
//! Module implementing the `recursive_match` function, which, well, recursively matches files (and folders!)
//! in a given directory.
use crate::pattern::Pattern;
use anyhow::{Context, Result};
use std::{
Expand Down Expand Up @@ -40,17 +43,18 @@ pub fn recursive_match(
// Get a reference
let opts = &opts;

if file.metadata().unwrap().is_file() {
let mut does_match = true;
for i in pattern {
if !i.matches(&file.path().to_path_buf()) {
does_match = false;
}
let mut does_match = true;
// The variable stays true only if the file matches all patterns.
for i in pattern {
if !i.matches(&file.path().to_path_buf()) {
does_match = false;
}
}

if file.metadata().unwrap().is_file() {
does_match
} else {
opts.include_folders
opts.include_folders && does_match
}
};
// ..and another closure to decide whether to traverse a subdirectory provided depth isn't exceeded.
Expand Down

0 comments on commit 021b4c8

Please sign in to comment.