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