]> git.r.bdr.sh - rbdr/page/blobdiff - src/file_handler/file_strategies/gemini.rs
Format and lint the code
[rbdr/page] / src / file_handler / file_strategies / gemini.rs
index 04e2b6aae554e9cf2094bb1cbb1b6407096e2def..e987baba989aeecc7477af731480080b4a723960 100644 (file)
@@ -1,11 +1,12 @@
 pub struct Strategy {}
 
-use std::path::PathBuf;
-use std::io::Write;
 use std::fs::{create_dir_all, read_to_string, File as IOFile};
+use std::io::Write;
+use std::path::Path;
 
-use crate::file_handler::{File, FileType, FileHandlerStrategy};
+use crate::file_handler::{File, FileHandlerStrategy, FileType};
 use crate::gemini_parser::parse;
+use crate::html_renderer::render_html;
 
 impl Strategy {
     fn is_title(&self, line: &str) -> bool {
@@ -26,9 +27,9 @@ impl Strategy {
 }
 
 impl FileHandlerStrategy for Strategy {
-    fn is(&self, path: &PathBuf) -> bool {
+    fn is(&self, path: &Path) -> bool {
         if let Some(extension) = path.extension() {
-            return !path.is_dir() && extension == "gmi"
+            return !path.is_dir() && extension == "gmi";
         }
         false
     }
@@ -38,13 +39,10 @@ impl FileHandlerStrategy for Strategy {
     }
 
     fn can_handle(&self, file_type: &FileType) -> bool {
-        match file_type {
-            FileType::Gemini => true,
-            _ => false,
-        }
+        matches!(file_type, FileType::Gemini)
     }
 
-    fn handle(&self, source: &PathBuf, destination: &PathBuf, file: &File, layout: &String) {
+    fn handle_html(&self, source: &Path, destination: &Path, file: &File, layout: &str) {
         let gemini_contents = read_to_string(&file.path).unwrap();
 
         // Front matter extraction
@@ -52,34 +50,303 @@ impl FileHandlerStrategy for Strategy {
         let mut lines_found = 0;
         let mut title = "";
         let mut description = "";
-        for line in lines[..2].iter() {
-            if self.is_title(&line) {
-                title = self.get_title(&line).trim();
-                lines_found = lines_found + 1;
-                continue;
-            }
-            if self.is_description(&line) {
-                description = self.get_description(&line).trim();
-                lines_found = lines_found + 1;
-                continue;
+        if let Some(slice) = lines.get(..2) {
+            for line in slice.iter() {
+                if self.is_title(line) {
+                    title = self.get_title(line).trim();
+                    lines_found += 1;
+                    continue;
+                }
+                if self.is_description(line) {
+                    description = self.get_description(line).trim();
+                    lines_found += 1;
+                    continue;
+                }
             }
         }
 
         let gemini_source = lines[lines_found..].join("\n");
-        let content_html = parse(&gemini_source[..]);
+        let content_html = render_html(parse(&gemini_source[..]));
 
         let generated_html = layout
             .replace("{{ title }}", title)
             .replace("{{ description }}", description)
             .replace("{{ content }}", &content_html[..]);
 
+        let relative_path = file.path.strip_prefix(source).unwrap();
+        let mut complete_destination = destination.join(relative_path);
+        complete_destination.set_extension("html");
+        let destination_parent = complete_destination.parent().unwrap();
+        create_dir_all(destination_parent).unwrap();
+
+        let mut destination_file = IOFile::create(&complete_destination).unwrap();
+        destination_file
+            .write_all(generated_html.as_bytes())
+            .unwrap();
+    }
+
+    fn handle_gemini(&self, source: &Path, destination: &Path, file: &File) {
+        let gemini_contents = read_to_string(&file.path).unwrap();
+
+        // Front matter extraction
+        let lines: Vec<&str> = gemini_contents.split("\n").collect();
+        let mut lines_found = 0;
+        if let Some(slice) = lines.get(..2) {
+            for line in slice.iter() {
+                if self.is_title(line) {
+                    lines_found += 1;
+                    continue;
+                }
+                if self.is_description(line) {
+                    lines_found += 1;
+                    continue;
+                }
+            }
+        }
+
+        let gemini_source = lines[lines_found..].join("\n");
 
-        let relative_path = file.path.strip_prefix(&source).unwrap();
+        let relative_path = file.path.strip_prefix(source).unwrap();
         let complete_destination = destination.join(relative_path);
         let destination_parent = complete_destination.parent().unwrap();
         create_dir_all(destination_parent).unwrap();
 
         let mut destination_file = IOFile::create(&complete_destination).unwrap();
-        destination_file.write_all(generated_html.as_bytes()).unwrap();
+        destination_file
+            .write_all(gemini_source.as_bytes())
+            .unwrap();
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use std::fs::create_dir_all;
+
+    use super::*;
+
+    use test_utilities::*;
+
+    #[test]
+    fn detects_title() {
+        let strategy = Strategy {};
+        assert!(strategy.is_title("--- title: Hello!"));
+    }
+
+    #[test]
+    fn does_not_detect_other_keys_as_title() {
+        let strategy = Strategy {};
+        assert!(!strategy.is_title("--- description: Hello!"));
+    }
+
+    #[test]
+    fn detects_description() {
+        let strategy = Strategy {};
+        assert!(strategy.is_description("--- description: What is this?"));
+    }
+
+    #[test]
+    fn does_not_detect_other_keys_as_description() {
+        let strategy = Strategy {};
+        assert!(!strategy.is_description("--- title: What is this?"));
+    }
+
+    #[test]
+    fn extracts_title() {
+        let strategy = Strategy {};
+        assert_eq!(strategy.get_title("--- title: Hello!").trim(), "Hello!");
+    }
+
+    #[test]
+    fn extracts_description() {
+        let strategy = Strategy {};
+        assert_eq!(
+            strategy
+                .get_description("--- description: What is this?")
+                .trim(),
+            "What is this?"
+        );
+    }
+
+    #[test]
+    fn identifies_gemini_file() {
+        let test_dir = setup_test_dir();
+        create_test_file(&test_dir.join("test.gmi"), "");
+        let strategy = Strategy {};
+        assert!(strategy.is(&test_dir.join("test.gmi")));
+    }
+
+    #[test]
+    fn rejects_non_gemini_file() {
+        let test_dir = setup_test_dir();
+        create_test_file(&test_dir.join("_layout.html"), "");
+        create_test_file(&test_dir.join("image.png"), "");
+        let strategy = Strategy {};
+        assert!(!strategy.is(&test_dir.join("_layout.html")));
+        assert!(!strategy.is(&test_dir.join("image.png")));
+        assert!(!strategy.is(&test_dir));
+    }
+
+    #[test]
+    fn identifies_gemini_type() {
+        let strategy = Strategy {};
+        assert!(matches!(strategy.identify(), FileType::Gemini));
+    }
+
+    #[test]
+    fn handles_gemini_type() {
+        let strategy = Strategy {};
+        assert!(strategy.can_handle(&FileType::Gemini));
+    }
+
+    #[test]
+    fn rejects_non_gemini_types() {
+        let strategy = Strategy {};
+        assert!(!strategy.can_handle(&FileType::Layout));
+        assert!(!strategy.can_handle(&FileType::File));
+        assert!(!strategy.can_handle(&FileType::Unknown));
+    }
+
+    #[test]
+    fn handles_html_generation() {
+        let test_dir = setup_test_dir();
+        let source_dir = test_dir.join("source");
+        let output_dir = test_dir.join("output");
+        create_dir_all(&source_dir).expect("Could not create source test directory");
+        create_dir_all(&output_dir).expect("Could not create output test directory");
+        let layout = "\
+<html>
+<head>
+<title>{{ title }}</title>
+<meta name=\"description\" content=\"{{ description }}\">
+</head>
+<body>{{ content }}</body>
+</html>
+";
+        create_test_file(
+            &source_dir.join("test.gmi"),
+            "\
+--- title: Page Is Cool!
+--- description: My Description
+# Test
+Hello world
+",
+        );
+
+        let strategy = Strategy {};
+        let file = File {
+            path: source_dir.join("test.gmi"),
+            file_type: FileType::Gemini,
+        };
+
+        strategy.handle_html(&source_dir, &output_dir, &file, &layout);
+
+        let html_output = output_dir.join("test.html");
+        assert!(html_output.exists());
+        assert_file_contents(
+            &html_output,
+            "\
+<html>
+<head>
+<title>Page Is Cool!</title>
+<meta name=\"description\" content=\"My Description\">
+</head>
+<body><section class=\"h1\">
+<h1> Test</h1>
+<p>Hello world</p>
+</section>
+</body>
+</html>
+",
+        );
+    }
+
+    #[test]
+    fn handles_gemini_generation() {
+        let test_dir = setup_test_dir();
+        let source_dir = test_dir.join("source");
+        let output_dir = test_dir.join("output");
+        create_dir_all(&source_dir).expect("Could not create source test directory");
+        create_dir_all(&output_dir).expect("Could not create output test directory");
+        create_test_file(
+            &source_dir.join("test.gmi"),
+            "\
+--- title: Page Is Cool!
+--- description: My Description
+# Test
+Hello world
+",
+        );
+
+        let strategy = Strategy {};
+        let file = File {
+            path: source_dir.join("test.gmi"),
+            file_type: FileType::Gemini,
+        };
+
+        strategy.handle_gemini(&source_dir, &output_dir, &file);
+
+        let gemini_output = output_dir.join("test.gmi");
+        assert!(gemini_output.exists());
+        assert_file_contents(
+            &gemini_output,
+            "\
+# Test
+Hello world
+",
+        );
+    }
+
+    #[test]
+    fn handles_nested_structure() {
+        let test_dir = setup_test_dir();
+        let source_dir = test_dir.join("source");
+        let output_dir = test_dir.join("output");
+        create_dir_all(&source_dir.join("nested")).expect("Could not create source test directory");
+        create_dir_all(&output_dir).expect("Could not create output test directory");
+        let layout = "\
+<html>
+<head>
+<title>{{ title }}</title>
+<meta name=\"description\" content=\"{{ description }}\">
+</head>
+<body>{{ content }}</body>
+</html>
+";
+        create_test_file(
+            &source_dir.join("nested/test.gmi"),
+            "\
+--- title: Page Is Cool!
+--- description: My Description
+# Test
+Hello world
+",
+        );
+
+        let strategy = Strategy {};
+        let file = File {
+            path: source_dir.join("nested/test.gmi"),
+            file_type: FileType::Gemini,
+        };
+
+        strategy.handle_html(&source_dir, &output_dir, &file, &layout);
+
+        let html_output = output_dir.join("nested/test.html");
+        assert!(html_output.exists());
+        assert_file_contents(
+            &html_output,
+            "\
+<html>
+<head>
+<title>Page Is Cool!</title>
+<meta name=\"description\" content=\"My Description\">
+</head>
+<body><section class=\"h1\">
+<h1> Test</h1>
+<p>Hello world</p>
+</section>
+</body>
+</html>
+",
+        );
     }
 }