diff options
| author | Ruben Beltran del Rio <git@r.bdr.sh> | 2025-01-09 22:02:19 +0100 |
|---|---|---|
| committer | Ruben Beltran del Rio <git@r.bdr.sh> | 2025-01-09 22:02:19 +0100 |
| commit | f5ac7b8efb4e0d1dd197ddecbad2b46fd19f5ddb (patch) | |
| tree | 8859ed7bc7b1caf8d18d04ea438d6114b06eea78 /src | |
| parent | f08e8d64dce087201089d2b3276b3dfcdb000173 (diff) | |
Remove no longer necessary code
Diffstat (limited to 'src')
| -rw-r--r-- | src/file_finder.rs | 1 | ||||
| -rw-r--r-- | src/file_handler/mod.rs | 7 | ||||
| -rw-r--r-- | src/gemini_parser.rs | 272 | ||||
| -rw-r--r-- | src/html_renderer.rs | 256 |
4 files changed, 7 insertions, 529 deletions
diff --git a/src/file_finder.rs b/src/file_finder.rs index a834bd9..ef57950 100644 --- a/src/file_finder.rs +++ b/src/file_finder.rs @@ -78,6 +78,7 @@ mod tests { 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("notalayout.html"), ""); create_test_file(&test_dir.join("nested/nested.gmi"), ""); create_test_file(&test_dir.join("assets/style.css"), ""); create_test_file(&test_dir.join("image.png"), ""); diff --git a/src/file_handler/mod.rs b/src/file_handler/mod.rs index e8a446b..4e4bed9 100644 --- a/src/file_handler/mod.rs +++ b/src/file_handler/mod.rs @@ -208,11 +208,16 @@ mod tests { let handler = FileHandler { strategies: vec![Box::new(mock_strategy)], - layout: None, + layout: Some("None".to_string()), }; let path = PathBuf::from("test.whatever"); + let file = File{ + path: path.clone(), + file_type: FileType::Gemini + }; assert!(matches!(handler.identify(&path), FileType::Gemini)); + handler.handle(&path, &path, &path, &file); } #[test] diff --git a/src/gemini_parser.rs b/src/gemini_parser.rs deleted file mode 100644 index f7aa0cc..0000000 --- a/src/gemini_parser.rs +++ /dev/null @@ -1,272 +0,0 @@ -#[derive(PartialEq, Eq, Debug)] -pub enum GeminiLine { - Text(String, bool), - PreformattedToggle(bool, String), - Heading(u8, String), - Link(String, String), - Quote(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) - }) - .0 -} - -fn parse_preformatted_line(line: &str) -> GeminiLine { - match line { - s if s.starts_with("```") => GeminiLine::PreformattedToggle(false, String::new()), - _ => GeminiLine::Text(line.to_string(), true), - } -} - -fn parse_line(line: &str) -> GeminiLine { - match line { - s if s.starts_with("###") => GeminiLine::Heading(3, s[3..].to_string()), - s if s.starts_with("##") => GeminiLine::Heading(2, s[2..].to_string()), - s if s.starts_with('#') => GeminiLine::Heading(1, s[1..].to_string()), - 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()) - } - 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()), - _ => GeminiLine::Text(line.to_string(), false), - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[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("###"), GeminiLine::Heading(3, String::new())); - 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) - ); - } - - #[test] - fn test_links() { - assert_eq!( - parse_line("=> https://example.com Link text"), - GeminiLine::Link("https://example.com".to_string(), "Link text".to_string()) - ); - assert_eq!( - parse_line("=> /local/path"), - GeminiLine::Link("/local/path".to_string(), String::new()) - ); - - assert_eq!( - parse_line("=>"), - GeminiLine::Link(String::new(), String::new()) - ); - assert_eq!( - parse_line("=> "), - GeminiLine::Link(String::new(), String::new()) - ); - assert_eq!( - parse_line("=> multiple spaces in text"), - GeminiLine::Link("multiple".to_string(), "spaces in text".to_string()) - ); - - assert_eq!( - parse_preformatted_line("=> https://example.com Link text"), - GeminiLine::Text("=> https://example.com Link text".to_string(), true) - ); - } - - #[test] - fn test_list_items() { - assert_eq!( - parse_line("* List item"), - GeminiLine::ListItem("List item".to_string()) - ); - - assert_eq!(parse_line("* "), GeminiLine::ListItem(String::new())); - 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()) - ); - } - - #[test] - fn test_quotes() { - assert_eq!( - parse_line(">Quote text"), - GeminiLine::Quote("Quote text".to_string()) - ); - - assert_eq!(parse_line(">"), GeminiLine::Quote(String::new())); - assert_eq!(parse_line("> "), GeminiLine::Quote(" ".to_string())); - assert_eq!( - parse_line(">>Nested"), - GeminiLine::Quote(">Nested".to_string()) - ); - } - - #[test] - fn test_preformatted() { - assert_eq!( - parse_line("```alt-text"), - GeminiLine::PreformattedToggle(true, "alt-text".to_string()) - ); - - assert_eq!( - parse_line("```"), - GeminiLine::PreformattedToggle(true, String::new()) - ); - 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"), - GeminiLine::PreformattedToggle(false, String::new()) - ); - assert_eq!( - parse_preformatted_line("```"), - GeminiLine::PreformattedToggle(false, String::new()) - ); - } - - #[test] - fn test_text() { - // Normal case - assert_eq!( - parse_line("Regular text"), - GeminiLine::Text("Regular text".to_string(), false) - ); - - // Edge cases - assert_eq!(parse_line(""), GeminiLine::Text(String::new(), false)); - assert_eq!(parse_line(" "), GeminiLine::Text(" ".to_string(), false)); - assert_eq!(parse_line(" "), GeminiLine::Text(" ".to_string(), false)); - } - - #[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) - ); - } - - #[test] - fn test_full_document() { - let input = "\ -# Heading 1 -## Heading 2 -### Heading 3 -Regular text -=> https://example.com Link text -* List item ->Quote -```alt -code -# Heading 1 -## Heading 2 -### Heading 3 -=> https://example.com Link text -* List item ->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, String::new()), - ] - ); - } -} diff --git a/src/html_renderer.rs b/src/html_renderer.rs deleted file mode 100644 index 6b9ce38..0000000 --- a/src/html_renderer.rs +++ /dev/null @@ -1,256 +0,0 @@ -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: &[GeminiLine]) -> String { - let mut heading_stack = Vec::new(); - let mut last_line: Option<&GeminiLine> = None; - let mut result = String::new(); - - for line in lines { - result.push_str(&line_preamble(line, last_line, &mut heading_stack)); - result.push_str(&line_content(line)); - result.push('\n'); - last_line = Some(line); - } - - result.push_str(&finalize_html(&heading_stack, last_line)); - result -} - -fn line_preamble( - line: &GeminiLine, - last_line: Option<&GeminiLine>, - 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::Quote(_) => match line { - GeminiLine::Quote(_) => {} - _ => html.push_str("</blockquote>\n"), - }, - _ => {} - } - } - - match line { - GeminiLine::Heading(level, _) => { - while let Some(open_heading) = heading_stack.pop() { - // You just encountered a more important heading. - // Put it back. Desist. - if open_heading < *level { - heading_stack.push(open_heading); - break; - } - - html.push_str("</section>\n"); - - if open_heading == *level { - break; - } - } - heading_stack.push(*level); - html.push_str(&format!("<section class=\"h{level}\">\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"), - }, - _ => {} - } - - html -} - -fn line_content(line: &GeminiLine) -> String { - match line { - GeminiLine::Text(content, false) => format!("<p>{content}</p>"), - GeminiLine::Link(url, text) => { - let display = if text.is_empty() { url } else { text }; - format!("<p class=\"a\"><a href=\"{url}\">{display}</a></p>") - } - GeminiLine::Heading(level, content) => format!("<h{level}>{content}</h{level}>"), - GeminiLine::ListItem(content) => format!("<li>{content}</li>"), - 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(), - } -} - -fn finalize_html(heading_stack: &[u8], last_line: Option<&GeminiLine>) -> String { - let mut html = String::new(); - - match last_line { - Some(GeminiLine::ListItem(_)) => html.push_str("</ul>\n"), - Some(GeminiLine::Quote(_)) => html.push_str("</blockquote>\n"), - _ => {} - } - - for _ in heading_stack { - html.push_str("</section>\n"); - } - - html -} - -#[cfg(test)] -mod tests { - use super::*; - - #[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"); - } - - #[test] - fn test_heading_nesting() { - let input = vec![ - GeminiLine::Heading(1, "Top".to_string()), - GeminiLine::Heading(2, "Sub".to_string()), - GeminiLine::Heading(3, "SubSub".to_string()), - GeminiLine::Heading(2, "Another Sub".to_string()), - ]; - assert_eq!( - render_html(&input), - "<section class=\"h1\">\n\ - <h1>Top</h1>\n\ - <section class=\"h2\">\n\ - <h2>Sub</h2>\n\ - <section class=\"h3\">\n\ - <h3>SubSub</h3>\n\ - </section>\n\ - </section>\n\ - <section class=\"h2\">\n\ - <h2>Another Sub</h2>\n\ - </section>\n\ - </section>\n" - ); - } - - #[test] - fn test_list_transitions() { - let input = vec![ - GeminiLine::ListItem("First".to_string()), - GeminiLine::ListItem("Second".to_string()), - GeminiLine::Text("Break".to_string(), false), - GeminiLine::ListItem("New list".to_string()), - ]; - assert_eq!( - render_html(&input), - "<ul>\n\ - <li>First</li>\n\ - <li>Second</li>\n\ - </ul>\n\ - <p>Break</p>\n\ - <ul>\n\ - <li>New list</li>\n\ - </ul>\n" - ); - } - - #[test] - fn test_quote_transitions() { - let input = vec![ - GeminiLine::Quote("First quote".to_string()), - GeminiLine::Quote("Still quoting".to_string()), - GeminiLine::Text("Normal text".to_string(), false), - GeminiLine::Quote("New quote".to_string()), - ]; - assert_eq!( - render_html(&input), - "<blockquote>\n\ - First quote\n\ - Still quoting\n\ - </blockquote>\n\ - <p>Normal text</p>\n\ - <blockquote>\n\ - New quote\n\ - </blockquote>\n" - ); - } - - #[test] - fn test_preformatted() { - let input = vec![ - GeminiLine::PreformattedToggle(true, "code".to_string()), - GeminiLine::Text("let x = 42;".to_string(), true), - GeminiLine::PreformattedToggle(false, String::new()), - ]; - assert_eq!( - render_html(&input), - "<pre aria-label=\"code\">\n\ - let x = 42;\n\ - </pre>\n" - ); - } - - #[test] - fn test_links() { - let input = vec![ - GeminiLine::Link("https://example.com".to_string(), "Example".to_string()), - GeminiLine::Link("https://rust-lang.org".to_string(), String::new()), - ]; - assert_eq!( - render_html(&input), - "<p class=\"a\"><a href=\"https://example.com\">Example</a></p>\n\ - <p class=\"a\"><a href=\"https://rust-lang.org\">https://rust-lang.org</a></p>\n" - ); - } - - #[test] - fn test_complex_nesting() { - let input = vec![ - GeminiLine::Heading(1, "Title".to_string()), - GeminiLine::Text("Intro".to_string(), false), - GeminiLine::Heading(2, "Section".to_string()), - GeminiLine::ListItem("Point 1".to_string()), - GeminiLine::ListItem("Point 2".to_string()), - GeminiLine::Quote("Important quote".to_string()), - GeminiLine::Heading(2, "Another Section".to_string()), - ]; - assert_eq!( - render_html(&input), - "<section class=\"h1\">\n\ - <h1>Title</h1>\n\ - <p>Intro</p>\n\ - <section class=\"h2\">\n\ - <h2>Section</h2>\n\ - <ul>\n\ - <li>Point 1</li>\n\ - <li>Point 2</li>\n\ - </ul>\n\ - <blockquote>\n\ - Important quote\n\ - </blockquote>\n\ - </section>\n\ - <section class=\"h2\">\n\ - <h2>Another Section</h2>\n\ - </section>\n\ - </section>\n" - ); - } - - #[test] - fn test_empty_input() { - let input = Vec::new(); - assert_eq!(render_html(&input), ""); - } -} |