lb_rs/model/
filename.rs

1use crate::model::file::File;
2
3pub const MAX_FILENAME_LENGTH: usize = 230;
4pub const MAX_ENCRYPTED_FILENAME_LENGTH: usize = MAX_FILENAME_LENGTH + 24;
5
6#[derive(Debug, PartialEq, Eq)]
7pub enum DocumentType {
8    Text,
9    Drawing,
10    Other,
11}
12
13// todo: be more exhaustive
14impl DocumentType {
15    pub fn from_file_name_using_extension(name: &str) -> DocumentType {
16        match name.split('.').next_back() {
17            Some("md") | Some("txt") => DocumentType::Text,
18            Some("svg") => DocumentType::Drawing,
19            _ => DocumentType::Other,
20        }
21    }
22}
23
24#[derive(PartialEq, Eq, Debug, Clone)]
25pub struct NameComponents {
26    pub name: String,
27    pub variant: Option<usize>,
28    pub extension: Option<String>,
29}
30
31impl NameComponents {
32    pub fn from(file_name: &str) -> NameComponents {
33        let extension_location = file_name.rfind('.').and_then(|location| {
34            if location == file_name.len() - 1 { None } else { Some(location) }
35        });
36
37        let name_with_variant = match extension_location {
38            Some(location) => &file_name[..location],
39            None => file_name,
40        };
41
42        let mut variant_location = name_with_variant.rfind('-');
43
44        let variant = variant_location
45            .map(|location| name_with_variant[location + 1..].to_string())
46            .and_then(|maybe_variant| maybe_variant.parse::<usize>().ok());
47
48        if variant.is_none() {
49            variant_location = None
50        }
51
52        let name = {
53            let name_right_bound =
54                variant_location.unwrap_or_else(|| extension_location.unwrap_or(file_name.len()));
55            file_name[0..name_right_bound].to_string()
56        };
57
58        let extension = extension_location.map(|location| file_name[location + 1..].to_string());
59
60        NameComponents { name, variant, extension }
61    }
62
63    pub fn generate_next(&self) -> NameComponents {
64        self.generate_incremented(1)
65    }
66
67    pub fn generate_incremented(&self, n: usize) -> NameComponents {
68        let mut next = self.clone();
69        next.variant = Some(self.variant.unwrap_or(0) + n);
70        next
71    }
72
73    pub fn next_in_children(&mut self, children: Vec<File>) {
74        if !children.iter().any(|f| f.name == self.to_name()) {
75            return;
76        }
77
78        self.variant = Some(self.variant.unwrap_or_default() + 1);
79        self.next_in_children(children);
80    }
81
82    pub fn to_name(&self) -> String {
83        match (&self.variant, &self.extension) {
84            (Some(variant), Some(extension)) => format!("{}-{}.{}", self.name, variant, extension),
85            (Some(variant), None) => format!("{}-{}", self.name, variant),
86            (None, Some(extension)) => format!("{}.{}", self.name, extension),
87            (None, None) => self.name.to_string(),
88        }
89    }
90}
91
92#[cfg(test)]
93mod unit_tests {
94    use crate::model::filename::NameComponents;
95
96    fn from_components(
97        name: &str, variant: Option<usize>, extension: Option<&str>,
98    ) -> NameComponents {
99        NameComponents {
100            name: name.to_string(),
101            variant,
102            extension: extension.map(|str| str.to_string()),
103        }
104    }
105
106    #[test]
107    fn test_name_components() {
108        assert_eq!(NameComponents::from("test-1.md"), from_components("test", Some(1), Some("md")));
109        assert_eq!(NameComponents::from("test-.md"), from_components("test-", None, Some("md")));
110        assert_eq!(NameComponents::from(".md"), from_components("", None, Some("md")));
111        assert_eq!(NameComponents::from(""), from_components("", None, None));
112        assert_eq!(
113            NameComponents::from("test-file.md"),
114            from_components("test-file", None, Some("md"))
115        );
116        assert_eq!(
117            NameComponents::from("test-file-1.md"),
118            from_components("test-file", Some(1), Some("md"))
119        );
120        assert_eq!(
121            NameComponents::from("test-file-1.md."),
122            from_components("test-file-1.md.", None, None)
123        );
124        assert_eq!(
125            NameComponents::from("test-file-1.m"),
126            from_components("test-file", Some(1), Some("m"))
127        );
128        assert_eq!(
129            NameComponents::from("test-file-100.m"),
130            from_components("test-file", Some(100), Some("m"))
131        );
132        assert_eq!(
133            NameComponents::from("test-file--100.m"),
134            from_components("test-file-", Some(100), Some("m"))
135        );
136        assert_eq!(
137            NameComponents::from("test-file-.-100.m"),
138            from_components("test-file-.", Some(100), Some("m"))
139        );
140        assert_eq!(NameComponents::from("."), from_components(".", None, None));
141        assert_eq!(NameComponents::from("-1."), from_components("-1.", None, None));
142        assert_eq!(NameComponents::from("-1."), from_components("-1.", None, None));
143        assert_eq!(NameComponents::from("test"), from_components("test", None, None));
144        assert_eq!(NameComponents::from("test-32"), from_components("test", Some(32), None));
145    }
146
147    fn assert_symmetry(name: &str) {
148        assert_eq!(NameComponents::from(name).to_name(), name);
149    }
150
151    #[test]
152    fn test_back_to_name() {
153        assert_symmetry("test-1.md");
154        assert_symmetry("test-.md");
155        assert_symmetry(".md");
156        assert_symmetry("");
157        assert_symmetry("test-file.md");
158        assert_symmetry("test-file-1.md");
159        assert_symmetry("test-file-1.md.");
160        assert_symmetry("test-file-1.m");
161        assert_symmetry("test-file-100.m");
162        assert_symmetry("test-file--100.m");
163        assert_symmetry("test-file-.-100.m");
164        assert_symmetry(".");
165        assert_symmetry("-1.");
166        assert_symmetry("-1.");
167        assert_symmetry("test");
168        assert_symmetry("test-32");
169    }
170
171    #[test]
172    fn test_next_variant() {
173        assert_eq!(NameComponents::from("test.md").generate_next().to_name(), "test-1.md");
174        assert_eq!(NameComponents::from("test-2.md").generate_next().to_name(), "test-3.md");
175    }
176}