html_generator/
performance.rsuse crate::{HtmlError, Result};
use comrak::{markdown_to_html, ComrakOptions};
use minify_html::{minify, Cfg};
use std::{fs, path::Path};
use tokio::task;
fn default_minify_cfg() -> Cfg {
let mut cfg = Cfg::new();
cfg.do_not_minify_doctype = true;
cfg.ensure_spec_compliant_unquoted_attribute_values = true;
cfg.keep_closing_tags = true;
cfg.keep_html_and_head_opening_tags = true;
cfg.keep_spaces_between_attributes = true;
cfg.keep_comments = false;
cfg.minify_css = true;
cfg.minify_js = true;
cfg.remove_bangs = true;
cfg.remove_processing_instructions = true;
cfg
}
pub fn minify_html(file_path: &Path) -> Result<String> {
let content = fs::read_to_string(file_path).map_err(|e| {
HtmlError::MinificationError(format!(
"Failed to read file: {}",
e
))
})?;
let minified_content =
minify(content.as_bytes(), &default_minify_cfg());
String::from_utf8(minified_content).map_err(|e| {
HtmlError::MinificationError(format!(
"Invalid UTF-8 in minified content: {}",
e
))
})
}
pub async fn async_generate_html(markdown: &str) -> Result<String> {
let markdown = markdown.to_string();
task::spawn_blocking(move || {
let options = ComrakOptions::default();
Ok(markdown_to_html(&markdown, &options))
})
.await
.map_err(|e| HtmlError::MarkdownConversionError(e.to_string()))?
}
pub fn generate_html(markdown: &str) -> Result<String> {
let options = ComrakOptions::default();
Ok(markdown_to_html(markdown, &options))
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs::File;
use std::io::Write;
use tempfile::tempdir;
fn create_html_file(file_path: &Path, content: &str) {
let mut file = File::create(file_path).unwrap();
file.write_all(content.as_bytes()).unwrap();
}
#[test]
fn test_minify_html_basic() {
let dir = tempdir().unwrap();
let file_path = dir.path().join("test.html");
let html = "<html> <body> <p>Test</p> </body> </html>";
create_html_file(&file_path, html);
let result = minify_html(&file_path);
assert!(result.is_ok());
assert_eq!(
result.unwrap(),
"<html><body><p>Test</p></body></html>"
);
}
#[test]
fn test_minify_html_with_comments() {
let dir = tempdir().unwrap();
let file_path = dir.path().join("test_comments.html");
let html = "<html> <body> <!-- This is a comment --> <p>Test</p> </body> </html>";
create_html_file(&file_path, html);
let result = minify_html(&file_path);
assert!(result.is_ok());
assert_eq!(
result.unwrap(),
"<html><body><p>Test</p></body></html>"
);
}
#[test]
fn test_minify_html_with_css() {
let dir = tempdir().unwrap();
let file_path = dir.path().join("test_css.html");
let html = "<html><head><style> body { color: red; } </style></head><body><p>Test</p></body></html>";
create_html_file(&file_path, html);
let result = minify_html(&file_path);
assert!(result.is_ok());
assert_eq!(result.unwrap(), "<html><head><style>body{color:red}</style></head><body><p>Test</p></body></html>");
}
#[test]
fn test_minify_html_with_js() {
let dir = tempdir().unwrap();
let file_path = dir.path().join("test_js.html");
let html = "<html><head><script> function test() { console.log('Hello'); } </script></head><body><p>Test</p></body></html>";
create_html_file(&file_path, html);
let result = minify_html(&file_path);
assert!(result.is_ok());
let minified = result.unwrap();
assert!(minified.contains("<script>"));
assert!(minified.contains("console.log"));
assert!(minified.contains("Hello"));
assert!(minified.contains("<p>Test</p>"));
}
#[test]
fn test_minify_html_non_existent_file() {
let dir = tempdir().unwrap();
let file_path = dir.path().join("non_existent.html");
let result = minify_html(&file_path);
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
HtmlError::MinificationError(_)
));
}
#[test]
fn test_minify_html_invalid_utf8() {
let dir = tempdir().unwrap();
let file_path = dir.path().join("invalid_utf8.html");
let invalid_utf8 = vec![0, 159, 146, 150]; let mut file = File::create(&file_path).unwrap();
file.write_all(&invalid_utf8).unwrap();
let result = minify_html(&file_path);
assert!(
result.is_err(),
"Expected an error due to invalid UTF-8 sequence"
);
assert!(matches!(
result.unwrap_err(),
HtmlError::MinificationError(_)
));
}
#[test]
fn test_generate_html_basic() {
let markdown = "# Test\n\nThis is a test.";
let result = generate_html(markdown);
assert!(result.is_ok());
let html = result.unwrap();
assert!(html.contains("<h1>Test</h1>"));
assert!(html.contains("<p>This is a test.</p>"));
}
#[test]
fn test_generate_html_complex() {
let markdown = "# Header\n\n## Subheader\n\n- List item 1\n- List item 2\n\n```rust\nfn main() {\n println!(\"Hello, world!\");\n}\n```";
let result = generate_html(markdown);
assert!(result.is_ok());
let html = result.unwrap();
assert!(html.contains("<h1>Header</h1>"));
assert!(html.contains("<h2>Subheader</h2>"));
assert!(html.contains("<ul>"));
assert!(html.contains("<li>List item 1</li>"));
assert!(html.contains("<li>List item 2</li>"));
assert!(html.contains("<pre><code class=\"language-rust\">"));
assert!(html.contains("fn main()"));
assert!(html.contains("println!"));
assert!(html.contains("Hello, world!"));
}
#[test]
fn test_generate_html_empty_input() {
let markdown = "";
let result = generate_html(markdown);
assert!(result.is_ok());
let html = result.unwrap();
assert!(html.trim().is_empty());
}
}