mod html;
mod markdown;
mod plaintext;
pub use html::HtmlParser;
pub use markdown::MarkdownParser;
pub use plaintext::PlainTextParser;
use crate::elements::ContentElement;
use crate::error::Result;
#[derive(Debug, Clone)]
pub struct InputParserConfig {
pub default_font: String,
pub default_font_size: f32,
pub page_width: f32,
pub page_height: f32,
pub margin_left: f32,
pub margin_right: f32,
pub margin_top: f32,
pub margin_bottom: f32,
pub line_height: f32,
pub paragraph_spacing: f32,
}
impl Default for InputParserConfig {
fn default() -> Self {
Self {
default_font: "Helvetica".to_string(),
default_font_size: 12.0,
page_width: 612.0, page_height: 792.0, margin_left: 72.0, margin_right: 72.0,
margin_top: 72.0,
margin_bottom: 72.0,
line_height: 1.2,
paragraph_spacing: 12.0,
}
}
}
impl InputParserConfig {
pub fn a4() -> Self {
Self {
page_width: 595.0, page_height: 842.0, ..Default::default()
}
}
pub fn with_font(mut self, font: impl Into<String>) -> Self {
self.default_font = font.into();
self
}
pub fn with_font_size(mut self, size: f32) -> Self {
self.default_font_size = size;
self
}
pub fn with_margins(mut self, margin: f32) -> Self {
self.margin_left = margin;
self.margin_right = margin;
self.margin_top = margin;
self.margin_bottom = margin;
self
}
pub fn content_width(&self) -> f32 {
self.page_width - self.margin_left - self.margin_right
}
pub fn content_height(&self) -> f32 {
self.page_height - self.margin_top - self.margin_bottom
}
pub fn content_start_y(&self) -> f32 {
self.page_height - self.margin_top
}
}
pub trait InputParser: Send + Sync {
fn parse(&self, input: &str, config: &InputParserConfig) -> Result<Vec<ContentElement>>;
fn name(&self) -> &'static str;
fn mime_type(&self) -> &'static str;
fn extensions(&self) -> &[&'static str];
}
pub fn create_parser(format: &str) -> Option<Box<dyn InputParser>> {
match format.to_lowercase().as_str() {
"markdown" | "md" => Some(Box::new(MarkdownParser::new())),
"text" | "plain" | "txt" => Some(Box::new(PlainTextParser::new())),
"html" | "htm" => Some(Box::new(HtmlParser::new())),
_ => None,
}
}
pub fn parser_for_extension(extension: &str) -> Option<Box<dyn InputParser>> {
match extension.to_lowercase().as_str() {
"md" | "markdown" => Some(Box::new(MarkdownParser::new())),
"txt" | "text" => Some(Box::new(PlainTextParser::new())),
"html" | "htm" => Some(Box::new(HtmlParser::new())),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = InputParserConfig::default();
assert_eq!(config.page_width, 612.0);
assert_eq!(config.page_height, 792.0);
assert_eq!(config.default_font_size, 12.0);
}
#[test]
fn test_a4_config() {
let config = InputParserConfig::a4();
assert_eq!(config.page_width, 595.0);
assert_eq!(config.page_height, 842.0);
}
#[test]
fn test_content_dimensions() {
let config = InputParserConfig::default();
assert_eq!(config.content_width(), 468.0);
assert_eq!(config.content_height(), 648.0);
}
#[test]
fn test_create_parser_markdown() {
let parser = create_parser("markdown").unwrap();
assert_eq!(parser.name(), "MarkdownParser");
assert_eq!(parser.mime_type(), "text/markdown");
}
#[test]
fn test_create_parser_text() {
let parser = create_parser("text").unwrap();
assert_eq!(parser.name(), "PlainTextParser");
assert_eq!(parser.mime_type(), "text/plain");
}
#[test]
fn test_create_parser_unknown() {
assert!(create_parser("unknown").is_none());
}
#[test]
fn test_parser_for_extension() {
assert!(parser_for_extension("md").is_some());
assert!(parser_for_extension("txt").is_some());
assert!(parser_for_extension("html").is_some());
assert!(parser_for_extension("htm").is_some());
assert!(parser_for_extension("pdf").is_none());
}
#[test]
fn test_create_parser_html() {
let parser = create_parser("html").unwrap();
assert_eq!(parser.name(), "html");
assert_eq!(parser.mime_type(), "text/html");
}
#[test]
fn test_content_start_y() {
let config = InputParserConfig::default();
assert_eq!(config.content_start_y(), 720.0);
}
}