1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
use crate::{Action, Data, Flag, Predicate, Rule, Scalar, SerializableRegex, Token};
use std::fs;
use std::io;
use std::path::{Path, PathBuf};

pub fn run(working_dir: impl AsRef<Path>, data: &Data) -> io::Result<()> {
    for dir_entry in fs::read_dir(working_dir)? {
        let dir_entry = dbg!(dir_entry?);
        if dir_entry.file_type()?.is_dir() {
            continue;
        }
        for rule in &data.rules {
            run_rule(dir_entry.path(), rule)?;
        }
    }

    Ok(())
}

fn run_rule(path: impl AsRef<Path>, rule: &Rule) -> io::Result<()> {
    if path_matches_patterns(&path, rule.flags.contains(&Flag::IgnoreCase), &rule.patterns) {
        if let Some(predicate) = &rule.predicate {
            if file_matches_predicates(&path, predicate)? {
                return apply_action(&path, &rule.action);
            }
        } else {
            return apply_action(&path, &rule.action);
        }
    }

    Ok(())
}

fn apply_action(path: impl AsRef<Path>, action: &Action) -> io::Result<()> {
    println!("apply_action({:?}, {:?})", path.as_ref(), action);
    match action {
        Action::Delete => fs::remove_file(path),
        Action::Move { to } => {
            fs::copy(&path, to.join(&path))?;
            fs::remove_file(&path)
        }
    }
}

fn path_matches_patterns(
    path: impl AsRef<Path>,
    ignore_case: bool,
    patterns: &[SerializableRegex],
) -> bool {
    if patterns.is_empty() {
        return true;
    }

    let path = if let Some(path) = path.as_ref().to_str() {
        path
    } else {
        return false;
    };

    patterns.iter().map(|pat| pat.as_ref()).any(|pattern| {
        if ignore_case {
            pattern.is_match(&path.to_lowercase()) || pattern.is_match(&path.to_uppercase())
        } else {
            pattern.is_match(path)
        }
    })
}

fn file_matches_predicates(path: impl AsRef<Path>, predicate: &Predicate) -> io::Result<bool> {
    match predicate {
        Predicate::Comparison {
            token,
            operator,
            value,
        } => match (token, value) {
            (Token::Size, Scalar::Size(size)) => {
                let file_metadata = fs::metadata(path)?;

                Ok(operator.compare(&file_metadata.len(), size))
            }
            (Token::Age, Scalar::Time(_)) => unimplemented!(),
            _ => Ok(false),
        },
    }
}