]> git.r.bdr.sh - rbdr/page/blob - src/file_handler/mod.rs
Add first tests
[rbdr/page] / src / file_handler / mod.rs
1 mod file_strategies;
2
3 use file_strategies::file::Strategy as FileStrategy;
4 use file_strategies::gemini::Strategy as GeminiStrategy;
5 use file_strategies::layout::Strategy as LayoutStrategy;
6
7 use std::path::PathBuf;
8 use std::fs::read_to_string;
9
10 pub struct FileHandler {
11 pub strategies: Vec<Box<dyn FileHandlerStrategy>>,
12 pub layout: Option<String>
13 }
14
15 impl Default for FileHandler {
16 fn default() -> FileHandler {
17 FileHandler {
18 strategies: vec![
19 Box::new(GeminiStrategy{}),
20 Box::new(LayoutStrategy{}),
21 Box::new(FileStrategy{}),
22 ],
23 layout: None
24 }
25 }
26 }
27
28 impl FileHandler {
29 pub fn identify(&self, path: &PathBuf) -> FileType {
30 for strategy in self.strategies.iter() {
31 if strategy.is(&path) {
32 return strategy.identify();
33 }
34 }
35 FileType::Unknown
36 }
37
38 pub fn get_layout_or_panic(&mut self, files: &[File]) -> Result<(), &str> {
39 for file in files {
40 match file.file_type {
41 FileType::Layout => {
42 let layout_text = read_to_string(&file.path).unwrap();
43 self.layout = Some(layout_text);
44 return Ok(());
45 },
46 _ => {}
47 }
48 }
49 Err("No layout found. Please ensure there's a _layout.html file at the root")
50 }
51
52 pub fn handle_all(&self, source: &PathBuf, html_destination: &PathBuf, gemini_destination: &PathBuf, files: &[File]) {
53 files.iter().for_each(|file| {
54 self.handle(source, html_destination, gemini_destination, file);
55 });
56 }
57
58 pub fn handle(&self, source: &PathBuf, html_destination: &PathBuf, gemini_destination: &PathBuf, file: &File) {
59 if let Some(strategy) = self.strategies
60 .iter()
61 .find(|s| s.can_handle(&file.file_type))
62 {
63 let layout = self.layout.as_ref()
64 .expect("Layout should be initialized before handling files");
65 strategy.handle_html(source, html_destination, file, layout);
66 strategy.handle_gemini(source, gemini_destination, file);
67 return;
68 }
69 }
70 }
71
72 pub trait FileHandlerStrategy {
73 fn is(&self, path: &PathBuf) -> bool;
74 fn identify(&self) -> FileType;
75 fn can_handle(&self, file_type: &FileType) -> bool;
76 fn handle_html(&self, source: &PathBuf, destination: &PathBuf, file: &File, layout: &str);
77 fn handle_gemini(&self, source: &PathBuf, destination: &PathBuf, file: &File);
78 }
79
80 #[derive(Debug, Clone, PartialEq)]
81 pub enum FileType {
82 Gemini,
83 File,
84 Layout,
85 Unknown,
86 }
87
88 #[derive(PartialEq, Debug)]
89 pub struct File {
90 pub path: PathBuf,
91 pub file_type: FileType,
92 }
93
94
95 #[cfg(test)]
96 mod tests {
97 use super::*;
98 use std::path::PathBuf;
99
100 fn create_test_file(path: &str, file_type: FileType) -> File {
101 File {
102 path: PathBuf::from(path),
103 file_type,
104 }
105 }
106
107 #[test]
108 fn test_identify_gemini_file() {
109 let handler = FileHandler::default();
110 let path = PathBuf::from("test.gmi");
111 assert!(matches!(handler.identify(&path), FileType::Gemini));
112 }
113
114 #[test]
115 fn test_identify_layout_file() {
116 let handler = FileHandler::default();
117 let path = PathBuf::from("_layout.html");
118 assert!(matches!(handler.identify(&path), FileType::Layout));
119 }
120
121 #[test]
122 fn test_identify_regular_file() {
123 let handler = FileHandler::default();
124 let path = PathBuf::from("regular.html");
125 assert!(matches!(handler.identify(&path), FileType::File));
126 }
127
128 #[test]
129 fn test_identify_unknown_file() {
130 let handler = FileHandler::default();
131 let path = PathBuf::from("tests/fixtures");
132 assert!(matches!(handler.identify(&path), FileType::Unknown));
133 }
134
135 #[test]
136 fn test_get_layout_success() {
137 let mut handler = FileHandler::default();
138 let files = vec![
139 create_test_file("test.gmi", FileType::Gemini),
140 create_test_file("tests/fixtures/_layout.html", FileType::Layout),
141 create_test_file("regular.html", FileType::File),
142 ];
143
144 assert!(handler.get_layout_or_panic(&files).is_ok());
145 }
146
147 #[test]
148 fn test_get_layout_failure() {
149 let mut handler = FileHandler::default();
150 let files = vec![
151 create_test_file("test.gmi", FileType::Gemini),
152 create_test_file("regular.html", FileType::File),
153 ];
154
155 assert!(handler.get_layout_or_panic(&files).is_err());
156 }
157
158 // Mock strategy for testing
159 struct MockStrategy {
160 is_match: bool,
161 file_type: FileType,
162 }
163
164 impl FileHandlerStrategy for MockStrategy {
165 fn is(&self, _path: &PathBuf) -> bool {
166 self.is_match
167 }
168
169 fn identify(&self) -> FileType {
170 self.file_type.clone()
171 }
172
173 fn can_handle(&self, file_type: &FileType) -> bool {
174 &self.file_type == file_type
175 }
176
177 fn handle_html(&self, _source: &PathBuf, _destination: &PathBuf, _file: &File, _layout: &str) {}
178 fn handle_gemini(&self, _source: &PathBuf, _destination: &PathBuf, _file: &File) {}
179 }
180
181 #[test]
182 fn test_custom_strategy() {
183 let mock_strategy = MockStrategy {
184 is_match: true,
185 file_type: FileType::Gemini,
186 };
187
188 let handler = FileHandler {
189 strategies: vec![Box::new(mock_strategy)],
190 layout: None,
191 };
192
193 let path = PathBuf::from("test.whatever");
194 assert!(matches!(handler.identify(&path), FileType::Gemini));
195 }
196
197 #[test]
198 fn test_handle_all_empty_files() {
199 let handler = FileHandler::default();
200 let files: Vec<File> = vec![];
201
202 // Should not panic with empty vector
203 handler.handle_all(
204 &PathBuf::from("source"),
205 &PathBuf::from("tests/fixtures/output"),
206 &PathBuf::from("tests/fixtures/output"),
207 &files
208 );
209 }
210
211 #[test]
212 fn test_handle_with_layout() {
213 let mut handler = FileHandler::default();
214 handler.layout = Some("test layout".to_string());
215
216 let file = create_test_file("tests/fixtures/test1.gmi", FileType::Gemini);
217
218 // Should not panic with valid layout
219 handler.handle(
220 &PathBuf::from(""),
221 &PathBuf::from("tests/fixtures/output"),
222 &PathBuf::from("tests/fixtures/output"),
223 &file
224 );
225 }
226
227 #[test]
228 #[should_panic(expected = "Layout should be initialized before handling files")]
229 fn test_handle_without_layout() {
230 let handler = FileHandler::default();
231 let file = create_test_file("test.gmi", FileType::Gemini);
232
233 handler.handle(
234 &PathBuf::from("source"),
235 &PathBuf::from("tests/fixtures/output"),
236 &PathBuf::from("tests/fixtures/output"),
237 &file
238 );
239 }
240
241 #[test]
242 fn test_slice_handling() {
243 let mut handler = FileHandler::default();
244 let files = vec![
245 create_test_file("tests/fixtures/test1.gmi", FileType::Gemini),
246 create_test_file("tests/fixtures/_layout.html", FileType::Layout),
247 create_test_file("tests/fixtures/test2.gmi", FileType::Gemini),
248 create_test_file("tests/fixtures/test3.gmi", FileType::Gemini),
249 ];
250
251 let _ = handler.get_layout_or_panic(&files[1..]);
252
253 // Test with slice
254 handler.handle_all(
255 &PathBuf::from(""),
256 &PathBuf::from("tests/fixtures/output"),
257 &PathBuf::from("tests/fixtures/output"),
258 &files[1..] // Test with slice of last three elements
259 );
260 }
261 }