mmv_lib/
input_parser.rs

1#![forbid(unsafe_code)]
2
3use std::path::{Path, PathBuf};
4
5/// Parses path to directory and name, then parses name to vector of patterns with '*' delimeter
6#[derive(Debug, PartialEq)]
7pub struct ParsedPattern {
8    directory: PathBuf,
9    pattern: Vec<String>,
10}
11
12impl ParsedPattern {
13    /// Parses path to directory and name,
14    /// then parses name to vector of patterns with '*' delimeter,
15    /// returning ParsedPattern
16    /// 
17    /// # Arguments
18    /// 
19    /// * `path` - A &Path object, representing path with tempate name of file (i.e. name with '*' symbols)
20    /// If path is empty, pattern will be vec![""]; If path is '*', pattern will be vec![]
21    /// 
22    /// # Examples
23    ///
24    /// ```
25    /// use mmv_lib::input_parser::ParsedPattern;
26    /// use std::path::Path;
27    /// 
28    /// let path = Path::new("src/te*.txt");
29    /// let parsed_pattern = ParsedPattern::new(&path);
30    /// ```
31    pub fn new(path: &Path) -> Self {
32        Self {
33            directory: match path.parent() {
34                None => PathBuf::from("/"),
35                Some(directory) => directory.to_owned(),
36            },
37            pattern: match path.file_name() {
38                None => vec![String::from("")],
39                Some(file_name_pattern) => file_name_pattern
40                    .to_str()
41                    .unwrap()
42                    .split("*")
43                    .map(|part| part.to_string())
44                    .filter(|str| !str.is_empty())
45                    .collect(),
46            },
47        }
48    }
49
50    /// Matching file name with pattern, and if it fits, return vector of fragments, which was read as template
51    /// 
52    /// # Arguments
53    /// 
54    /// * `file_name` - A &String, representing name of file.
55    /// 
56    /// # Error
57    /// 
58    /// If file name doesn't fit to the pattern, return ()
59    /// 
60    /// # Examples
61    /// 
62    /// ```
63    /// use mmv_lib::input_parser::ParsedPattern;
64    /// use std::path::Path;
65    /// 
66    /// let parsed_pattern = ParsedPattern::new(&Path::new("src/te*.*"));
67    /// let file_name = String::from("test.txt");
68    /// let fragments:Vec<String> = parsed_pattern.match_to_pattern(&file_name).unwrap();
69    /// ```
70    pub fn match_to_pattern(&self, file_name: &String) -> Result<Vec<String>, ()> {
71        if self.pattern.is_empty() {
72            return Ok(vec![file_name.clone()]);
73        }
74
75        if self.pattern.len() == 1 && self.pattern[0].is_empty() {
76            match file_name.is_empty() {
77                false => return Err(()),
78                true => return Ok(Vec::<String>::new()),
79            }
80        }
81
82        let mut file_name = file_name.clone();
83        let mut found_fragments = Vec::<String>::new();
84        let mut new_fragment = String::new();
85        let mut first_pattern = true;
86
87        for pattern_part in &self.pattern {
88            while !file_name.is_empty() && !file_name.starts_with(pattern_part) {
89                let (prefix, suffix) = file_name.split_at(1);
90                new_fragment += prefix;
91                file_name = suffix.to_string();
92            }
93
94            if file_name.starts_with(pattern_part) {
95                if !first_pattern || !new_fragment.is_empty() {
96                    found_fragments.push(new_fragment);
97                }
98                first_pattern = false;
99                new_fragment = String::new();
100                let (_, new_file_name) = file_name.split_at(pattern_part.len());
101                file_name = new_file_name.to_string();
102            } else {
103                return Err(());
104            }
105        }
106
107        if !file_name.is_empty() {
108            found_fragments.push(file_name);
109        }
110
111        Ok(found_fragments)
112    }
113
114    pub fn get_directory(&self) -> &PathBuf {
115        &self.directory
116    }
117}
118
119#[test]
120fn test_new() {
121    let directory = PathBuf::from("/");
122    let pattern = vec![String::from("")];
123    let path = Path::new("");
124    assert_eq!(
125        ParsedPattern {
126            directory: directory,
127            pattern: pattern,
128        },
129        ParsedPattern::new(path)
130    );
131    let directory = PathBuf::from("src/");
132    let pattern = vec![String::from("test.txt")];
133    let path = Path::new("src/test.txt");
134    assert_eq!(
135        ParsedPattern {
136            directory: directory,
137            pattern: pattern,
138        },
139        ParsedPattern::new(path)
140    );
141    let directory = PathBuf::from("src/");
142    let pattern = vec![String::from("test.")];
143    let path = Path::new("src/test.*");
144    assert_eq!(
145        ParsedPattern {
146            directory: directory,
147            pattern: pattern,
148        },
149        ParsedPattern::new(path)
150    );
151    let directory = PathBuf::from("src/");
152    let pattern = vec![String::from("t"), String::from(".")];
153    let path = Path::new("src/t*.*");
154    assert_eq!(
155        ParsedPattern {
156            directory: directory,
157            pattern: pattern,
158        },
159        ParsedPattern::new(path)
160    );
161}
162
163#[test]
164fn test_matching() {
165    let directory = PathBuf::from("src/");
166    let pattern = vec![String::from("te"), String::from("t.")];
167    let path = Path::new("src/te*t.*");
168    let parsed_pattern = ParsedPattern::new(path);
169    assert_eq!(ParsedPattern { directory, pattern }, parsed_pattern);
170    assert_eq!(
171        Ok(vec![String::from("s"), String::from("txt")]),
172        parsed_pattern.match_to_pattern(&String::from("test.txt"))
173    );
174    let directory = PathBuf::from("src/");
175    let pattern = vec![];
176    let path = Path::new("src/*");
177    let parsed_pattern = ParsedPattern::new(path);
178    assert_eq!(ParsedPattern { directory, pattern }, parsed_pattern);
179    assert_eq!(
180        Ok(vec![String::from("test.txt")]),
181        parsed_pattern.match_to_pattern(&String::from("test.txt"))
182    );
183    let directory = PathBuf::from("/");
184    let pattern = vec![String::from("")];
185    let path = Path::new("");
186    let parsed_pattern = ParsedPattern::new(path);
187    assert_eq!(ParsedPattern { directory, pattern }, parsed_pattern);
188    assert_eq!(
189        Err(()),
190        parsed_pattern.match_to_pattern(&String::from("test.txt"))
191    );
192}