Skip to main content

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