]> git.r.bdr.sh - rbdr/page/commitdiff
Format and lint the code
authorRuben Beltran del Rio <redacted>
Sat, 4 Jan 2025 00:41:15 +0000 (01:41 +0100)
committerRuben Beltran del Rio <redacted>
Sat, 4 Jan 2025 00:41:15 +0000 (01:41 +0100)
18 files changed:
Makefile
src/file_finder.rs
src/file_handler/file_strategies/file.rs
src/file_handler/file_strategies/gemini.rs
src/file_handler/file_strategies/layout.rs
src/file_handler/mod.rs
src/gemini_parser.rs
src/html_renderer.rs
src/main.rs
tests/cli_test.rs
tests/fixtures/_layout.html [deleted file]
tests/fixtures/assets/style.css [deleted file]
tests/fixtures/image.png [deleted file]
tests/fixtures/nested/nested.gmi [deleted file]
tests/fixtures/regular.html [deleted file]
tests/fixtures/test1.gmi [deleted file]
tests/fixtures/test2.gmi [deleted file]
tests/fixtures/test3.gmi [deleted file]

index 85cf2664eda977b908de1ac8151c484e575c6d7b..33f107f8dac21b848d4c662a00d36d607432c5e9 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -20,6 +20,12 @@ test:
 coverage:
        cargo tarpaulin
 
+format:
+       cargo fmt && cargo clippy --fix
+
+lint:
+       cargo fmt -- --check && cargo clippy
+
 release: rpm tar deb
        @$(eval filename := $(app_name)-$(target)-$(channel))
 
@@ -67,11 +73,11 @@ else
        $(MAKE) -e profile=release -e architectures='$(mac_architectures)' -e channel=$(tag) package
 endif
 
-ci:
+ci: lint coverage
 ifeq ($(GIT_REF),refs/heads/main)
        $(MAKE) -e profile=release -e channel=unstable package
 else ifneq (,$(findstring refs/tags/,$(GIT_REF)))
        $(MAKE) -e profile=release -e channel=$(subst refs/tags/,,$(GIT_REF)) package
 endif
 
-.PHONY: default build $(architectures) rpm package prepare set_rust ci release test coverage
+.PHONY: default build $(architectures) rpm package prepare set_rust ci release test coverage format
index 43eee33ca0233fbc2a6d60eaacb4c1cc632d7115..754412ac1ee3499dd369110cb2991b2c81608e56 100644 (file)
@@ -3,65 +3,59 @@ use std::fs::read_dir;
 use std::path::PathBuf;
 
 pub fn find_files(directory_path: &PathBuf) -> Vec<File> {
-    return find_files_recursively(directory_path, directory_path);
+    find_files_recursively(directory_path, directory_path)
 }
 
 fn find_files_recursively(root_path: &PathBuf, directory_path: &PathBuf) -> Vec<File> {
     let mut result: Vec<File> = vec![];
     let file_handler = FileHandler::default();
-    let entries = read_dir(&directory_path).unwrap();
+    let entries = read_dir(directory_path).unwrap();
     for entry in entries {
         let path = entry.unwrap().path();
-        let relative_path = path.strip_prefix(&root_path).unwrap();
+        let relative_path = path.strip_prefix(root_path).unwrap();
         if relative_path.starts_with(".git") || relative_path.starts_with(".gitignore") {
             continue;
         }
         if path.is_dir() {
-            result.append(&mut find_files_recursively(&root_path, &path))
+            result.append(&mut find_files_recursively(root_path, &path))
         } else {
             let file_type = file_handler.identify(&path);
-            result.push(File {
-                path: path,
-                file_type: file_type,
-            });
+            result.push(File { path, file_type });
         }
     }
-    return result;
+    result
 }
 
-
 #[cfg(test)]
 mod tests {
     use std::collections::HashSet;
-    use std::path::PathBuf;
     use std::fs::create_dir_all;
+    use std::path::PathBuf;
 
     use super::*;
 
-    use crate::file_handler::FileType;
     use crate::file_handler::File;
+    use crate::file_handler::FileType;
     use test_utilities::*;
 
     fn get_paths(root_directory: &PathBuf, files: &Vec<File>) -> HashSet<String> {
         files
             .iter()
-            .map( |file|
+            .map(|file| {
                 file.path
                     .strip_prefix(root_directory)
                     .unwrap()
                     .to_string_lossy()
                     .to_string()
-            )
+            })
             .collect()
     }
 
     #[test]
     fn finds_all_files() {
         let test_dir = setup_test_dir();
-        create_dir_all(&test_dir.join("nested"))
-            .expect("Could not create nested test directory");
-        create_dir_all(&test_dir.join("assets"))
-            .expect("Could not create assets test directory");
+        create_dir_all(&test_dir.join("nested")).expect("Could not create nested test directory");
+        create_dir_all(&test_dir.join("assets")).expect("Could not create assets test directory");
         create_test_file(&test_dir.join("test1.gmi"), "");
         create_test_file(&test_dir.join("_layout.html"), "");
         create_test_file(&test_dir.join("nested/nested.gmi"), "");
@@ -81,10 +75,8 @@ mod tests {
     #[test]
     fn identifies_correct_file_types() {
         let test_dir = setup_test_dir();
-        create_dir_all(&test_dir.join("nested"))
-            .expect("Could not create nested test directory");
-        create_dir_all(&test_dir.join("assets"))
-            .expect("Could not create assets test directory");
+        create_dir_all(&test_dir.join("nested")).expect("Could not create nested test directory");
+        create_dir_all(&test_dir.join("assets")).expect("Could not create assets test directory");
         create_test_file(&test_dir.join("_layout.html"), "");
         create_test_file(&test_dir.join("nested/nested.gmi"), "");
         create_test_file(&test_dir.join("assets/style.css"), "");
@@ -101,7 +93,7 @@ mod tests {
                     } else {
                         assert_eq!(file.file_type, FileType::File)
                     }
-                },
+                }
                 _ => assert_eq!(file.file_type, FileType::File),
             }
         }
@@ -110,12 +102,9 @@ mod tests {
     #[test]
     fn ignores_git_directory() {
         let test_dir = setup_test_dir();
-        create_dir_all(&test_dir.join("nested"))
-            .expect("Could not create nested test directory");
-        create_dir_all(&test_dir.join("assets"))
-            .expect("Could not create assets test directory");
-        create_dir_all(&test_dir.join(".git"))
-            .expect("Could not create git test directory");
+        create_dir_all(&test_dir.join("nested")).expect("Could not create nested test directory");
+        create_dir_all(&test_dir.join("assets")).expect("Could not create assets test directory");
+        create_dir_all(&test_dir.join(".git")).expect("Could not create git test directory");
         create_test_file(&test_dir.join("_layout.html"), "");
         create_test_file(&test_dir.join("nested/nested.gmi"), "");
         create_test_file(&test_dir.join("assets/style.css"), "");
index 603d1894e76feed4dffdd10e4d8d2917250fb829..601be87ddc73fd45d1e131586fc285b255a4bef9 100644 (file)
@@ -1,13 +1,13 @@
 pub struct Strategy {}
 
-use std::path::PathBuf;
 use std::fs::{copy, create_dir_all};
+use std::path::Path;
 
-use crate::file_handler::{File, FileType, FileHandlerStrategy};
+use crate::file_handler::{File, FileHandlerStrategy, FileType};
 
 impl Strategy {
-    fn handle(&self, source: &PathBuf, destination: &PathBuf, file: &File) {
-        let relative_path = file.path.strip_prefix(&source).unwrap();
+    fn handle(&self, source: &Path, destination: &Path, file: &File) {
+        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();
@@ -16,7 +16,7 @@ impl Strategy {
 }
 
 impl FileHandlerStrategy for Strategy {
-    fn is(&self, path: &PathBuf) -> bool {
+    fn is(&self, path: &Path) -> bool {
         !path.is_dir()
     }
 
@@ -25,18 +25,15 @@ impl FileHandlerStrategy for Strategy {
     }
 
     fn can_handle(&self, file_type: &FileType) -> bool {
-        match file_type {
-            FileType::File => true,
-            _ => false,
-        }
+        matches!(file_type, FileType::File)
     }
 
-    fn handle_html(&self, source: &PathBuf, destination: &PathBuf, file: &File, _l: &str) {
-        return self.handle(source, destination, file);
+    fn handle_html(&self, source: &Path, destination: &Path, file: &File, _l: &str) {
+        self.handle(source, destination, file)
     }
 
-    fn handle_gemini(&self, source: &PathBuf, destination: &PathBuf, file: &File) {
-        return self.handle(source, destination, file);
+    fn handle_gemini(&self, source: &Path, destination: &Path, file: &File) {
+        self.handle(source, destination, file)
     }
 }
 
@@ -90,10 +87,8 @@ mod tests {
         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_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("image.png"), "A fish playing the banjo");
         let strategy = Strategy {};
 
@@ -118,13 +113,13 @@ mod tests {
         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_dir_all(&source_dir.join("nested"))
-            .expect("Could not create source test directory");
-        create_test_file(&source_dir.join("nested/style.css"), "* { margin: 0; padding: 0 }");
+        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_dir_all(&source_dir.join("nested")).expect("Could not create source test directory");
+        create_test_file(
+            &source_dir.join("nested/style.css"),
+            "* { margin: 0; padding: 0 }",
+        );
         let strategy = Strategy {};
 
         let file = File {
@@ -148,10 +143,8 @@ mod tests {
         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_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("image.png"), "A fish playing the banjo");
         let strategy = Strategy {};
 
@@ -176,10 +169,8 @@ mod tests {
         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_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("image.png"), "A fish playing the banjo");
         let strategy = Strategy {};
 
index d8bde1434c19be4fec7af4b95dbe0310cc41778b..e987baba989aeecc7477af731480080b4a723960 100644 (file)
@@ -1,10 +1,10 @@
 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;
 
@@ -27,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
     }
@@ -39,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_html(&self, source: &PathBuf, destination: &PathBuf, file: &File, layout: &str) {
+    fn handle_html(&self, source: &Path, destination: &Path, file: &File, layout: &str) {
         let gemini_contents = read_to_string(&file.path).unwrap();
 
         // Front matter extraction
@@ -55,14 +52,14 @@ impl FileHandlerStrategy for Strategy {
         let mut description = "";
         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 = lines_found + 1;
+                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 = lines_found + 1;
+                if self.is_description(line) {
+                    description = self.get_description(line).trim();
+                    lines_found += 1;
                     continue;
                 }
             }
@@ -76,18 +73,19 @@ impl FileHandlerStrategy for Strategy {
             .replace("{{ description }}", description)
             .replace("{{ content }}", &content_html[..]);
 
-
-        let relative_path = file.path.strip_prefix(&source).unwrap();
+        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();
+        destination_file
+            .write_all(generated_html.as_bytes())
+            .unwrap();
     }
 
-    fn handle_gemini(&self, source: &PathBuf, destination: &PathBuf, file: &File) {
+    fn handle_gemini(&self, source: &Path, destination: &Path, file: &File) {
         let gemini_contents = read_to_string(&file.path).unwrap();
 
         // Front matter extraction
@@ -95,12 +93,12 @@ impl FileHandlerStrategy for Strategy {
         let mut lines_found = 0;
         if let Some(slice) = lines.get(..2) {
             for line in slice.iter() {
-                if self.is_title(&line) {
-                    lines_found = lines_found + 1;
+                if self.is_title(line) {
+                    lines_found += 1;
                     continue;
                 }
-                if self.is_description(&line) {
-                    lines_found = lines_found + 1;
+                if self.is_description(line) {
+                    lines_found += 1;
                     continue;
                 }
             }
@@ -108,187 +106,247 @@ impl FileHandlerStrategy for Strategy {
 
         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(gemini_source.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 std::fs;
 
-    fn setup() -> Strategy {
-        Strategy {}
-    }
+    use test_utilities::*;
 
-    fn fixtures_dir() -> PathBuf {
-        PathBuf::from("tests/fixtures")
+    #[test]
+    fn detects_title() {
+        let strategy = Strategy {};
+        assert!(strategy.is_title("--- title: Hello!"));
     }
 
-    fn fixture_path(filename: &str) -> PathBuf {
-        fixtures_dir().join(filename)
+    #[test]
+    fn does_not_detect_other_keys_as_title() {
+        let strategy = Strategy {};
+        assert!(!strategy.is_title("--- description: Hello!"));
     }
 
-    fn read_fixture(filename: &str) -> String {
-        fs::read_to_string(fixture_path(filename))
-            .unwrap_or_else(|_| panic!("Failed to read fixture file: {}", filename))
+    #[test]
+    fn detects_description() {
+        let strategy = Strategy {};
+        assert!(strategy.is_description("--- description: What is this?"));
     }
 
-    mod front_matter_tests {
-        use super::*;
-
-        #[test]
-        fn detects_title() {
-            let strategy = setup();
-            let content = read_fixture("test1.gmi");
-            let first_line = content.lines().next().unwrap();
-            assert!(strategy.is_title(first_line));
-        }
-
-        #[test]
-        fn detects_description() {
-            let strategy = setup();
-            let content = read_fixture("test1.gmi");
-            let second_line = content.lines().nth(1).unwrap();
-            assert!(strategy.is_description(second_line));
-        }
-
-        #[test]
-        fn extracts_title() {
-            let strategy = setup();
-            let content = read_fixture("test1.gmi");
-            let first_line = content.lines().next().unwrap();
-            assert_eq!(strategy.get_title(first_line).trim(), "Test Title");
-        }
-
-        #[test]
-        fn extracts_description() {
-            let strategy = setup();
-            let content = read_fixture("test1.gmi");
-            let second_line = content.lines().nth(1).unwrap();
-            assert_eq!(strategy.get_description(second_line).trim(), "Test Description");
-        }
+    #[test]
+    fn does_not_detect_other_keys_as_description() {
+        let strategy = Strategy {};
+        assert!(!strategy.is_description("--- title: What is this?"));
     }
 
-    mod file_type_tests {
-        use super::*;
-
-        #[test]
-        fn identifies_gemini_files() {
-            let strategy = setup();
-            assert!(strategy.is(&fixture_path("test1.gmi")));
-            assert!(!strategy.is(&fixture_path("_layout.html")));
-            assert!(!strategy.is(&fixtures_dir()));
-        }
-
-        #[test]
-        fn identifies_file_type() {
-            let strategy = setup();
-            assert!(matches!(strategy.identify(), FileType::Gemini));
-        }
+    #[test]
+    fn extracts_title() {
+        let strategy = Strategy {};
+        assert_eq!(strategy.get_title("--- title: Hello!").trim(), "Hello!");
+    }
 
-        #[test]
-        fn handles_correct_file_type() {
-            let strategy = setup();
-            assert!(strategy.can_handle(&FileType::Gemini));
-            assert!(!strategy.can_handle(&FileType::Layout));
-            assert!(!strategy.can_handle(&FileType::File));
-            assert!(!strategy.can_handle(&FileType::Unknown));
-        }
+    #[test]
+    fn extracts_description() {
+        let strategy = Strategy {};
+        assert_eq!(
+            strategy
+                .get_description("--- description: What is this?")
+                .trim(),
+            "What is this?"
+        );
     }
 
-    mod file_handling_tests {
-        use super::*;
-
-        #[test]
-        fn handles_html_generation() {
-            let strategy = setup();
-            let source = fixtures_dir();
-            let output = fixture_path("output");
-            let layout = read_fixture("_layout.html");
-
-            let file = File {
-                path: fixture_path("test1.gmi"),
-                file_type: FileType::Gemini,
-            };
-
-            strategy.handle_html(
-                &source,
-                &output,
-                &file,
-                &layout,
-            );
-
-            let generated_path = output.join("test1.html");
-            assert!(generated_path.exists());
-
-            let content = fs::read_to_string(generated_path.clone()).unwrap();
-            assert!(content.contains("Test Title"));
-            assert!(content.contains("<h1>"));
-
-            // Cleanup
-            let _ = fs::remove_file(generated_path);
-        }
+    #[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 handles_gemini_generation() {
-            let strategy = setup();
-            let source = fixtures_dir();
-            let output = fixture_path("output");
+    #[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));
+    }
 
-            let file = File {
-                path: fixture_path("test1.gmi"),
-                file_type: FileType::Gemini,
-            };
+    #[test]
+    fn identifies_gemini_type() {
+        let strategy = Strategy {};
+        assert!(matches!(strategy.identify(), FileType::Gemini));
+    }
 
-            strategy.handle_gemini(
-                &source,
-                &output,
-                &file,
-            );
+    #[test]
+    fn handles_gemini_type() {
+        let strategy = Strategy {};
+        assert!(strategy.can_handle(&FileType::Gemini));
+    }
 
-            let generated_path = output.join("test1.gmi");
-            assert!(generated_path.exists());
+    #[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));
+    }
 
-            let content = fs::read_to_string(&generated_path).unwrap();
-            assert!(content.contains("# Heading"));
-            assert!(!content.contains("Test Title")); // Front matter should be removed
+    #[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>
+",
+        );
+    }
 
-            // Cleanup
-            let _ = fs::remove_file(generated_path);
-        }
+    #[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 strategy = setup();
-            let source = fixtures_dir();
-            let output = fixture_path("output");
-            let layout = read_fixture("_layout.html");
-
-            let file = File {
-                path: fixture_path("nested/nested.gmi"),
-                file_type: FileType::Gemini,
-            };
-
-            strategy.handle_html(
-                &source,
-                &output,
-                &file,
-                &layout,
-            );
-
-            let generated_path = output.join("nested").join("nested.html");
-            assert!(generated_path.exists());
-
-            // Cleanup
-            let _ = fs::remove_file(generated_path);
-            let _ = fs::remove_dir(output.join("nested"));
-        }
+    #[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>
+",
+        );
     }
 }
index 41acde15cc9023e960302946e47d469158ed66db..8d9689c4b67a087e34096cc1655d4831e1a74e9d 100644 (file)
@@ -1,12 +1,12 @@
 pub struct Strategy {}
 
-use std::path::PathBuf;
+use std::path::Path;
 
-use crate::file_handler::{File, FileType, FileHandlerStrategy};
+use crate::file_handler::{File, FileHandlerStrategy, FileType};
 
 impl FileHandlerStrategy for Strategy {
-    fn is(&self, path: &PathBuf) -> bool {
-        return !path.is_dir() && path.ends_with("_layout.html");
+    fn is(&self, path: &Path) -> bool {
+        !path.is_dir() && path.ends_with("_layout.html")
     }
 
     fn identify(&self) -> FileType {
@@ -14,21 +14,19 @@ impl FileHandlerStrategy for Strategy {
     }
 
     fn can_handle(&self, file_type: &FileType) -> bool {
-        match file_type {
-            FileType::Layout => true,
-            _ => false,
-        }
+        matches!(file_type, FileType::Layout)
     }
 
     // We don't implement handling for layout, as we assume there's only one
     // and it got handled before.
-    fn handle_html(&self, _s: &PathBuf, _d: &PathBuf,  _f: &File, _l: &str) {}
-    fn handle_gemini(&self, _s: &PathBuf, _d: &PathBuf, _f: &File) {}
+    fn handle_html(&self, _s: &Path, _d: &Path, _f: &File, _l: &str) {}
+    fn handle_gemini(&self, _s: &Path, _d: &Path, _f: &File) {}
 }
 
 #[cfg(test)]
 mod tests {
     use std::fs::create_dir_all;
+    use std::path::PathBuf;
 
     use super::*;
 
@@ -70,8 +68,7 @@ mod tests {
     fn rejects_directory_named_layout() {
         let test_dir = setup_test_dir();
         let layout_dir = test_dir.join("_layout");
-        create_dir_all(&layout_dir)
-            .expect("Could not create _layout test directory");
+        create_dir_all(&layout_dir).expect("Could not create _layout test directory");
         let strategy = Strategy {};
         assert!(!strategy.is(&layout_dir));
     }
@@ -108,7 +105,7 @@ mod tests {
             &PathBuf::from("source"),
             &PathBuf::from("dest"),
             &file,
-            "layout content"
+            "layout content",
         );
     }
 
@@ -121,10 +118,6 @@ mod tests {
         };
 
         // Should not panic
-        strategy.handle_gemini(
-            &PathBuf::from("source"),
-            &PathBuf::from("dest"),
-            &file
-        );
+        strategy.handle_gemini(&PathBuf::from("source"), &PathBuf::from("dest"), &file);
     }
 }
index cefe6a20b374072dd735e4be19922fb2f7cfc8ba..a079c74f352f68b5c2028b1e16697182b9586812 100644 (file)
@@ -4,31 +4,31 @@ use file_strategies::file::Strategy as FileStrategy;
 use file_strategies::gemini::Strategy as GeminiStrategy;
 use file_strategies::layout::Strategy as LayoutStrategy;
 
-use std::path::PathBuf;
 use std::fs::read_to_string;
+use std::path::{Path, PathBuf};
 
 pub struct FileHandler {
-   pub strategies: Vec<Box<dyn FileHandlerStrategy>>,
-   pub layout: Option<String>
+    pub strategies: Vec<Box<dyn FileHandlerStrategy>>,
+    pub layout: Option<String>,
 }
 
 impl Default for FileHandler {
     fn default() -> FileHandler {
         FileHandler {
             strategies: vec![
-                Box::new(GeminiStrategy{}),
-                Box::new(LayoutStrategy{}),
-                Box::new(FileStrategy{}),
+                Box::new(GeminiStrategy {}),
+                Box::new(LayoutStrategy {}),
+                Box::new(FileStrategy {}),
             ],
-            layout: None
+            layout: None,
         }
     }
 }
 
 impl FileHandler {
-    pub fn identify(&self, path: &PathBuf) -> FileType {
+    pub fn identify(&self, path: &Path) -> FileType {
         for strategy in self.strategies.iter() {
-            if strategy.is(&path) {
+            if strategy.is(path) {
                 return strategy.identify();
             }
         }
@@ -37,44 +37,55 @@ impl FileHandler {
 
     pub fn get_layout_or_panic(&mut self, files: &[File]) -> Result<(), &str> {
         for file in files {
-            match file.file_type {
-                FileType::Layout => {
-                    let layout_text = read_to_string(&file.path).unwrap();
-                    self.layout = Some(layout_text);
-                    return Ok(());
-                },
-                _ => {}
+            if file.file_type == FileType::Layout {
+                let layout_text = read_to_string(&file.path).unwrap();
+                self.layout = Some(layout_text);
+                return Ok(());
             }
         }
         Err("No layout found. Please ensure there's a _layout.html file at the root")
     }
 
-    pub fn handle_all(&self, source: &PathBuf, html_destination: &PathBuf, gemini_destination: &PathBuf, files: &[File]) {
+    pub fn handle_all(
+        &self,
+        source: &Path,
+        html_destination: &Path,
+        gemini_destination: &Path,
+        files: &[File],
+    ) {
         files.iter().for_each(|file| {
             self.handle(source, html_destination, gemini_destination, file);
         });
     }
 
-    pub fn handle(&self, source: &PathBuf, html_destination: &PathBuf, gemini_destination: &PathBuf, file: &File) {
-        if let Some(strategy) = self.strategies
+    pub fn handle(
+        &self,
+        source: &Path,
+        html_destination: &Path,
+        gemini_destination: &Path,
+        file: &File,
+    ) {
+        if let Some(strategy) = self
+            .strategies
             .iter()
-            .find(|s| s.can_handle(&file.file_type)) 
+            .find(|s| s.can_handle(&file.file_type))
         {
-                let layout = self.layout.as_ref()
-                    .expect("Layout should be initialized before handling files");
-                strategy.handle_html(source, html_destination, file, layout);
-                strategy.handle_gemini(source, gemini_destination, file);
-                return;
+            let layout = self
+                .layout
+                .as_ref()
+                .expect("Layout should be initialized before handling files");
+            strategy.handle_html(source, html_destination, file, layout);
+            strategy.handle_gemini(source, gemini_destination, file);
         }
     }
 }
 
 pub trait FileHandlerStrategy {
-    fn is(&self, path: &PathBuf) -> bool;
+    fn is(&self, path: &Path) -> bool;
     fn identify(&self) -> FileType;
     fn can_handle(&self, file_type: &FileType) -> bool;
-    fn handle_html(&self, source: &PathBuf, destination: &PathBuf, file: &File, layout: &str);
-    fn handle_gemini(&self, source: &PathBuf, destination: &PathBuf, file: &File);
+    fn handle_html(&self, source: &Path, destination: &Path, file: &File, layout: &str);
+    fn handle_gemini(&self, source: &Path, destination: &Path, file: &File);
 }
 
 #[derive(Debug, Clone, PartialEq)]
@@ -91,9 +102,9 @@ pub struct File {
     pub file_type: FileType,
 }
 
-
 #[cfg(test)]
 mod tests {
+    use std::fs::create_dir_all;
     use std::path::PathBuf;
 
     use super::*;
@@ -144,7 +155,10 @@ mod tests {
         let mut handler = FileHandler::default();
         let files = vec![
             create_test_internal_file("test.gmi", FileType::Gemini),
-            create_test_internal_file(&layout_path.to_str().expect("Could not encode layout"), FileType::Layout),
+            create_test_internal_file(
+                &layout_path.to_str().expect("Could not encode layout"),
+                FileType::Layout,
+            ),
             create_test_internal_file("regular.html", FileType::File),
         ];
 
@@ -169,7 +183,7 @@ mod tests {
     }
 
     impl FileHandlerStrategy for MockStrategy {
-        fn is(&self, _path: &PathBuf) -> bool {
+        fn is(&self, _path: &Path) -> bool {
             self.is_match
         }
 
@@ -181,8 +195,8 @@ mod tests {
             &self.file_type == file_type
         }
 
-        fn handle_html(&self, _source: &PathBuf, _destination: &PathBuf, _file: &File, _layout: &str) {}
-        fn handle_gemini(&self, _source: &PathBuf, _destination: &PathBuf, _file: &File) {}
+        fn handle_html(&self, _source: &Path, _destination: &Path, _file: &File, _layout: &str) {}
+        fn handle_gemini(&self, _source: &Path, _destination: &Path, _file: &File) {}
     }
 
     #[test]
@@ -209,9 +223,9 @@ mod tests {
         // Should not panic with empty vector
         handler.handle_all(
             &PathBuf::from("source"),
-            &PathBuf::from("tests/fixtures/output"),
-            &PathBuf::from("tests/fixtures/output"),
-            &files
+            &PathBuf::from("output_html"),
+            &PathBuf::from("output_gemini"),
+            &files,
         );
     }
 
@@ -220,14 +234,26 @@ mod tests {
         let mut handler = FileHandler::default();
         handler.layout = Some("test layout".to_string());
 
-        let file = create_test_internal_file("tests/fixtures/test1.gmi", FileType::Gemini);
+        let test_dir = setup_test_dir();
+        create_dir_all(&test_dir.join("output_html"))
+            .expect("Could not create output html test directory");
+        create_dir_all(&test_dir.join("output_gemini"))
+            .expect("Could not create output gemini test directory");
+        let test_path = test_dir.join("test.gmi");
+        create_test_file(&test_path, "");
+        let file = create_test_internal_file(
+            &test_path
+                .to_str()
+                .expect("Could not encode gemini test file"),
+            FileType::Gemini,
+        );
 
         // Should not panic with valid layout
         handler.handle(
-            &PathBuf::from(""),
-            &PathBuf::from("tests/fixtures/output"),
-            &PathBuf::from("tests/fixtures/output"),
-            &file
+            &test_dir,
+            &test_dir.join("output_html"),
+            &test_dir.join("output_gemini"),
+            &file,
         );
     }
 
@@ -239,9 +265,9 @@ mod tests {
 
         handler.handle(
             &PathBuf::from("source"),
-            &PathBuf::from("tests/fixtures/output"),
-            &PathBuf::from("tests/fixtures/output"),
-            &file
+            &PathBuf::from("output_html"),
+            &PathBuf::from("output_gemini"),
+            &file,
         );
     }
 
@@ -250,23 +276,51 @@ mod tests {
         let test_dir = setup_test_dir();
         let layout_path = test_dir.join("_layout.html");
         create_test_file(&layout_path, "");
+        create_test_file(&test_dir.join("test1.gmi"), "");
+        create_test_file(&test_dir.join("test2.gmi"), "");
+        create_test_file(&test_dir.join("test3.gmi"), "");
+        create_dir_all(&test_dir.join("output_html"))
+            .expect("Could not create output html test directory");
+        create_dir_all(&test_dir.join("output_gemini"))
+            .expect("Could not create output gemini test directory");
 
         let mut handler = FileHandler::default();
         let files = vec![
-            create_test_internal_file("tests/fixtures/test1.gmi", FileType::Gemini),
-            create_test_internal_file(&layout_path.to_str().expect("Could not encode layout"), FileType::Layout),
-            create_test_internal_file("tests/fixtures/test2.gmi", FileType::Gemini),
-            create_test_internal_file("tests/fixtures/test3.gmi", FileType::Gemini),
+            create_test_internal_file(
+                &test_dir
+                    .join("test1.gmi")
+                    .to_str()
+                    .expect("Could not encode test1"),
+                FileType::Gemini,
+            ),
+            create_test_internal_file(
+                &layout_path.to_str().expect("Could not encode layout"),
+                FileType::Layout,
+            ),
+            create_test_internal_file(
+                &test_dir
+                    .join("test2.gmi")
+                    .to_str()
+                    .expect("Could not encode test2"),
+                FileType::Gemini,
+            ),
+            create_test_internal_file(
+                &test_dir
+                    .join("test3.gmi")
+                    .to_str()
+                    .expect("Could not encode test3"),
+                FileType::Gemini,
+            ),
         ];
 
         let _ = handler.get_layout_or_panic(&files[1..]);
 
         // Test with slice
         handler.handle_all(
-            &PathBuf::from(""),
-            &PathBuf::from("tests/fixtures/output"),
-            &PathBuf::from("tests/fixtures/output"),
-            &files[1..] // Test with slice of last three elements
+            &test_dir,
+            &test_dir.join("output_html"),
+            &test_dir.join("output_gemini"),
+            &files[1..], // Test with slice of last three elements
         );
     }
 }
index 418b758b2206bfc294373e0f4e18e9e8d5d25662..e6de4eceeeccc6425b133bb8b5ce9beec573c414 100644 (file)
@@ -5,36 +5,34 @@ pub enum GeminiLine {
     Heading(u8, String),
     Link(String, String),
     Quote(String),
-    ListItem(String)
+    ListItem(String),
 }
 
 /// Parses gemtext source code into a vector of GeminiLine elements.
-/// 
+///
 /// # Arguments
 /// * `source` - A string slice that contains the gemtext
-/// 
+///
 /// # Returns
 /// A `Vec<GeminiLine>` containing the rendered HTML.
 pub fn parse(source: &str) -> Vec<GeminiLine> {
-    source.lines()
-        .fold(
-            (Vec::new(), false),
-            |(mut lines, is_preformatted), line| {
-                let parsed = if is_preformatted {
-                    parse_preformatted_line(line)
-                } else {
-                    parse_line(line)
-                };
-
-                let new_is_preformatted = match parsed {
-                    GeminiLine::PreformattedToggle(x, _) => x,
-                    _ => is_preformatted
-                };
-
-                lines.push(parsed);
-                (lines, new_is_preformatted)
-            }
-        )
+    source
+        .lines()
+        .fold((Vec::new(), false), |(mut lines, is_preformatted), line| {
+            let parsed = if is_preformatted {
+                parse_preformatted_line(line)
+            } else {
+                parse_line(line)
+            };
+
+            let new_is_preformatted = match parsed {
+                GeminiLine::PreformattedToggle(x, _) => x,
+                _ => is_preformatted,
+            };
+
+            lines.push(parsed);
+            (lines, new_is_preformatted)
+        })
         .0
 }
 
@@ -53,10 +51,12 @@ fn parse_line(line: &str) -> GeminiLine {
         s if s.starts_with("=>") => {
             let content = s[2..].trim();
             match content.split_once(char::is_whitespace) {
-                Some((url, text)) => GeminiLine::Link(url.trim().to_string(), text.trim().to_string()),
+                Some((url, text)) => {
+                    GeminiLine::Link(url.trim().to_string(), text.trim().to_string())
+                }
                 None => GeminiLine::Link(content.trim().to_string(), String::new()),
             }
-        },
+        }
         s if s.starts_with("* ") => GeminiLine::ListItem(s[2..].to_string()),
         s if s.starts_with(">") => GeminiLine::Quote(s[1..].to_string()),
         s if s.starts_with("```") => GeminiLine::PreformattedToggle(true, s[3..].to_string()),
@@ -70,16 +70,37 @@ mod tests {
 
     #[test]
     fn test_headings() {
-        assert_eq!(parse_line("### Heading"), GeminiLine::Heading(3, " Heading".to_string()));
-        assert_eq!(parse_line("## Heading"), GeminiLine::Heading(2, " Heading".to_string()));
-        assert_eq!(parse_line("# Heading"), GeminiLine::Heading(1, " Heading".to_string()));
+        assert_eq!(
+            parse_line("### Heading"),
+            GeminiLine::Heading(3, " Heading".to_string())
+        );
+        assert_eq!(
+            parse_line("## Heading"),
+            GeminiLine::Heading(2, " Heading".to_string())
+        );
+        assert_eq!(
+            parse_line("# Heading"),
+            GeminiLine::Heading(1, " Heading".to_string())
+        );
         assert_eq!(parse_line("###"), GeminiLine::Heading(3, "".to_string()));
-        assert_eq!(parse_line("#####"), GeminiLine::Heading(3, "##".to_string()));
+        assert_eq!(
+            parse_line("#####"),
+            GeminiLine::Heading(3, "##".to_string())
+        );
         assert_eq!(parse_line("# "), GeminiLine::Heading(1, " ".to_string()));
 
-        assert_eq!(parse_preformatted_line("### Heading"), GeminiLine::Text("### Heading".to_string(), true));
-        assert_eq!(parse_preformatted_line("## Heading"), GeminiLine::Text("## Heading".to_string(), true));
-        assert_eq!(parse_preformatted_line("# Heading"), GeminiLine::Text("# Heading".to_string(), true));
+        assert_eq!(
+            parse_preformatted_line("### Heading"),
+            GeminiLine::Text("### Heading".to_string(), true)
+        );
+        assert_eq!(
+            parse_preformatted_line("## Heading"),
+            GeminiLine::Text("## Heading".to_string(), true)
+        );
+        assert_eq!(
+            parse_preformatted_line("# Heading"),
+            GeminiLine::Text("# Heading".to_string(), true)
+        );
     }
 
     #[test]
@@ -121,8 +142,14 @@ mod tests {
 
         assert_eq!(parse_line("* "), GeminiLine::ListItem("".to_string()));
         assert_eq!(parse_line("*"), GeminiLine::Text("*".to_string(), false));
-        assert_eq!(parse_line("*WithText"), GeminiLine::Text("*WithText".to_string(), false));
-        assert_eq!(parse_line("*  Multiple spaces"), GeminiLine::ListItem(" Multiple spaces".to_string()));
+        assert_eq!(
+            parse_line("*WithText"),
+            GeminiLine::Text("*WithText".to_string(), false)
+        );
+        assert_eq!(
+            parse_line("*  Multiple spaces"),
+            GeminiLine::ListItem(" Multiple spaces".to_string())
+        );
     }
 
     #[test]
@@ -134,7 +161,10 @@ mod tests {
 
         assert_eq!(parse_line(">"), GeminiLine::Quote("".to_string()));
         assert_eq!(parse_line("> "), GeminiLine::Quote(" ".to_string()));
-        assert_eq!(parse_line(">>Nested"), GeminiLine::Quote(">Nested".to_string()));
+        assert_eq!(
+            parse_line(">>Nested"),
+            GeminiLine::Quote(">Nested".to_string())
+        );
     }
 
     #[test]
@@ -144,9 +174,18 @@ mod tests {
             GeminiLine::PreformattedToggle(true, "alt-text".to_string())
         );
 
-        assert_eq!(parse_line("```"), GeminiLine::PreformattedToggle(true, "".to_string()));
-        assert_eq!(parse_line("``` "), GeminiLine::PreformattedToggle(true, " ".to_string()));
-        assert_eq!(parse_line("````"), GeminiLine::PreformattedToggle(true, "`".to_string()));
+        assert_eq!(
+            parse_line("```"),
+            GeminiLine::PreformattedToggle(true, "".to_string())
+        );
+        assert_eq!(
+            parse_line("``` "),
+            GeminiLine::PreformattedToggle(true, " ".to_string())
+        );
+        assert_eq!(
+            parse_line("````"),
+            GeminiLine::PreformattedToggle(true, "`".to_string())
+        );
 
         assert_eq!(
             parse_preformatted_line("```alt-text"),
@@ -156,7 +195,6 @@ mod tests {
             parse_preformatted_line("```"),
             GeminiLine::PreformattedToggle(false, "".to_string())
         );
-
     }
 
     #[test]
@@ -175,9 +213,18 @@ mod tests {
 
     #[test]
     fn test_malformed_input() {
-        assert_eq!(parse_line("= >Not a link"), GeminiLine::Text("= >Not a link".to_string(), false));
-        assert_eq!(parse_line("``Not preformatted"), GeminiLine::Text("``Not preformatted".to_string(), false));
-        assert_eq!(parse_line("** Not a list"), GeminiLine::Text("** Not a list".to_string(), false));
+        assert_eq!(
+            parse_line("= >Not a link"),
+            GeminiLine::Text("= >Not a link".to_string(), false)
+        );
+        assert_eq!(
+            parse_line("``Not preformatted"),
+            GeminiLine::Text("``Not preformatted".to_string(), false)
+        );
+        assert_eq!(
+            parse_line("** Not a list"),
+            GeminiLine::Text("** Not a list".to_string(), false)
+        );
     }
 
     #[test]
@@ -200,23 +247,26 @@ code
 >Quote
 ```trailing alt";
         let result = parse(input);
-        assert_eq!(result, vec![
-            GeminiLine::Heading(1, " Heading 1".to_string()),
-            GeminiLine::Heading(2, " Heading 2".to_string()),
-            GeminiLine::Heading(3, " Heading 3".to_string()),
-            GeminiLine::Text("Regular text".to_string(), false),
-            GeminiLine::Link("https://example.com".to_string(), "Link text".to_string()),
-            GeminiLine::ListItem("List item".to_string()),
-            GeminiLine::Quote("Quote".to_string()),
-            GeminiLine::PreformattedToggle(true, "alt".to_string()),
-            GeminiLine::Text("code".to_string(), true),
-            GeminiLine::Text("# Heading 1".to_string(), true),
-            GeminiLine::Text("## Heading 2".to_string(), true),
-            GeminiLine::Text("### Heading 3".to_string(), true),
-            GeminiLine::Text("=> https://example.com Link text".to_string(), true),
-            GeminiLine::Text("* List item".to_string(), true),
-            GeminiLine::Text(">Quote".to_string(), true),
-            GeminiLine::PreformattedToggle(false, "".to_string()),
-        ]);
+        assert_eq!(
+            result,
+            vec![
+                GeminiLine::Heading(1, " Heading 1".to_string()),
+                GeminiLine::Heading(2, " Heading 2".to_string()),
+                GeminiLine::Heading(3, " Heading 3".to_string()),
+                GeminiLine::Text("Regular text".to_string(), false),
+                GeminiLine::Link("https://example.com".to_string(), "Link text".to_string()),
+                GeminiLine::ListItem("List item".to_string()),
+                GeminiLine::Quote("Quote".to_string()),
+                GeminiLine::PreformattedToggle(true, "alt".to_string()),
+                GeminiLine::Text("code".to_string(), true),
+                GeminiLine::Text("# Heading 1".to_string(), true),
+                GeminiLine::Text("## Heading 2".to_string(), true),
+                GeminiLine::Text("### Heading 3".to_string(), true),
+                GeminiLine::Text("=> https://example.com Link text".to_string(), true),
+                GeminiLine::Text("* List item".to_string(), true),
+                GeminiLine::Text(">Quote".to_string(), true),
+                GeminiLine::PreformattedToggle(false, "".to_string()),
+            ]
+        );
     }
 }
index 47d36bb68a699743aadf4b466bb91d5dec87f9bb..1b26bff79d856a36015c468d757592195639a037 100644 (file)
@@ -1,10 +1,10 @@
 use crate::gemini_parser::GeminiLine;
 
 /// Renders HTML from a vector of GeminiLine elements.
-/// 
+///
 /// # Arguments
 /// * `lines` - Vector of GeminiLine elements to render
-/// 
+///
 /// # Returns
 /// A String containing the rendered HTML.
 pub fn render_html(lines: Vec<GeminiLine>) -> String {
@@ -26,23 +26,19 @@ pub fn render_html(lines: Vec<GeminiLine>) -> String {
 fn line_preamble(
     line: &GeminiLine,
     last_line: Option<&GeminiLine>,
-    heading_stack: &mut Vec<u8>
+    heading_stack: &mut Vec<u8>,
 ) -> String {
     let mut html = String::new();
 
     if let Some(last_line) = last_line {
         match last_line {
-            GeminiLine::ListItem(_) => {
-                match line {
-                    GeminiLine::ListItem(_) => {},
-                    _ => html.push_str("</ul>\n"),
-                }
+            GeminiLine::ListItem(_) => match line {
+                GeminiLine::ListItem(_) => {}
+                _ => html.push_str("</ul>\n"),
             },
-            GeminiLine::Quote(_) => {
-                match line {
-                    GeminiLine::Quote(_) => {},
-                    _ => html.push_str("</blockquote>\n"),
-                }
+            GeminiLine::Quote(_) => match line {
+                GeminiLine::Quote(_) => {}
+                _ => html.push_str("</blockquote>\n"),
             },
             _ => {}
         }
@@ -66,18 +62,14 @@ fn line_preamble(
             }
             heading_stack.push(*level);
             html.push_str(&format!("<section class=\"h{}\">\n", level));
+        }
+        GeminiLine::ListItem(_) => match last_line {
+            Some(GeminiLine::ListItem(_)) => {}
+            _ => html.push_str("<ul>\n"),
         },
-        GeminiLine::ListItem(_) => {
-            match last_line {
-                Some(GeminiLine::ListItem(_)) => {},
-                _ => html.push_str("<ul>\n"),
-            }
-        },
-        GeminiLine::Quote(_) => {
-            match last_line {
-                Some(GeminiLine::Quote(_)) => {},
-                _ => html.push_str("<blockquote>\n"),
-            }
+        GeminiLine::Quote(_) => match last_line {
+            Some(GeminiLine::Quote(_)) => {}
+            _ => html.push_str("<blockquote>\n"),
         },
         _ => {}
     }
@@ -91,12 +83,14 @@ fn line_content(line: &GeminiLine) -> String {
         GeminiLine::Link(url, text) => {
             let display = if text.is_empty() { url } else { text };
             format!("<p class=\"a\"><a href=\"{}\">{}</a></p>", url, display)
-        },
+        }
         GeminiLine::Heading(level, content) => format!("<h{}>{}</h{}>", level, content, level),
         GeminiLine::ListItem(content) => format!("<li>{}</li>", content),
-        GeminiLine::PreformattedToggle(true, alt_text) => { format!("<pre aria-label=\"{}\">", alt_text) }
-        GeminiLine::PreformattedToggle(false, _) => { "</pre>".to_string() }
-        GeminiLine::Text(content, true) | GeminiLine::Quote(content) => format!("{}", content)
+        GeminiLine::PreformattedToggle(true, alt_text) => {
+            format!("<pre aria-label=\"{}\">", alt_text)
+        }
+        GeminiLine::PreformattedToggle(false, _) => "</pre>".to_string(),
+        GeminiLine::Text(content, true) | GeminiLine::Quote(content) => content.to_string(),
     }
 }
 
@@ -122,13 +116,8 @@ mod tests {
 
     #[test]
     fn test_simple_text() {
-        let input = vec![
-            GeminiLine::Text("Hello world".to_string(), false),
-        ];
-        assert_eq!(
-            render_html(input),
-            "<p>Hello world</p>\n"
-        );
+        let input = vec![GeminiLine::Text("Hello world".to_string(), false)];
+        assert_eq!(render_html(input), "<p>Hello world</p>\n");
     }
 
     #[test]
index 902f3c3a757747e08581999b0e57d1c85f5cd732..cedd55761a9ced615637ca98326c693e10be9491 100644 (file)
@@ -1,12 +1,12 @@
-mod gemini_parser;
-mod html_renderer;
 mod file_finder;
 mod file_handler;
+mod gemini_parser;
+mod html_renderer;
 
-use std::io::Result;
-use std::process::exit;
 use std::env::current_dir;
 use std::fs::{create_dir_all, remove_dir_all};
+use std::io::Result;
+use std::process::exit;
 
 use crate::file_finder::find_files;
 use crate::file_handler::FileHandler;
@@ -26,7 +26,7 @@ fn main() -> Result<()> {
     // Step 2. Load the layout
     let mut file_handler = FileHandler::default();
     match file_handler.get_layout_or_panic(&files) {
-        Ok(_) => {},
+        Ok(_) => {}
         Err(error) => {
             eprintln!("{}", error);
             exit(1);
@@ -34,17 +34,11 @@ fn main() -> Result<()> {
     }
 
     // Step 3. Prepare the target priority
-    match remove_dir_all(&html_destination) {
-        _ => {}
-    };
-    match remove_dir_all(&gemini_destination) {
-        _ => {}
-    };
-
-    create_dir_all(&html_destination)
-        .expect("Could not create HTML directory.");
-    create_dir_all(&gemini_destination)
-        .expect("Could not create Gemini directory.");
+    let _ = remove_dir_all(&html_destination);
+    let _ = remove_dir_all(&gemini_destination);
+
+    create_dir_all(&html_destination).expect("Could not create HTML directory.");
+    create_dir_all(&gemini_destination).expect("Could not create Gemini directory.");
 
     // Step 4. Process all files
     file_handler.handle_all(&source, &html_destination, &gemini_destination, &files);
index 0bb46bb11ed6267aae3f1435f8a5d7fde9bd88f7..fcbdb99a8cb484d239279c6999d86e8dd1df8fb7 100644 (file)
@@ -24,22 +24,28 @@ fn test_basic_generation() {
     let html_dir = parent.join(format!("{}_html", dir_name));
     let gemini_dir = parent.join(format!("{}_gemini", dir_name));
 
-    let _cleanup = TestDir { 
-        paths: vec![test_dir.clone(), html_dir.clone(), gemini_dir.clone()] 
+    let _cleanup = TestDir {
+        paths: vec![test_dir.clone(), html_dir.clone(), gemini_dir.clone()],
     };
 
     // Create test input files
-    create_test_file(&test_dir.join("_layout.html"), "\
+    create_test_file(
+        &test_dir.join("_layout.html"),
+        "\
 <html>
 <head><title>{{ title }}</title></head>
 <body>{{ content }}</body>
 </html>
-");
-    create_test_file(&test_dir.join("test.gmi"), "\
+",
+    );
+    create_test_file(
+        &test_dir.join("test.gmi"),
+        "\
 --- title: Page Is Cool!
 # Test
 Hello world
-");
+",
+    );
     create_test_file(&test_dir.join("test.png"), "A picture of a cute cat");
 
     // Run the program from the test directory
@@ -54,7 +60,9 @@ Hello world
     assert!(gemini_dir.exists());
 
     let html_output = html_dir.join("test.html");
-    assert_file_contents(&html_output, "\
+    assert_file_contents(
+        &html_output,
+        "\
 <html>
 <head><title>Page Is Cool!</title></head>
 <body><section class=\"h1\">
@@ -63,16 +71,20 @@ Hello world
 </section>
 </body>
 </html>
-");
+",
+    );
 
     let html_asset_output = html_dir.join("test.png");
     assert_file_contents(&html_asset_output, "A picture of a cute cat");
 
     let gemini_output = gemini_dir.join("test.gmi");
-    assert_file_contents(&gemini_output, "\
+    assert_file_contents(
+        &gemini_output,
+        "\
 # Test
 Hello world
-");
+",
+    );
 
     let gemini_asset_output = gemini_dir.join("test.png");
     assert!(gemini_asset_output.exists());
@@ -88,16 +100,19 @@ fn test_missing_layout() {
     let html_dir = parent.join(format!("{}_html", dir_name));
     let gemini_dir = parent.join(format!("{}_gemini", dir_name));
 
-    let _cleanup = TestDir { 
-        paths: vec![test_dir.clone(), html_dir.clone(), gemini_dir.clone()] 
+    let _cleanup = TestDir {
+        paths: vec![test_dir.clone(), html_dir.clone(), gemini_dir.clone()],
     };
 
     // Create test input files
-    create_test_file(&test_dir.join("test.gmi"), "\
+    create_test_file(
+        &test_dir.join("test.gmi"),
+        "\
 --- title: Page Is Cool!
 # Test
 Hello world
-");
+",
+    );
     create_test_file(&test_dir.join("test.png"), "A picture of a cute cat");
 
     // Run the program from the test directory
diff --git a/tests/fixtures/_layout.html b/tests/fixtures/_layout.html
deleted file mode 100644 (file)
index 2b57c5d..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-<html>
-<head><title>{{ title }}</title></head>
-<body>
-{{ content }}
-</body>
-</html>
diff --git a/tests/fixtures/assets/style.css b/tests/fixtures/assets/style.css
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/tests/fixtures/image.png b/tests/fixtures/image.png
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/tests/fixtures/nested/nested.gmi b/tests/fixtures/nested/nested.gmi
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/tests/fixtures/regular.html b/tests/fixtures/regular.html
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/tests/fixtures/test1.gmi b/tests/fixtures/test1.gmi
deleted file mode 100644 (file)
index 65fd302..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
---- title: Test Title
---- description: Test Description
-# Heading
-Some content here
diff --git a/tests/fixtures/test2.gmi b/tests/fixtures/test2.gmi
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/tests/fixtures/test3.gmi b/tests/fixtures/test3.gmi
deleted file mode 100644 (file)
index e69de29..0000000