pub mod component;
pub mod config;
pub mod error;
pub mod parser;
pub mod renderer;
pub mod theme;
pub use component::{Component, ComponentRegistry};
pub use config::Config;
pub use error::Error;
pub use parser::{parse, InlineNode, Node, ParseOptions, ParsedDocument};
pub use renderer::{render, RenderOptions};
pub use theme::{Theme, ThemeOptions};
pub fn render_to_html(markdown: &str, config: Option<Config>) -> Result<String, error::Error> {
let config = config.unwrap_or_default();
let parse_options = ParseOptions::from(&config);
let registry = ComponentRegistry::default();
let preprocessed_markdown = preprocess_components(markdown);
let ast = parser::parse(&preprocessed_markdown, &parse_options)?;
renderer::render(&ast, ®istry, &config)
}
pub fn render_to_html_with_registry(
markdown: &str,
config: Option<Config>,
registry: &ComponentRegistry,
) -> Result<String, error::Error> {
let config = config.unwrap_or_default();
let parse_options = ParseOptions::from(&config);
let preprocessed_markdown = preprocess_components(markdown);
let ast = parser::parse(&preprocessed_markdown, &parse_options)?;
renderer::render(&ast, registry, &config)
}
fn preprocess_components(markdown: &str) -> String {
use regex::Regex;
let component_regex = Regex::new(r"(?m)^::([a-zA-Z0-9_-]+)(\{([^}]*)\})?\s*$").unwrap();
let component_end_regex = Regex::new(r"(?m)^::\s*$").unwrap();
let nested_component_regex = Regex::new(r"(?m)^:::([a-zA-Z0-9_-]+)(\{([^}]*)\})?\s*$").unwrap();
let nested_component_end_regex = Regex::new(r"(?m)^:::\s*$").unwrap();
let mut result = component_regex
.replace_all(markdown, |caps: ®ex::Captures| {
let component_name = &caps[1];
let attributes = caps.get(3).map_or("", |m| m.as_str());
format!(
"<!-- component_start:{}:{} -->\n",
component_name, attributes
)
})
.to_string();
result = component_end_regex
.replace_all(&result, |_: ®ex::Captures| {
"<!-- component_end -->\n".to_string()
})
.to_string();
result = nested_component_regex
.replace_all(&result, |caps: ®ex::Captures| {
let component_name = &caps[1];
let attributes = caps.get(3).map_or("", |m| m.as_str());
format!(
"<!-- nested_component_start:{}:{} -->\n",
component_name, attributes
)
})
.to_string();
result = nested_component_end_regex
.replace_all(&result, |_: ®ex::Captures| {
"<!-- nested_component_end -->\n".to_string()
})
.to_string();
result
}
pub fn render_file(path: &str, config: Option<Config>) -> Result<String, error::Error> {
use std::fs;
let markdown = fs::read_to_string(path).map_err(error::Error::IoError)?;
render_to_html(&markdown, config)
}
pub fn render_file_to_file(
input: &str,
output: &str,
config: Option<Config>,
) -> Result<(), error::Error> {
use std::fs;
let html = render_file(input, config)?;
fs::write(output, html).map_err(error::Error::IoError)?;
Ok(())
}