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
use std::{
    borrow::Cow,
    path::{Path, PathBuf},
};

use once_cell::sync::Lazy;
use regex::Regex;

pub fn build_path_for_challenge(path: &Path, challenge_name: &str) -> PathBuf {
    let challenge_name = sanitize_name(challenge_name);
    let ext = path.extension();

    let mut path = path.with_extension("").as_os_str().to_owned();
    path.push("_");
    path.push(&*challenge_name);
    let path = PathBuf::from(path);

    match ext {
        Some(ext) => path.with_extension(ext),
        None => path,
    }
}

fn sanitize_name(name: &str) -> Cow<'_, str> {
    static REMOVE_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r#"[^-\w.]"#).unwrap());
    static REPLACE_UNDERSCORE_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r#" |:"#).unwrap()); // space or colon
                                                                                                //
    let name = REPLACE_UNDERSCORE_REGEX.replace_all(name, "_");
    match name {
        Cow::Borrowed(borrow) => REMOVE_REGEX.replace_all(borrow, ""),
        Cow::Owned(owned) => {
            let result = REMOVE_REGEX.replace_all(&owned, "");
            Cow::Owned(result.to_string())
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn sanitize_replaces_correctly() {
        assert_eq!(sanitize_name("asdf"), "asdf");
        // colons and spaces replaced with underscores
        assert_eq!(sanitize_name("a b:c"), "a_b_c");
        // other symbols removed
        assert_eq!(sanitize_name("a£$%^&*()bc"), "abc");
        // . and - are allowed
        assert_eq!(sanitize_name("a.b-c"), "a.b-c");
        // all together
        assert_eq!(sanitize_name("foo$%. bar:baz"), "foo._bar_baz");
    }

    #[test]
    fn test_build_path() {
        let path = "/some/path.ext";
        let challenge = "challenge";
        let built_path = build_path_for_challenge(Path::new(path), challenge);
        assert_eq!(built_path, PathBuf::from("/some/path_challenge.ext"));
    }

    #[test]
    fn test_build_path_hidden_file() {
        let path = "/some/.path.ext";
        let challenge = "challenge";
        let built_path = build_path_for_challenge(Path::new(path), challenge);
        assert_eq!(built_path, PathBuf::from("/some/.path_challenge.ext"));
    }
}