mmv 0.1.0

lib to mass move files with template names
Documentation
#![forbid(unsafe_code)]

use std::path::{Path, PathBuf};

/// Parses path to directory and name, then parses name to vector of patterns with '*' delimeter
#[derive(Debug, PartialEq)]
pub struct ParsedPattern {
    directory: PathBuf,
    pattern: Vec<String>,
}

impl ParsedPattern {
    /// Parses path to directory and name,
    /// then parses name to vector of patterns with '*' delimeter,
    /// returning ParsedPattern
    /// 
    /// # Arguments
    /// 
    /// * `path` - A &Path object, representing path with tempate name of file (i.e. name with '*' symbols)
    /// If path is empty, pattern will be vec![""]; If path is '*', pattern will be vec![]
    /// 
    /// # Examples
    ///
    /// ```
    /// use mmv_lib::input_parser::ParsedPattern;
    /// use std::path::Path;
    /// 
    /// let path = Path::new("src/te*.txt");
    /// let parsed_pattern = ParsedPattern::new(&path);
    /// ```
    pub fn new(path: &Path) -> Self {
        Self {
            directory: match path.parent() {
                None => PathBuf::from("/"),
                Some(directory) => directory.to_owned(),
            },
            pattern: match path.file_name() {
                None => vec![String::from("")],
                Some(file_name_pattern) => file_name_pattern
                    .to_str()
                    .unwrap()
                    .split("*")
                    .map(|part| part.to_string())
                    .filter(|str| !str.is_empty())
                    .collect(),
            },
        }
    }

    /// Matching file name with pattern, and if it fits, return vector of fragments, which was read as template
    /// 
    /// # Arguments
    /// 
    /// * `file_name` - A &String, representing name of file.
    /// 
    /// # Error
    /// 
    /// If file name doesn't fit to the pattern, return ()
    /// 
    /// # Examples
    /// 
    /// ```
    /// use mmv_lib::input_parser::ParsedPattern;
    /// use std::path::Path;
    /// 
    /// let parsed_pattern = ParsedPattern::new(&Path::new("src/te*.*"));
    /// let file_name = String::from("test.txt");
    /// let fragments:Vec<String> = parsed_pattern.match_to_pattern(&file_name).unwrap();
    /// ```
    pub fn match_to_pattern(&self, file_name: &String) -> Result<Vec<String>, ()> {
        if self.pattern.is_empty() {
            return Ok(vec![file_name.clone()]);
        }

        if self.pattern.len() == 1 && self.pattern[0].is_empty() {
            match file_name.is_empty() {
                false => return Err(()),
                true => return Ok(Vec::<String>::new()),
            }
        }

        let mut file_name = file_name.clone();
        let mut found_fragments = Vec::<String>::new();
        let mut new_fragment = String::new();
        let mut first_pattern = true;

        for pattern_part in &self.pattern {
            while !file_name.is_empty() && !file_name.starts_with(pattern_part) {
                let (prefix, suffix) = file_name.split_at(1);
                new_fragment += prefix;
                file_name = suffix.to_string();
            }

            if file_name.starts_with(pattern_part) {
                if !first_pattern || !new_fragment.is_empty() {
                    found_fragments.push(new_fragment);
                }
                first_pattern = false;
                new_fragment = String::new();
                let (_, new_file_name) = file_name.split_at(pattern_part.len());
                file_name = new_file_name.to_string();
            } else {
                return Err(());
            }
        }

        if !file_name.is_empty() {
            found_fragments.push(file_name);
        }

        Ok(found_fragments)
    }

    pub fn get_directory(&self) -> &PathBuf {
        &self.directory
    }
}

#[test]
fn test_new() {
    let directory = PathBuf::from("/");
    let pattern = vec![String::from("")];
    let path = Path::new("");
    assert_eq!(
        ParsedPattern {
            directory: directory,
            pattern: pattern,
        },
        ParsedPattern::new(path)
    );
    let directory = PathBuf::from("src/");
    let pattern = vec![String::from("test.txt")];
    let path = Path::new("src/test.txt");
    assert_eq!(
        ParsedPattern {
            directory: directory,
            pattern: pattern,
        },
        ParsedPattern::new(path)
    );
    let directory = PathBuf::from("src/");
    let pattern = vec![String::from("test.")];
    let path = Path::new("src/test.*");
    assert_eq!(
        ParsedPattern {
            directory: directory,
            pattern: pattern,
        },
        ParsedPattern::new(path)
    );
    let directory = PathBuf::from("src/");
    let pattern = vec![String::from("t"), String::from(".")];
    let path = Path::new("src/t*.*");
    assert_eq!(
        ParsedPattern {
            directory: directory,
            pattern: pattern,
        },
        ParsedPattern::new(path)
    );
}

#[test]
fn test_matching() {
    let directory = PathBuf::from("src/");
    let pattern = vec![String::from("te"), String::from("t.")];
    let path = Path::new("src/te*t.*");
    let parsed_pattern = ParsedPattern::new(path);
    assert_eq!(ParsedPattern { directory, pattern }, parsed_pattern);
    assert_eq!(
        Ok(vec![String::from("s"), String::from("txt")]),
        parsed_pattern.match_to_pattern(&String::from("test.txt"))
    );
    let directory = PathBuf::from("src/");
    let pattern = vec![];
    let path = Path::new("src/*");
    let parsed_pattern = ParsedPattern::new(path);
    assert_eq!(ParsedPattern { directory, pattern }, parsed_pattern);
    assert_eq!(
        Ok(vec![String::from("test.txt")]),
        parsed_pattern.match_to_pattern(&String::from("test.txt"))
    );
    let directory = PathBuf::from("/");
    let pattern = vec![String::from("")];
    let path = Path::new("");
    let parsed_pattern = ParsedPattern::new(path);
    assert_eq!(ParsedPattern { directory, pattern }, parsed_pattern);
    assert_eq!(
        Err(()),
        parsed_pattern.match_to_pattern(&String::from("test.txt"))
    );
}