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
14impl 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}