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