From 875584a3d6a278cf6555f408d509929eb30703da Mon Sep 17 00:00:00 2001 From: Lars Wirzenius Date: Sun, 4 Dec 2022 11:21:29 +0200 Subject: refactor: create an error type for the html module This allows the HTML module to be more easily used in Subplot by copying the module into Subplot. Sponsored-by: author --- src/error.rs | 4 ++++ src/html.rs | 50 +++++++++++++++++++++++++++++++++++++------------- src/page.rs | 4 ++-- src/parser.rs | 8 +++++++- 4 files changed, 50 insertions(+), 16 deletions(-) diff --git a/src/error.rs b/src/error.rs index d58924e..23ef7bc 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,3 +1,4 @@ +use crate::html::HtmlError; use std::path::PathBuf; #[derive(Debug, thiserror::Error)] @@ -94,6 +95,9 @@ pub enum SiteError { #[error("toc directive arguments 'levels' could not be parsed as an integer: {0}")] LevelsParse(String, #[source] std::num::ParseIntError), + + #[error(transparent)] + HtmlError(#[from] HtmlError), } impl SiteError { diff --git a/src/html.rs b/src/html.rs index 6d4d009..d023ba4 100644 --- a/src/html.rs +++ b/src/html.rs @@ -1,12 +1,10 @@ -use crate::error::SiteError; -use crate::util::mkdir; use html_escape::{encode_double_quoted_attribute, encode_text}; use line_col::LineColLookup; -use log::trace; +use log::{debug, trace}; use pulldown_cmark::{Event, HeadingLevel, Options, Parser, Tag}; use std::fmt::Write as _; use std::io::Write; -use std::path::Path; +use std::path::{Path, PathBuf}; #[derive(Debug)] pub struct HtmlPage { @@ -36,32 +34,34 @@ impl HtmlPage { &self.body } - pub fn serialize(&self) -> Result { + pub fn serialize(&self) -> Result { let mut html = Element::new(ElementTag::Html); html.push_child(Content::Elt(self.head.clone())); html.push_child(Content::Elt(self.body.clone())); html.serialize() } - pub fn write(&self, filename: &Path) -> Result<(), SiteError> { + pub fn write(&self, filename: &Path) -> Result<(), HtmlError> { if let Some(parent) = filename.parent() { trace!("parent: {}", parent.display()); if !parent.exists() { - mkdir(parent)?; + debug!("creating directory {}", parent.display()); + std::fs::create_dir_all(parent) + .map_err(|e| HtmlError::CreateDir(parent.into(), e))?; } } trace!("writing HTML: {}", filename.display()); let mut f = std::fs::File::create(filename) - .map_err(|e| SiteError::CreateFile(filename.into(), e))?; + .map_err(|e| HtmlError::CreateFile(filename.into(), e))?; let html = self.serialize()?; f.write_all(html.as_bytes()) - .map_err(|e| SiteError::FileWrite(filename.into(), e))?; + .map_err(|e| HtmlError::FileWrite(filename.into(), e))?; Ok(()) } } -pub fn parse(markdown: &str) -> Result { +pub fn parse(markdown: &str) -> Result { let mut options = Options::empty(); options.insert(Options::ENABLE_HEADING_ATTRIBUTES); options.insert(Options::ENABLE_STRIKETHROUGH); @@ -144,7 +144,7 @@ pub fn parse(markdown: &str) -> Result { let s = as_plain_text(e.children()); trace!("paragraph text: {:?}", s); if s.starts_with(": ") || s.contains("\n: ") { - return Err(SiteError::DefinitionList(loc.line, loc.col)); + return Err(HtmlError::DefinitionList(loc.line, loc.col)); } stack.append_child(Content::Elt(e)); } @@ -258,10 +258,10 @@ impl Element { } } - pub fn serialize(&self) -> Result { + pub fn serialize(&self) -> Result { let mut buf = String::new(); self.serialize_to_buf_without_added_newlines(&mut buf) - .map_err(SiteError::Format)?; + .map_err(HtmlError::Format)?; Ok(buf) } @@ -487,3 +487,27 @@ impl Stack { self.append_child(Content::Elt(e)); } } + +#[derive(Debug, thiserror::Error)] +pub enum HtmlError { + /// Failed to create a directory. + #[error("failed to create directory {0}")] + CreateDir(PathBuf, #[source] std::io::Error), + + /// Failed to create a file. + #[error("failed to create file {0}")] + CreateFile(PathBuf, #[source] std::io::Error), + + /// Failed to write to a file. + #[error("failed to write to file {0}")] + FileWrite(PathBuf, #[source] std::io::Error), + + /// Input contains an attempt to use a definition list in + /// Markdown. + #[error("attempt to use definition lists in Markdown: line {0}, column {1}")] + DefinitionList(usize, usize), + + /// String formatting error. This is likely a programming error. + #[error("string formatting error: {0}")] + Format(#[source] std::fmt::Error), +} diff --git a/src/page.rs b/src/page.rs index 2e555c8..a8d60a5 100644 --- a/src/page.rs +++ b/src/page.rs @@ -151,7 +151,7 @@ impl MarkdownPage { pub fn body_to_html(&self) -> Result { let head = Element::new(ElementTag::Head); let body = parse(self.markdown()) - .map_err(|e| SiteError::PageProblem(self.meta.path().into(), Box::new(e)))?; + .map_err(|e| SiteError::PageProblem(self.meta.path().into(), Box::new(e.into())))?; Ok(HtmlPage::new(head, body)) } @@ -163,7 +163,7 @@ impl MarkdownPage { head.push_child(Content::Elt(title)); let body = parse(self.markdown()) - .map_err(|e| SiteError::PageProblem(self.meta.path().into(), Box::new(e)))?; + .map_err(|e| SiteError::PageProblem(self.meta.path().into(), Box::new(e.into())))?; trace!("MarkdownPage::to_html: head={:?}", head); Ok(HtmlPage::new(head, body)) diff --git a/src/parser.rs b/src/parser.rs index c81be74..59ba111 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -198,7 +198,13 @@ impl WikitextParser { self.drain(6); break; } - _ => return Err(SiteError::wikitext_syntax(line, col, &self.tokens[..std::cmp::min(5, self.tokens.len())])), + _ => { + return Err(SiteError::wikitext_syntax( + line, + col, + &self.tokens[..std::cmp::min(5, self.tokens.len())], + )) + } } } if let Some(title) = title { -- cgit v1.2.1