use crate::error::SiteError; use crate::html::{parse, Content, Element, ElementTag, HtmlPage}; use crate::parser::WikitextParser; use crate::site::Site; use crate::util::{get_mtime, join_subpath, make_path_relative_to}; use crate::wikitext::Snippet; use log::{info, trace}; use std::path::{Path, PathBuf}; use std::time::SystemTime; #[derive(Debug, Eq, PartialEq)] pub struct WikitextPage { meta: PageMeta, wikitext: String, } impl WikitextPage { pub fn new(meta: PageMeta, wikitext: String) -> Self { Self { meta, wikitext } } pub fn read(srcdir: &Path, filename: &Path) -> Result { info!("input file: {}", filename.display()); let relative = make_path_relative_to(srcdir, filename).with_extension(""); let absolute = Path::new("/").join(&relative); let name = relative .file_name() .unwrap_or_else(|| panic!("get filename from {}", relative.display())) .to_string_lossy() .to_string(); let data = std::fs::read(filename).map_err(|e| SiteError::FileRead(filename.into(), e))?; let wikitext = String::from_utf8(data).map_err(|e| SiteError::Utf8(filename.into(), e))?; let mtime = get_mtime(filename)?; let meta = MetaBuilder::default() .name(name) .path(absolute) .mtime(mtime) .build(); Ok(Self::new(meta, wikitext)) } pub fn meta(&self) -> &PageMeta { &self.meta } pub fn wikitext(&self) -> &str { &self.wikitext } } #[derive(Debug)] pub struct UnprocessedPage { meta: PageMeta, snippets: Vec, } impl UnprocessedPage { pub fn new(meta: PageMeta, parser: &mut WikitextParser) -> Result { Ok(Self { meta, snippets: Self::snippets(parser)?, }) } pub fn meta(&self) -> &PageMeta { &self.meta } fn snippets(parser: &mut WikitextParser) -> Result, SiteError> { let mut snippets = vec![]; while let Some(snippet) = parser.parse()? { snippets.push(snippet); } Ok(snippets) } pub fn process(&self, site: &mut Site) -> Result { let mut meta = self.meta.clone(); let mut m = String::new(); for snippet in self.snippets.iter() { m.push_str(&snippet.process(site, &mut meta)?); } Ok(MarkdownPage::new(m, meta)) } } #[derive(Debug, Clone, Eq, PartialEq)] pub struct MarkdownPage { meta: PageMeta, markdown: String, } impl MarkdownPage { fn new(markdown: String, meta: PageMeta) -> Self { Self { markdown, meta } } pub fn markdown(&self) -> &str { &self.markdown } pub fn meta(&self) -> &PageMeta { &self.meta } pub fn body_to_html(&self) -> Result { let head = Element::new(ElementTag::Head); let body = parse(self.markdown())?; Ok(HtmlPage::new(head, body)) } pub fn to_html(&self) -> Result { let mut title = Element::new(ElementTag::Title); title.push_child(Content::Text(self.meta.title().into())); let mut head = Element::new(ElementTag::Head); head.push_child(Content::Elt(title)); let body = parse(self.markdown())?; trace!("MarkdownPage::to_html: head={:?}", head); Ok(HtmlPage::new(head, body)) } } #[derive(Debug, Clone, Eq, PartialEq)] pub struct PageMeta { name: String, title: Option, path: PathBuf, mtime: SystemTime, } impl PageMeta { fn new(name: String, title: Option, path: PathBuf, mtime: SystemTime) -> Self { trace!( "PageMeta: name={:?} title={:?} path={:?} mtime={:?}", name, title, path, mtime, ); Self { name, title, path, mtime, } } pub fn destination_filename(&self, destdir: &Path) -> PathBuf { join_subpath(destdir, &self.path).with_extension("html") } pub fn name(&self) -> &str { &self.name } pub fn set_title(&mut self, title: String) { trace!("PageMeta::set_title: title={:?}", title); self.title = Some(title); } pub fn title(&self) -> &str { if let Some(title) = &self.title { title } else { &self.name } } pub fn path(&self) -> &Path { &self.path } pub fn mtime(&self) -> SystemTime { self.mtime } } #[derive(Debug, Default)] pub struct MetaBuilder { name: String, title: Option, path: Option, mtime: Option, } impl MetaBuilder { pub fn build(self) -> PageMeta { PageMeta::new( self.name, self.title, self.path.expect("path set on MetaBuilder"), self.mtime.expect("mtime set on MetaBuilder"), ) } pub fn name(mut self, name: String) -> Self { self.name = name; self } pub fn title(mut self, title: String) -> Self { self.title = Some(title); self } pub fn path(mut self, path: PathBuf) -> Self { self.path = Some(path); self } pub fn mtime(mut self, mtime: SystemTime) -> Self { self.mtime = Some(mtime); self } }