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, file_type });
use std::fs::{copy, create_dir_all};
use std::path::Path;
-use crate::file_handler::{File, FileHandlerStrategy, FileType};
+use crate::file_handler::{File, FileType, Strategy as FileHandlerStrategy};
impl Strategy {
- fn handle(&self, source: &Path, destination: &Path, file: &File) {
+ fn handle(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();
}
fn handle_html(&self, source: &Path, destination: &Path, file: &File, _l: &str) {
- self.handle(source, destination, file)
+ Strategy::handle(source, destination, file);
}
fn handle_gemini(&self, source: &Path, destination: &Path, file: &File) {
- self.handle(source, destination, file)
+ Strategy::handle(source, destination, file);
}
}
use std::io::Write;
use std::path::Path;
-use crate::file_handler::{File, FileHandlerStrategy, FileType};
+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(&self, line: &str) -> bool {
+ fn is_title(line: &str) -> bool {
line.starts_with("--- title:")
}
- fn is_description(&self, line: &str) -> bool {
+ fn is_description(line: &str) -> bool {
line.starts_with("--- description:")
}
- fn get_title<'a>(&self, line: &'a str) -> &'a str {
+ fn get_title(line: &str) -> &str {
line.split_once("--- title:").unwrap().1
}
- fn get_description<'a>(&self, line: &'a str) -> &'a str {
+ fn get_description(line: &str) -> &str {
line.split_once("--- description:").unwrap().1
}
}
let gemini_contents = read_to_string(&file.path).unwrap();
// Front matter extraction
- let lines: Vec<&str> = gemini_contents.split("\n").collect();
+ 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.iter() {
- if self.is_title(line) {
- title = self.get_title(line).trim();
+ for line in slice {
+ if Strategy::is_title(line) {
+ title = Strategy::get_title(line).trim();
lines_found += 1;
continue;
}
- if self.is_description(line) {
- description = self.get_description(line).trim();
+ 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 content_html = render_html(&parse(&gemini_source[..]));
let generated_html = layout
.replace("{{ title }}", title)
let gemini_contents = read_to_string(&file.path).unwrap();
// Front matter extraction
- let lines: Vec<&str> = gemini_contents.split("\n").collect();
+ 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) {
+ for line in slice {
+ if Strategy::is_title(line) {
lines_found += 1;
continue;
}
- if self.is_description(line) {
+ if Strategy::is_description(line) {
lines_found += 1;
continue;
}
#[test]
fn detects_title() {
- let strategy = Strategy {};
- assert!(strategy.is_title("--- title: Hello!"));
+ assert!(Strategy::is_title("--- title: Hello!"));
}
#[test]
fn does_not_detect_other_keys_as_title() {
- let strategy = Strategy {};
- assert!(!strategy.is_title("--- description: Hello!"));
+ assert!(!Strategy::is_title("--- description: Hello!"));
}
#[test]
fn detects_description() {
- let strategy = Strategy {};
- assert!(strategy.is_description("--- description: What is this?"));
+ 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?"));
+ assert!(!Strategy::is_description("--- title: What is this?"));
}
#[test]
fn extracts_title() {
- let strategy = Strategy {};
- assert_eq!(strategy.get_title("--- title: Hello!").trim(), "Hello!");
+ 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?")
+ Strategy
+ ::get_description("--- description: What is this?")
.trim(),
"What is this?"
);
use std::path::Path;
-use crate::file_handler::{File, FileHandlerStrategy, FileType};
+use crate::file_handler::{File, FileType, Strategy as FileHandlerStrategy};
impl FileHandlerStrategy for Strategy {
fn is(&self, path: &Path) -> bool {
use std::path::{Path, PathBuf};
pub struct FileHandler {
- pub strategies: Vec<Box<dyn FileHandlerStrategy>>,
+ pub strategies: Vec<Box<dyn Strategy>>,
pub layout: Option<String>,
}
impl FileHandler {
pub fn identify(&self, path: &Path) -> FileType {
- for strategy in self.strategies.iter() {
+ for strategy in &self.strategies {
if strategy.is(path) {
return strategy.identify();
}
gemini_destination: &Path,
files: &[File],
) {
- files.iter().for_each(|file| {
+ for file in files {
self.handle(source, html_destination, gemini_destination, file);
- });
+ }
}
pub fn handle(
}
}
-pub trait FileHandlerStrategy {
+pub trait Strategy {
fn is(&self, path: &Path) -> bool;
fn identify(&self) -> FileType;
fn can_handle(&self, file_type: &FileType) -> bool;
file_type: FileType,
}
- impl FileHandlerStrategy for MockStrategy {
+ impl Strategy for MockStrategy {
fn is(&self, _path: &Path) -> bool {
self.is_match
}
ListItem(String),
}
-/// Parses gemtext source code into a vector of GeminiLine elements.
+/// Parses gemtext source code into a vector of `GeminiLine` elements.
///
/// # Arguments
/// * `source` - A string slice that contains the gemtext
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('#') => GeminiLine::Heading(1, s[1..].to_string()),
s if s.starts_with("=>") => {
let content = s[2..].trim();
match content.split_once(char::is_whitespace) {
}
}
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::Quote(s[1..].to_string()),
s if s.starts_with("```") => GeminiLine::PreformattedToggle(true, s[3..].to_string()),
_ => GeminiLine::Text(line.to_string(), false),
}
use crate::gemini_parser::GeminiLine;
-/// Renders HTML from a vector of GeminiLine elements.
+/// Renders HTML from a vector of `GeminiLine` elements.
///
/// # Arguments
-/// * `lines` - Vector of GeminiLine elements to render
+/// * `lines` - Vector of `GeminiLine` elements to render
///
/// # Returns
/// A String containing the rendered HTML.
-pub fn render_html(lines: Vec<GeminiLine>) -> String {
+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 {
+ for line in lines {
result.push_str(&line_preamble(line, last_line, &mut heading_stack));
result.push_str(&line_content(line));
result.push('\n');
}
}
heading_stack.push(*level);
- html.push_str(&format!("<section class=\"h{}\">\n", level));
+ html.push_str(&format!("<section class=\"h{level}\">\n"));
}
GeminiLine::ListItem(_) => match last_line {
Some(GeminiLine::ListItem(_)) => {}
fn line_content(line: &GeminiLine) -> String {
match line {
- GeminiLine::Text(content, false) => format!("<p>{}</p>", content),
+ 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=\"{}\">{}</a></p>", url, display)
+ format!("<p class=\"a\"><a href=\"{url}\">{display}</a></p>")
}
- GeminiLine::Heading(level, content) => format!("<h{}>{}</h{}>", level, content, level),
- GeminiLine::ListItem(content) => format!("<li>{}</li>", content),
+ 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)
+ format!("<pre aria-label=\"{alt_text}\">")
}
GeminiLine::PreformattedToggle(false, _) => "</pre>".to_string(),
GeminiLine::Text(content, true) | GeminiLine::Quote(content) => content.to_string(),
#[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");
+ assert_eq!(render_html(&input), "<p>Hello world</p>\n");
}
#[test]
GeminiLine::Heading(2, "Another Sub".to_string()),
];
assert_eq!(
- render_html(input),
+ render_html(&input),
"<section class=\"h1\">\n\
<h1>Top</h1>\n\
<section class=\"h2\">\n\
GeminiLine::ListItem("New list".to_string()),
];
assert_eq!(
- render_html(input),
+ render_html(&input),
"<ul>\n\
<li>First</li>\n\
<li>Second</li>\n\
GeminiLine::Quote("New quote".to_string()),
];
assert_eq!(
- render_html(input),
+ render_html(&input),
"<blockquote>\n\
First quote\n\
Still quoting\n\
GeminiLine::PreformattedToggle(false, String::new()),
];
assert_eq!(
- render_html(input),
+ render_html(&input),
"<pre aria-label=\"code\">\n\
let x = 42;\n\
</pre>\n"
GeminiLine::Link("https://rust-lang.org".to_string(), "".to_string()),
];
assert_eq!(
- render_html(input),
+ 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"
);
GeminiLine::Heading(2, "Another Section".to_string()),
];
assert_eq!(
- render_html(input),
+ render_html(&input),
"<section class=\"h1\">\n\
<h1>Title</h1>\n\
<p>Intro</p>\n\
#[test]
fn test_empty_input() {
let input = Vec::new();
- assert_eq!(render_html(input), "");
+ assert_eq!(render_html(&input), "");
}
}
let source = current_dir()?;
let source_name = source.file_name().unwrap().to_string_lossy();
let parent = source.parent().unwrap();
- let gemini_destination_name = format!("{}_gemini", source_name);
+ let gemini_destination_name = format!("{source_name}_gemini");
let gemini_destination = parent.join(gemini_destination_name);
- let html_destination_name = format!("{}_html", source_name);
+ let html_destination_name = format!("{source_name}_html");
let html_destination = parent.join(html_destination_name);
// Step 1. Identify the files
// 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);
+ eprintln!("{error}");
exit(1);
}
}