use super::{utils, HtmlConfig, HtmlFormatter, HtmlOutputError, Output};
use chrono::Local;
pub trait ToHtmlString {
fn to_html_string(
&self,
config: HtmlConfig,
) -> Result<String, HtmlOutputError>;
}
impl<T: Output<Formatter = HtmlFormatter, Error = HtmlOutputError>> ToHtmlString
for T
{
fn to_html_string(
&self,
config: HtmlConfig,
) -> Result<String, HtmlOutputError> {
let mut formatter = HtmlFormatter::new(config);
self.fmt(&mut formatter)?;
Ok(formatter.into_content())
}
}
pub trait ToHtmlPage {
fn to_html_page(
&self,
config: HtmlConfig,
) -> Result<String, HtmlOutputError>;
}
impl<T: Output<Formatter = HtmlFormatter, Error = HtmlOutputError>> ToHtmlPage
for T
{
fn to_html_page(
&self,
config: HtmlConfig,
) -> Result<String, HtmlOutputError> {
let mut formatter = HtmlFormatter::new(config);
self.fmt(&mut formatter)?;
let title = formatter.take_title().unwrap_or_else(|| {
formatter
.config()
.active_page()
.file_stem()
.map(|x| x.to_string_lossy().to_string())
.unwrap_or_else(String::new)
});
let date = formatter
.take_date()
.unwrap_or_else(|| Local::now().naive_local().date());
let template = formatter
.take_template()
.map(|p| formatter.config().template.dir.join(p))
.map(std::fs::read_to_string)
.transpose()
.map_err(|source| HtmlOutputError::TemplateNotLoaded { source })?
.unwrap_or_else(|| formatter.config().template.text.to_string());
let template = template
.replace("%title%", &title)
.replace("%date%", &date.to_string())
.replace(
"%root_path%",
{
let path_str = utils::path_to_uri_string(
formatter
.config()
.to_active_page_path_to_wiki_root()
.as_path(),
);
if path_str.is_empty() {
String::new()
} else {
format!("{}/", path_str)
}
}
.as_str(),
)
.replace(
"%wiki_path%",
&utils::path_to_uri_string(
formatter.config().as_active_page_path_within_wiki(),
),
)
.replace(
"%css%",
formatter.config().to_current_wiki().css_name.as_str(),
)
.replace("%encoding%", "utf-8")
.replace("%content%", formatter.get_content());
Ok(template)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
HtmlOutputResult, HtmlRuntimeConfig, HtmlTemplateConfig, HtmlWikiConfig,
};
use chrono::NaiveDate;
use std::path::PathBuf;
struct TestOutput<F: Fn(&mut HtmlFormatter) -> HtmlOutputResult>(F);
impl<F: Fn(&mut HtmlFormatter) -> HtmlOutputResult> Output for TestOutput<F> {
type Formatter = HtmlFormatter;
type Error = HtmlOutputError;
fn fmt(&self, f: &mut Self::Formatter) -> HtmlOutputResult {
self.0(f)?;
Ok(())
}
}
fn _text(
text: impl Into<String>,
) -> impl Fn(&mut HtmlFormatter) -> HtmlOutputResult {
let text = text.into();
move |f: &mut HtmlFormatter| {
use std::fmt::Write;
write!(f, "{}", text.as_str())?;
Ok(())
}
}
#[test]
fn to_html_string_should_produce_a_string_representing_only_the_html_of_the_output(
) {
let output = TestOutput(_text("<b>I am some html output</b>"));
let result = output.to_html_string(HtmlConfig::default()).unwrap();
assert_eq!(result, "<b>I am some html output</b>");
}
#[test]
fn to_html_page_should_not_replace_placeholders_in_content() {
let output = TestOutput(_text("some %title% content"));
let template = HtmlTemplateConfig::from_text("<html>%content%</html>");
let config = HtmlConfig {
template,
..Default::default()
};
let result = output.to_html_page(config).unwrap();
assert_eq!(result, "<html>some %title% content</html>");
}
#[test]
fn to_html_page_should_replace_title_placeholder_with_provided_title() {
let output = TestOutput(|f| {
f.set_title("some title");
Ok(())
});
let template = HtmlTemplateConfig::from_text("<html>%title%</html>");
let config = HtmlConfig {
template,
..Default::default()
};
let result = output.to_html_page(config).unwrap();
assert_eq!(result, "<html>some title</html>");
}
#[test]
fn to_html_page_should_replace_title_placeholder_with_filename_if_no_provided_title(
) {
let output = TestOutput(_text(""));
let template = HtmlTemplateConfig::from_text("<html>%title%</html>");
let config = HtmlConfig {
template,
runtime: HtmlRuntimeConfig {
page: PathBuf::from("some/page.wiki"),
..Default::default()
},
..Default::default()
};
let result = output.to_html_page(config).unwrap();
assert_eq!(result, "<html>page</html>");
}
#[test]
fn to_html_page_should_replace_date_placeholder_with_provided_date() {
let output = TestOutput(|f| {
f.set_date(&NaiveDate::from_ymd(2003, 11, 27));
Ok(())
});
let template = HtmlTemplateConfig::from_text("<html>%date%</html>");
let config = HtmlConfig {
template,
..Default::default()
};
let result = output.to_html_page(config).unwrap();
assert_eq!(result, "<html>2003-11-27</html>");
}
#[test]
fn to_html_page_should_replace_date_placeholder_with_current_date_if_no_provided_date(
) {
let output = TestOutput(_text(""));
let template = HtmlTemplateConfig::from_text("<html>%date%</html>");
let config = HtmlConfig {
template,
..Default::default()
};
let result = output.to_html_page(config).unwrap();
assert_eq!(
result,
format!("<html>{}</html>", Local::now().naive_local().date())
);
}
#[test]
fn to_html_page_should_replace_root_path_placeholder_with_path_relative_to_wiki_root(
) {
let output = TestOutput(_text(""));
let template =
HtmlTemplateConfig::from_text("<html>%root_path%</html>");
let config = HtmlConfig {
template: template.clone(),
wikis: vec![HtmlWikiConfig {
path: ["some", "path"].iter().collect(),
..Default::default()
}],
runtime: HtmlRuntimeConfig {
wiki_index: Some(0),
page: ["some", "path", "to", "a", "file.wiki"].iter().collect(),
},
..Default::default()
};
let result = output.to_html_page(config).unwrap();
assert_eq!(result, "<html>../../</html>");
let config = HtmlConfig {
template: template.clone(),
wikis: vec![HtmlWikiConfig {
path: ["some", "path"].iter().collect(),
..Default::default()
}],
runtime: HtmlRuntimeConfig {
wiki_index: Some(0),
page: ["some", "path", "to", "file.wiki"].iter().collect(),
},
..Default::default()
};
let result = output.to_html_page(config).unwrap();
assert_eq!(result, "<html>../</html>");
let config = HtmlConfig {
template,
wikis: vec![HtmlWikiConfig {
path: ["some", "path"].iter().collect(),
..Default::default()
}],
runtime: HtmlRuntimeConfig {
wiki_index: Some(0),
page: ["some", "path", "file.wiki"].iter().collect(),
},
..Default::default()
};
let result = output.to_html_page(config).unwrap();
assert_eq!(result, "<html></html>");
}
#[test]
fn to_html_page_should_replace_wiki_path_placeholder_with_file_path() {
let output = TestOutput(_text(""));
let template =
HtmlTemplateConfig::from_text("<html>%wiki_path%</html>");
let config = HtmlConfig {
template,
wikis: vec![HtmlWikiConfig {
path: ["some", "path"].iter().collect(),
..Default::default()
}],
runtime: HtmlRuntimeConfig {
wiki_index: Some(0),
page: ["some", "path", "to", "a", "file.wiki"].iter().collect(),
},
..Default::default()
};
let result = output.to_html_page(config).unwrap();
assert_eq!(result, "<html>to/a/file.wiki</html>");
}
#[test]
fn to_html_page_should_replace_css_placeholder_with_provided_css_name() {
let output = TestOutput(_text(""));
let template = HtmlTemplateConfig::from_text("<html>%css%</html>");
let config = HtmlConfig {
template,
wikis: vec![HtmlWikiConfig {
css_name: "css_file".to_string(),
..Default::default()
}],
runtime: HtmlRuntimeConfig {
wiki_index: Some(0),
..Default::default()
},
..Default::default()
};
let result = output.to_html_page(config).unwrap();
assert_eq!(result, "<html>css_file</html>");
}
#[test]
fn to_html_page_should_replace_css_placeholder_with_default_if_no_provided_css_name(
) {
let output = TestOutput(_text(""));
let template = HtmlTemplateConfig::from_text("<html>%css%</html>");
let config = HtmlConfig {
template,
..Default::default()
};
let result = output.to_html_page(config).unwrap();
assert_eq!(
result,
format!("<html>{}</html>", HtmlWikiConfig::default_css_name())
);
}
#[test]
fn to_html_page_should_replace_encoding_placeholder_with_utf8() {
let output = TestOutput(_text(""));
let template = HtmlTemplateConfig::from_text("<html>%encoding%</html>");
let config = HtmlConfig {
template,
..Default::default()
};
let result = output.to_html_page(config).unwrap();
assert_eq!(result, "<html>utf-8</html>");
}
#[test]
fn to_html_page_should_replace_content_placeholder_with_output_results() {
let output = TestOutput(_text("some output content"));
let template = HtmlTemplateConfig::from_text("<html>%content%</html>");
let config = HtmlConfig {
template,
..Default::default()
};
let result = output.to_html_page(config).unwrap();
assert_eq!(result, "<html>some output content</html>");
}
}