]> git.r.bdr.sh - rbdr/page/blobdiff - src/file_handler/file_strategies/gemini.rs
Fix test
[rbdr/page] / src / file_handler / file_strategies / gemini.rs
index 905e1a9f07ecd7ba9a2dcc3efb07e928bfbaafb6..6cfb94d4d08b60162a37ff03266ef7c99a181160 100644 (file)
@@ -1,13 +1,35 @@
 pub struct Strategy {}
 
 pub struct Strategy {}
 
-use std::path::PathBuf;
+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, FileType, Strategy as FileHandlerStrategy};
+use crate::gemini_parser::parse;
+use crate::html_renderer::render_html;
+
+impl Strategy {
+    fn is_title(line: &str) -> bool {
+        line.starts_with("--- title:")
+    }
+
+    fn is_description(line: &str) -> bool {
+        line.starts_with("--- description:")
+    }
+
+    fn get_title(line: &str) -> &str {
+        line.split_once("--- title:").unwrap().1
+    }
+
+    fn get_description(line: &str) -> &str {
+        line.split_once("--- description:").unwrap().1
+    }
+}
 
 impl FileHandlerStrategy for Strategy {
 
 impl FileHandlerStrategy for Strategy {
-    fn is(&self, path: &PathBuf) -> bool {
+    fn is(&self, path: &Path) -> bool {
         if let Some(extension) = path.extension() {
         if let Some(extension) = path.extension() {
-            return !path.is_dir() && extension == "gmi"
+            return !path.is_dir() && extension == "gmi";
         }
         false
     }
         }
         false
     }
@@ -17,13 +39,306 @@ impl FileHandlerStrategy for Strategy {
     }
 
     fn can_handle(&self, file_type: &FileType) -> bool {
     }
 
     fn can_handle(&self, file_type: &FileType) -> bool {
-        match file_type {
-            FileType::Gemini => true,
-            _ => false,
+        matches!(file_type, FileType::Gemini)
+    }
+
+    fn handle_html(&self, source: &Path, destination: &Path, file: &File, layout: &str) {
+        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;
+        let mut title = "";
+        let mut description = "";
+        if let Some(slice) = lines.get(..2) {
+            for line in slice {
+                if Strategy::is_title(line) {
+                    title = Strategy::get_title(line).trim();
+                    lines_found += 1;
+                    continue;
+                }
+                if Strategy::is_description(line) {
+                    description = Strategy::get_description(line).trim();
+                    lines_found += 1;
+                    continue;
+                }
+            }
         }
         }
+
+        let gemini_source = lines[lines_found..].join("\n");
+        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 {
+                if Strategy::is_title(line) {
+                    lines_found += 1;
+                    continue;
+                }
+                if Strategy::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 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(gemini_source.as_bytes())
+            .unwrap();
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use std::fs::create_dir_all;
+
+    use super::*;
+
+    use test_utilities::*;
+
+    #[test]
+    fn detects_title() {
+        assert!(Strategy::is_title("--- title: Hello!"));
+    }
+
+    #[test]
+    fn does_not_detect_other_keys_as_title() {
+        assert!(!Strategy::is_title("--- description: Hello!"));
     }
 
     }
 
-    fn handle(&self, file: &File) {
-        println!("Should parse and copy {}", file.path.display())
+    #[test]
+    fn detects_description() {
+        assert!(Strategy::is_description("--- description: What is this?"));
+    }
+
+    #[test]
+    fn does_not_detect_other_keys_as_description() {
+        assert!(!Strategy::is_description("--- title: What is this?"));
+    }
+
+    #[test]
+    fn extracts_title() {
+        assert_eq!(Strategy::get_title("--- title: Hello!").trim(), "Hello!");
+    }
+
+    #[test]
+    fn extracts_description() {
+        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>
+",
+        );
     }
 }
     }
 }