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 {
}
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
}
}
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
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>
+",
+ );
}
}