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