diff options
author | Lars Wirzenius <liw@liw.fi> | 2023-01-07 11:30:19 +0000 |
---|---|---|
committer | Lars Wirzenius <liw@liw.fi> | 2023-01-07 11:30:19 +0000 |
commit | 2ac4193a30c5ef6186666269c171b88fabd86143 (patch) | |
tree | 2b0abcee8c9b2b7bc89acc40d14cac6b3e8ec9de | |
parent | 01c7f770bef9bd67c00cd5cd0f23495c68994a79 (diff) | |
parent | 883aca2e45efd82aaee843f381df6303315d628e (diff) | |
download | riki-2ac4193a30c5ef6186666269c171b88fabd86143.tar.gz |
Merge branch 'liw/refactor-errors' into 'main'
refactor error handling
See merge request larswirzenius/riki!72
-rw-r--r-- | src/bin/riki.rs | 66 | ||||
-rw-r--r-- | src/directive/calendar.rs | 7 | ||||
-rw-r--r-- | src/directive/format.rs | 7 | ||||
-rw-r--r-- | src/directive/graph.rs | 7 | ||||
-rw-r--r-- | src/directive/img.rs | 9 | ||||
-rw-r--r-- | src/directive/inline.rs | 7 | ||||
-rw-r--r-- | src/directive/map.rs | 7 | ||||
-rw-r--r-- | src/directive/meta.rs | 5 | ||||
-rw-r--r-- | src/directive/mod.rs | 57 | ||||
-rw-r--r-- | src/directive/pagestats.rs | 7 | ||||
-rw-r--r-- | src/directive/shortcut.rs | 7 | ||||
-rw-r--r-- | src/directive/sidebar.rs | 7 | ||||
-rw-r--r-- | src/directive/table.rs | 5 | ||||
-rw-r--r-- | src/directive/tag.rs | 5 | ||||
-rw-r--r-- | src/directive/toc.rs | 7 | ||||
-rw-r--r-- | src/directive/traillink.rs | 7 | ||||
-rw-r--r-- | src/error.rs | 110 | ||||
-rw-r--r-- | src/git.rs | 25 | ||||
-rw-r--r-- | src/lib.rs | 1 | ||||
-rw-r--r-- | src/page.rs | 48 | ||||
-rw-r--r-- | src/parser.rs | 33 | ||||
-rw-r--r-- | src/site.rs | 35 | ||||
-rw-r--r-- | src/srcdir.rs | 11 | ||||
-rw-r--r-- | src/time.rs | 13 | ||||
-rw-r--r-- | src/util.rs | 55 | ||||
-rw-r--r-- | src/version.rs | 57 | ||||
-rw-r--r-- | src/wikitext.rs | 29 |
27 files changed, 363 insertions, 271 deletions
diff --git a/src/bin/riki.rs b/src/bin/riki.rs index 3c24148..c1f9e4b 100644 --- a/src/bin/riki.rs +++ b/src/bin/riki.rs @@ -1,13 +1,14 @@ use clap::{CommandFactory, FromArgMatches, Parser}; -use git_testament::{git_testament, render_testament, GitModification}; +use git_testament::git_testament; use log::{debug, error, info}; -use riki::error::SiteError; +use riki::error::RikiError; use riki::git::{git_dirty, git_whatchanged}; use riki::name::Name; use riki::pagespec; use riki::site::Site; use riki::time::parse_timestamp; use riki::util::{canonicalize, copy_file_from_source, get_mtime, mkdir, set_mtime}; +use riki::version::Version; use std::error::Error; use std::path::{Path, PathBuf}; @@ -28,19 +29,20 @@ fn main() { } } -fn real_main() -> Result<(), SiteError> { +fn real_main() -> Result<(), RikiError> { let env = env_logger::Env::new().filter(ENVLOG); env_logger::init_from_env(env); info!("riki starts"); - let version = version().map_err(SiteError::Format)?; - let long_version = long_version().map_err(SiteError::Format)?; + let version = Version::new(&VERSION); + let short = version.version()?; + let long = version.long_version()?; { let argparser = Args::command(); - let argparser = argparser.version(version.as_str()); - let argparser = argparser.long_version(long_version.as_str()); + let argparser = argparser.version(short.as_str()); + let argparser = argparser.long_version(long.as_str()); let args = argparser.get_matches(); let args = Args::from_arg_matches(&args).unwrap(); @@ -57,46 +59,6 @@ fn real_main() -> Result<(), SiteError> { Ok(()) } -fn version() -> Result<String, std::fmt::Error> { - use std::fmt::Write as _; - let mut ret = String::new(); - writeln!(ret, "{}", render_testament!(VERSION))?; - writeln!( - ret, - "{} {}", - env!("CARGO_PKG_NAME"), - env!("CARGO_PKG_VERSION") - )?; - Ok(ret) -} - -fn long_version() -> Result<String, std::fmt::Error> { - use std::fmt::Write as _; - let mut ret = String::new(); - writeln!(ret, "{}", render_testament!(VERSION))?; - writeln!(ret, "Crate version: {}", env!("CARGO_PKG_VERSION"))?; - if let Some(branch) = VERSION.branch_name { - writeln!(ret, "Built from branch: {}", branch)?; - } else { - writeln!(ret, "Branch information is missing.")?; - } - writeln!(ret, "Commit info: {}", VERSION.commit)?; - if VERSION.modifications.is_empty() { - writeln!(ret, "Working tree is clean")?; - } else { - use GitModification::*; - for fmod in VERSION.modifications { - match fmod { - Added(f) => writeln!(ret, "Added: {}", String::from_utf8_lossy(f))?, - Removed(f) => writeln!(ret, "Removed: {}", String::from_utf8_lossy(f))?, - Modified(f) => writeln!(ret, "Modified: {}", String::from_utf8_lossy(f))?, - Untracked(f) => writeln!(ret, "Untracked: {}", String::from_utf8_lossy(f))?, - } - } - } - Ok(ret) -} - /// Static site generator. /// /// Riki generates a static web site from markdown files. It mostly @@ -134,7 +96,7 @@ struct Build { } impl Build { - fn run(&self) -> Result<(), SiteError> { + fn run(&self) -> Result<(), RikiError> { let srcdir = canonicalize(&self.srcdir)?; debug!("srcdir={}", srcdir.display()); @@ -175,7 +137,7 @@ struct List { } impl List { - fn run(&self) -> Result<(), SiteError> { + fn run(&self) -> Result<(), RikiError> { let srcdir = canonicalize(&self.srcdir)?; let mut site = Site::new(&srcdir, &srcdir); site.scan()?; @@ -196,7 +158,7 @@ struct Timestamps { } impl Timestamps { - fn run(&self) -> Result<(), SiteError> { + fn run(&self) -> Result<(), RikiError> { let srcdir = canonicalize(&self.srcdir)?; let mut site = Site::new(&srcdir, &srcdir); site.scan()?; @@ -234,7 +196,7 @@ struct ParseDate { } impl ParseDate { - fn run(&self) -> Result<(), SiteError> { + fn run(&self) -> Result<(), RikiError> { for date in self.dates.iter() { println!("input: {:?}", date); let parsed = parse_timestamp(date)?; @@ -258,7 +220,7 @@ struct PageSpec { } impl PageSpec { - fn run(&self) -> Result<(), SiteError> { + fn run(&self) -> Result<(), RikiError> { let srcdir = canonicalize(&self.srcdir)?; let mut site = Site::new(&srcdir, &PathBuf::from("/tmp")); site.scan()?; diff --git a/src/directive/calendar.rs b/src/directive/calendar.rs index f29aaf9..233a742 100644 --- a/src/directive/calendar.rs +++ b/src/directive/calendar.rs @@ -1,5 +1,4 @@ -use crate::directive::{DirectiveImplementation, Processed}; -use crate::error::SiteError; +use crate::directive::{DirectiveError, DirectiveImplementation, Processed}; use crate::page::PageMeta; use crate::site::Site; use crate::wikitext::ParsedDirective; @@ -24,7 +23,7 @@ impl DirectiveImplementation for Calendar { Self::default() } - fn process(&self, _site: &Site, _meta: &mut PageMeta) -> Result<Processed, SiteError> { - Err(SiteError::UnimplementedDirective("calendar".into())) + fn process(&self, _site: &Site, _meta: &mut PageMeta) -> Result<Processed, DirectiveError> { + Err(DirectiveError::UnimplementedDirective("calendar".into())) } } diff --git a/src/directive/format.rs b/src/directive/format.rs index 66741ff..0d89361 100644 --- a/src/directive/format.rs +++ b/src/directive/format.rs @@ -1,5 +1,4 @@ -use crate::directive::{DirectiveImplementation, Processed}; -use crate::error::SiteError; +use crate::directive::{DirectiveError, DirectiveImplementation, Processed}; use crate::page::PageMeta; use crate::site::Site; use crate::wikitext::ParsedDirective; @@ -16,7 +15,7 @@ impl DirectiveImplementation for Format { Self::default() } - fn process(&self, _site: &Site, _meta: &mut PageMeta) -> Result<Processed, SiteError> { - Err(SiteError::UnimplementedDirective("format".into())) + fn process(&self, _site: &Site, _meta: &mut PageMeta) -> Result<Processed, DirectiveError> { + Err(DirectiveError::UnimplementedDirective("format".into())) } } diff --git a/src/directive/graph.rs b/src/directive/graph.rs index 56a6ac8..21f91aa 100644 --- a/src/directive/graph.rs +++ b/src/directive/graph.rs @@ -1,5 +1,4 @@ -use crate::directive::{DirectiveImplementation, Processed}; -use crate::error::SiteError; +use crate::directive::{DirectiveError, DirectiveImplementation, Processed}; use crate::page::PageMeta; use crate::site::Site; use crate::wikitext::ParsedDirective; @@ -16,7 +15,7 @@ impl DirectiveImplementation for Graph { Self::default() } - fn process(&self, _site: &Site, _meta: &mut PageMeta) -> Result<Processed, SiteError> { - Err(SiteError::UnimplementedDirective("graph".into())) + fn process(&self, _site: &Site, _meta: &mut PageMeta) -> Result<Processed, DirectiveError> { + Err(DirectiveError::UnimplementedDirective("graph".into())) } } diff --git a/src/directive/img.rs b/src/directive/img.rs index 8b74e5e..0b352fa 100644 --- a/src/directive/img.rs +++ b/src/directive/img.rs @@ -1,5 +1,4 @@ -use crate::directive::{DirectiveImplementation, Processed}; -use crate::error::SiteError; +use crate::directive::{DirectiveError, DirectiveImplementation, Processed}; use crate::page::PageMeta; use crate::site::Site; use crate::wikitext::ParsedDirective; @@ -82,13 +81,15 @@ impl DirectiveImplementation for Img { img } - fn process(&self, site: &Site, meta: &mut PageMeta) -> Result<Processed, SiteError> { + fn process(&self, site: &Site, meta: &mut PageMeta) -> Result<Processed, DirectiveError> { trace!( "verify image exists: {} on {}", self.src, meta.path().display() ); - let src = site.resolve(meta.path(), Path::new(&self.src))?; + let src = site + .resolve(meta.path(), Path::new(&self.src)) + .map_err(|e| DirectiveError::Site(Box::new(e)))?; trace!("img src={:?}", src.display()); let mut img = String::new(); diff --git a/src/directive/inline.rs b/src/directive/inline.rs index f4e18d7..973890b 100644 --- a/src/directive/inline.rs +++ b/src/directive/inline.rs @@ -1,5 +1,4 @@ -use crate::directive::{DirectiveImplementation, Processed}; -use crate::error::SiteError; +use crate::directive::{DirectiveError, DirectiveImplementation, Processed}; use crate::page::PageMeta; use crate::pagespec::PageSpec; use crate::site::Site; @@ -38,8 +37,8 @@ impl DirectiveImplementation for Inline { Inline::new(pages.to_string()) } - fn process(&self, site: &Site, meta: &mut PageMeta) -> Result<Processed, SiteError> { - let pagespec = PageSpec::new(meta.path(), &self.pages)?; + fn process(&self, site: &Site, meta: &mut PageMeta) -> Result<Processed, DirectiveError> { + let pagespec = PageSpec::new(meta.path(), &self.pages).map_err(DirectiveError::PageSpec)?; let matches: Vec<String> = site .markdown_pages() .iter() diff --git a/src/directive/map.rs b/src/directive/map.rs index e468aad..5d784ab 100644 --- a/src/directive/map.rs +++ b/src/directive/map.rs @@ -1,5 +1,4 @@ -use crate::directive::{DirectiveImplementation, Processed}; -use crate::error::SiteError; +use crate::directive::{DirectiveError, DirectiveImplementation, Processed}; use crate::page::PageMeta; use crate::site::Site; use crate::wikitext::ParsedDirective; @@ -16,7 +15,7 @@ impl DirectiveImplementation for Map { Self::default() } - fn process(&self, _site: &Site, _meta: &mut PageMeta) -> Result<Processed, SiteError> { - Err(SiteError::UnimplementedDirective("map".into())) + fn process(&self, _site: &Site, _meta: &mut PageMeta) -> Result<Processed, DirectiveError> { + Err(DirectiveError::UnimplementedDirective("map".into())) } } diff --git a/src/directive/meta.rs b/src/directive/meta.rs index 882288b..974a955 100644 --- a/src/directive/meta.rs +++ b/src/directive/meta.rs @@ -1,5 +1,4 @@ -use crate::directive::{DirectiveImplementation, Processed}; -use crate::error::SiteError; +use crate::directive::{DirectiveError, DirectiveImplementation, Processed}; use crate::page::PageMeta; use crate::site::Site; use crate::time::parse_timestamp; @@ -28,7 +27,7 @@ impl DirectiveImplementation for Meta { meta } - fn process(&self, _site: &Site, meta: &mut PageMeta) -> Result<Processed, SiteError> { + fn process(&self, _site: &Site, meta: &mut PageMeta) -> Result<Processed, DirectiveError> { if let Some(title) = &self.title { meta.set_title(title.into()); } diff --git a/src/directive/mod.rs b/src/directive/mod.rs index ae2dc09..4efde55 100644 --- a/src/directive/mod.rs +++ b/src/directive/mod.rs @@ -1,4 +1,3 @@ -use crate::error::SiteError; use crate::page::PageMeta; use crate::site::Site; use crate::wikitext::ParsedDirective; @@ -6,6 +5,36 @@ use crate::wikitext::ParsedDirective; use log::{debug, trace}; use std::collections::HashSet; +#[derive(Debug, thiserror::Error)] +pub enum DirectiveError { + #[error("unknown directive {0}")] + UnknownDirective(String), + + #[error("directive {0} is missing required argument {1}")] + DirectiveMissingArg(String, String), + + #[error("directive {0} has unknown argument {1}")] + DirectiveUnknownArg(String, String), + + #[error("directive {0} does not allow unnamed arguments")] + UnknownArgsNotAllowed(String), + + #[error("directive isn't implemented yet: {0}")] + UnimplementedDirective(String), + + #[error("toc directive arguments 'levels' could not be parsed as an integer: {0}")] + LevelsParse(String, #[source] std::num::ParseIntError), + + #[error(transparent)] + PageSpec(#[from] crate::pagespec::PageSpecError), + + #[error(transparent)] + Time(#[from] crate::time::TimeError), + + #[error(transparent)] + Site(#[from] Box<crate::site::SiteError>), +} + pub enum Processed { Markdown(String), Toc(usize), @@ -17,9 +46,9 @@ pub trait DirectiveImplementation { const ALLOW_ANY_UNNAMED: bool; fn from_parsed(p: &ParsedDirective) -> Self; - fn process(&self, site: &Site, meta: &mut PageMeta) -> Result<Processed, SiteError>; + fn process(&self, site: &Site, meta: &mut PageMeta) -> Result<Processed, DirectiveError>; - fn prepare(&self, _site: &mut Site) -> Result<(), SiteError> { + fn prepare(&self, _site: &mut Site) -> Result<(), DirectiveError> { Ok(()) } } @@ -49,7 +78,7 @@ pub enum Directive { } impl TryFrom<ParsedDirective> for Directive { - type Error = SiteError; + type Error = DirectiveError; fn try_from(p: ParsedDirective) -> Result<Self, Self::Error> { Self::try_from(&p) @@ -57,7 +86,7 @@ impl TryFrom<ParsedDirective> for Directive { } impl TryFrom<&ParsedDirective> for Directive { - type Error = SiteError; + type Error = DirectiveError; fn try_from(p: &ParsedDirective) -> Result<Self, Self::Error> { let d = match p.name() { @@ -173,7 +202,7 @@ impl TryFrom<&ParsedDirective> for Directive { )?; Directive::TrailLink(TrailLink::from_parsed(p)) } - _ => return Err(SiteError::UnknownDirective(p.name().into())), + _ => return Err(DirectiveError::UnknownDirective(p.name().into())), }; Ok(d) } @@ -185,11 +214,11 @@ impl Directive { required: &[&str], allowed: &[&str], allow_any_unnamed: bool, - ) -> Result<(), SiteError> { + ) -> Result<(), DirectiveError> { let args = p.args(); for arg in required.iter() { if !args.contains_key(arg) { - return Err(SiteError::DirectiveMissingArg( + return Err(DirectiveError::DirectiveMissingArg( p.name().into(), arg.to_string(), )); @@ -201,11 +230,11 @@ impl Directive { for (arg, value) in p.args().iter() { if value.is_empty() { if !allow_any_unnamed { - return Err(SiteError::UnknownArgsNotAllowed(p.name().into())); + return Err(DirectiveError::UnknownArgsNotAllowed(p.name().into())); } } else if !allowed.contains(*arg) { debug!("parsed directive {:?}", p); - return Err(SiteError::DirectiveUnknownArg( + return Err(DirectiveError::DirectiveUnknownArg( p.name().into(), arg.to_string(), )); @@ -214,7 +243,7 @@ impl Directive { Ok(()) } - pub fn prepare(&self, site: &mut Site) -> Result<(), SiteError> { + pub fn prepare(&self, site: &mut Site) -> Result<(), DirectiveError> { trace!("prepare directive {:?}", self); if let Self::Shortcut(x) = self { x.prepare(site)?; @@ -222,7 +251,11 @@ impl Directive { Ok(()) } - pub fn process(&self, site: &mut Site, meta: &mut PageMeta) -> Result<Processed, SiteError> { + pub fn process( + &self, + site: &mut Site, + meta: &mut PageMeta, + ) -> Result<Processed, DirectiveError> { match self { Self::Simple | Self::UnnamedArg diff --git a/src/directive/pagestats.rs b/src/directive/pagestats.rs index 11edc29..eb3b595 100644 --- a/src/directive/pagestats.rs +++ b/src/directive/pagestats.rs @@ -1,5 +1,4 @@ -use crate::directive::{DirectiveImplementation, Processed}; -use crate::error::SiteError; +use crate::directive::{DirectiveError, DirectiveImplementation, Processed}; use crate::page::PageMeta; use crate::site::Site; use crate::wikitext::ParsedDirective; @@ -16,7 +15,7 @@ impl DirectiveImplementation for PageStats { Self::default() } - fn process(&self, _site: &Site, _meta: &mut PageMeta) -> Result<Processed, SiteError> { - Err(SiteError::UnimplementedDirective("pagestat".into())) + fn process(&self, _site: &Site, _meta: &mut PageMeta) -> Result<Processed, DirectiveError> { + Err(DirectiveError::UnimplementedDirective("pagestat".into())) } } diff --git a/src/directive/shortcut.rs b/src/directive/shortcut.rs index 48bc482..3bbd6a6 100644 --- a/src/directive/shortcut.rs +++ b/src/directive/shortcut.rs @@ -1,5 +1,4 @@ -use crate::directive::{DirectiveImplementation, Processed}; -use crate::error::SiteError; +use crate::directive::{DirectiveError, DirectiveImplementation, Processed}; use crate::page::PageMeta; use crate::site::{Shortcut as S, Site}; use crate::wikitext::ParsedDirective; @@ -24,13 +23,13 @@ impl DirectiveImplementation for Shortcut { Self::new(S::new(name, desc, url)) } - fn prepare(&self, site: &mut Site) -> Result<(), SiteError> { + fn prepare(&self, site: &mut Site) -> Result<(), DirectiveError> { trace!("shortcut: prepare"); site.add_shortcut(self.shortcut.clone()); Ok(()) } - fn process(&self, _site: &Site, _meta: &mut PageMeta) -> Result<Processed, SiteError> { + fn process(&self, _site: &Site, _meta: &mut PageMeta) -> Result<Processed, DirectiveError> { Ok(Processed::Markdown("".into())) } } diff --git a/src/directive/sidebar.rs b/src/directive/sidebar.rs index 1c25144..630ef1f 100644 --- a/src/directive/sidebar.rs +++ b/src/directive/sidebar.rs @@ -1,5 +1,4 @@ -use crate::directive::{DirectiveImplementation, Processed}; -use crate::error::SiteError; +use crate::directive::{DirectiveError, DirectiveImplementation, Processed}; use crate::page::PageMeta; use crate::site::Site; use crate::wikitext::ParsedDirective; @@ -16,7 +15,7 @@ impl DirectiveImplementation for Sidebar { Self::default() } - fn process(&self, _site: &Site, _meta: &mut PageMeta) -> Result<Processed, SiteError> { - Err(SiteError::UnimplementedDirective("sidebar".into())) + fn process(&self, _site: &Site, _meta: &mut PageMeta) -> Result<Processed, DirectiveError> { + Err(DirectiveError::UnimplementedDirective("sidebar".into())) } } diff --git a/src/directive/table.rs b/src/directive/table.rs index 2bbfbc8..4f1b9ca 100644 --- a/src/directive/table.rs +++ b/src/directive/table.rs @@ -1,5 +1,4 @@ -use crate::directive::{DirectiveImplementation, Processed}; -use crate::error::SiteError; +use crate::directive::{DirectiveError, DirectiveImplementation, Processed}; use crate::page::PageMeta; use crate::site::Site; use crate::wikitext::ParsedDirective; @@ -19,7 +18,7 @@ impl DirectiveImplementation for Table { Self::new(p.args().get("data").unwrap().to_string()) } - fn process(&self, _site: &Site, _meta: &mut PageMeta) -> Result<Processed, SiteError> { + fn process(&self, _site: &Site, _meta: &mut PageMeta) -> Result<Processed, DirectiveError> { let mut table = String::new(); let mut lines = self.data.trim().lines(); if let Some(first) = lines.next() { diff --git a/src/directive/tag.rs b/src/directive/tag.rs index 6d4377d..6439199 100644 --- a/src/directive/tag.rs +++ b/src/directive/tag.rs @@ -1,5 +1,4 @@ -use crate::directive::{DirectiveImplementation, Processed}; -use crate::error::SiteError; +use crate::directive::{DirectiveError, DirectiveImplementation, Processed}; use crate::page::PageMeta; use crate::site::Site; use crate::wikitext::ParsedDirective; @@ -19,7 +18,7 @@ impl DirectiveImplementation for Tag { Tag::new(tags) } - fn process(&self, _site: &Site, _meta: &mut PageMeta) -> Result<Processed, SiteError> { + fn process(&self, _site: &Site, _meta: &mut PageMeta) -> Result<Processed, DirectiveError> { Ok(Processed::Markdown("".into())) } } diff --git a/src/directive/toc.rs b/src/directive/toc.rs index f027931..fa115b6 100644 --- a/src/directive/toc.rs +++ b/src/directive/toc.rs @@ -1,5 +1,4 @@ -use crate::directive::{DirectiveImplementation, Processed}; -use crate::error::SiteError; +use crate::directive::{DirectiveError, DirectiveImplementation, Processed}; use crate::html::{Content, Element, ElementTag}; use crate::page::PageMeta; use crate::site::Site; @@ -21,11 +20,11 @@ impl DirectiveImplementation for Toc { Self::new(levels.to_string()) } - fn process(&self, _site: &Site, _meta: &mut PageMeta) -> Result<Processed, SiteError> { + fn process(&self, _site: &Site, _meta: &mut PageMeta) -> Result<Processed, DirectiveError> { let levels: usize = self .levels .parse() - .map_err(|e| SiteError::LevelsParse(self.levels.clone(), e))?; + .map_err(|e| DirectiveError::LevelsParse(self.levels.clone(), e))?; Ok(Processed::Toc(levels)) } } diff --git a/src/directive/traillink.rs b/src/directive/traillink.rs index b3e8cb7..9f880dc 100644 --- a/src/directive/traillink.rs +++ b/src/directive/traillink.rs @@ -1,5 +1,4 @@ -use crate::directive::{DirectiveImplementation, Processed}; -use crate::error::SiteError; +use crate::directive::{DirectiveError, DirectiveImplementation, Processed}; use crate::page::PageMeta; use crate::site::Site; use crate::wikitext::ParsedDirective; @@ -16,7 +15,7 @@ impl DirectiveImplementation for TrailLink { Self::default() } - fn process(&self, _site: &Site, _meta: &mut PageMeta) -> Result<Processed, SiteError> { - Err(SiteError::UnimplementedDirective("traillink".into())) + fn process(&self, _site: &Site, _meta: &mut PageMeta) -> Result<Processed, DirectiveError> { + Err(DirectiveError::UnimplementedDirective("traillink".into())) } } diff --git a/src/error.rs b/src/error.rs index 23ef7bc..d38c1e9 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,108 +1,40 @@ use crate::html::HtmlError; -use std::path::PathBuf; #[derive(Debug, thiserror::Error)] -pub enum SiteError { - #[error("source directory doesn't exist: {0}")] - SourceDirMissing(PathBuf), - - #[error("failed to list files in source directory: {0}")] - WalkDir(PathBuf, #[source] walkdir::Error), - - #[error("could not read file: {0}")] - FileRead(PathBuf, #[source] std::io::Error), - - #[error("could not write file: {0}")] - FileWrite(PathBuf, #[source] std::io::Error), - - #[error("string formatting error: {0}")] - Format(#[source] std::fmt::Error), - - #[error("could not convert input text from {0} to UTF-8")] - Utf8(PathBuf, #[source] std::string::FromUtf8Error), - - #[error("failed to canonicalize path {0}")] - Canonicalize(PathBuf, #[source] std::io::Error), - - #[error("failed to compute relative path for {0} against {1}")] - Relative(PathBuf, PathBuf), - - #[error("failed to create {0}")] - CreateFile(PathBuf, #[source] std::io::Error), - - #[error("failed to copy {0} to {1}")] - CopyFile(PathBuf, PathBuf, #[source] std::io::Error), - - #[error("failed to create directory {0}")] - CreateDir(PathBuf, #[source] std::io::Error), - - #[error("unknown directive {0}")] - UnknownDirective(String), - - #[error("directive {0} does not allow unnamed arguments")] - UnknownArgsNotAllowed(String), - - #[error("directive {0} has more than one unnamed argument")] - TooManyUnnamedArgs(String), - - #[error("directive {0} is missing required argument {1}")] - DirectiveMissingArg(String, String), - - #[error("directive {0} has unknown argument {1}")] - DirectiveUnknownArg(String, String), - - #[error("link to missing page {1} on {0}")] - PageMissing(PathBuf, PathBuf), - - #[error("attempt to use definition lists in Markdown: line {0}, column {1}")] - DefinitionList(usize, usize), - - #[error("failed to get file metadata: {0}")] - FileMetadata(PathBuf, #[source] std::io::Error), - - #[error("failed to get file modification time: {0}")] - FileMtime(PathBuf, #[source] std::io::Error), +pub enum RikiError { + #[error(transparent)] + WalkDir(#[from] crate::srcdir::SourceDirError), - #[error("failed to set modification time for file {0}")] - Utimensat(PathBuf, #[source] std::io::Error), + #[error(transparent)] + Util(#[from] crate::util::UtilError), - #[error("failed to convert time to Unix time")] - UnixTime(#[source] std::time::SystemTimeError), + #[error(transparent)] + Git(#[from] crate::git::GitError), - #[error("failed to parse Unix timetamp: {0}")] - ParseUnixTimestamp(String, #[source] std::num::ParseIntError), + #[error(transparent)] + Page(#[from] crate::page::PageError), - #[error("faileed to invoked git with subcommand {0} in {1}")] - GitInvoke(String, PathBuf, #[source] std::io::Error), + #[error(transparent)] + Directive(#[from] crate::directive::DirectiveError), - #[error("git {0} in in {1}:\n{2}")] - GitError(String, PathBuf, String), + #[error(transparent)] + Parser(#[from] crate::parser::ParserError), - #[error("failed to parse date: {0:?}")] - UnknownTimestamp(String), + #[error(transparent)] + Time(#[from] crate::time::TimeError), - #[error("failed to process page {0}")] - PageProblem(PathBuf, #[source] Box<Self>), + #[error(transparent)] + Version(#[from] crate::version::VersionError), #[error(transparent)] PageSpec(#[from] crate::pagespec::PageSpecError), - #[error("failed to parse wikitext, line {0}, column {1}: {2:?}")] - WikitextSyntax(usize, usize, Vec<crate::token::TokenKind>), - - #[error("directive isn't implemented yet: {0}")] - UnimplementedDirective(String), - - #[error("toc directive arguments 'levels' could not be parsed as an integer: {0}")] - LevelsParse(String, #[source] std::num::ParseIntError), + #[error(transparent)] + Wikitext(#[from] crate::wikitext::WikitextError), #[error(transparent)] HtmlError(#[from] HtmlError), -} -impl SiteError { - pub fn wikitext_syntax(line: usize, col: usize, tokens: &[crate::token::TokenKind]) -> Self { - let tokens = tokens.to_vec(); - Self::WikitextSyntax(line, col, tokens) - } + #[error(transparent)] + Site(#[from] crate::site::SiteError), } @@ -1,26 +1,37 @@ -use crate::error::SiteError; use regex::Regex; use std::collections::HashMap; use std::path::{Path, PathBuf}; use std::process::Command; use std::time::{Duration, SystemTime, UNIX_EPOCH}; -pub fn git(args: &[&str], cwd: &Path) -> Result<String, SiteError> { +#[derive(Debug, thiserror::Error)] +pub enum GitError { + #[error("failed to parse Unix timetamp: {0}")] + ParseUnixTimestamp(String, #[source] std::num::ParseIntError), + + #[error("faileed to invoked git with subcommand {0} in {1}")] + GitInvoke(String, PathBuf, #[source] std::io::Error), + + #[error("git {0} in in {1}:\n{2}")] + GitError(String, PathBuf, String), +} + +pub fn git(args: &[&str], cwd: &Path) -> Result<String, GitError> { assert!(!args.is_empty()); let output = Command::new("git") .args(args) .current_dir(cwd) .output() - .map_err(|e| SiteError::GitInvoke(args[0].into(), cwd.into(), e))?; + .map_err(|e| GitError::GitInvoke(args[0].into(), cwd.into(), e))?; if output.status.success() { Ok(String::from_utf8_lossy(&output.stdout).into()) } else { let stderr = String::from_utf8_lossy(&output.stderr).into(); - Err(SiteError::GitError(args[0].into(), cwd.into(), stderr)) + Err(GitError::GitError(args[0].into(), cwd.into(), stderr)) } } -pub fn git_whatchanged(cwd: &Path) -> Result<HashMap<PathBuf, SystemTime>, SiteError> { +pub fn git_whatchanged(cwd: &Path) -> Result<HashMap<PathBuf, SystemTime>, GitError> { let mut files = HashMap::new(); if cwd.join(".git").is_dir() { let output = git(&["whatchanged", "--pretty=format:%ad", "--date=unix"], cwd)?; @@ -34,7 +45,7 @@ pub fn git_whatchanged(cwd: &Path) -> Result<HashMap<PathBuf, SystemTime>, SiteE let secs = caps.name("secs").unwrap().as_str(); let timestamp = secs .parse::<u64>() - .map_err(|e| SiteError::ParseUnixTimestamp(secs.into(), e))?; + .map_err(|e| GitError::ParseUnixTimestamp(secs.into(), e))?; mtime = Some(UNIX_EPOCH + Duration::new(timestamp, 0)); } else if let Some(caps) = filepat.captures(line) { let flag = caps.name("flag").unwrap().as_str(); @@ -51,7 +62,7 @@ pub fn git_whatchanged(cwd: &Path) -> Result<HashMap<PathBuf, SystemTime>, SiteE Ok(files) } -pub fn git_dirty(cwd: &Path) -> Result<Vec<PathBuf>, SiteError> { +pub fn git_dirty(cwd: &Path) -> Result<Vec<PathBuf>, GitError> { let mut dirty = vec![]; if cwd.join(".git").is_dir() { let output = git(&["status", "--short"], cwd)?; @@ -21,3 +21,4 @@ pub mod time; pub mod token; pub mod util; pub mod wikitext; +pub mod version; diff --git a/src/page.rs b/src/page.rs index a8d60a5..d84721c 100644 --- a/src/page.rs +++ b/src/page.rs @@ -1,5 +1,4 @@ use crate::directive::{Processed, Toc}; -use crate::error::SiteError; use crate::html::{parse, Content, Element, ElementTag, HtmlPage}; use crate::name::Name; use crate::parser::WikitextParser; @@ -10,6 +9,27 @@ use log::{info, trace}; use std::path::{Path, PathBuf}; use std::time::SystemTime; +#[derive(Debug, thiserror::Error)] +pub enum PageError { + #[error("could not read file: {0}")] + FileRead(PathBuf, #[source] std::io::Error), + + #[error("could not convert input text from {0} to UTF-8")] + Utf8(PathBuf, #[source] std::string::FromUtf8Error), + + #[error(transparent)] + Util(#[from] crate::util::UtilError), + + #[error(transparent)] + Wikitext(#[from] crate::wikitext::WikitextError), + + #[error(transparent)] + Html(#[from] crate::html::HtmlError), + + #[error(transparent)] + Parser(#[from] crate::parser::ParserError), +} + pub struct Page { meta: PageMeta, unprocessed: UnprocessedPage, @@ -24,7 +44,7 @@ impl Page { &self.meta } - pub fn markdown(&self, site: &mut Site) -> Result<MarkdownPage, SiteError> { + pub fn markdown(&self, site: &mut Site) -> Result<MarkdownPage, PageError> { self.unprocessed.process(site) } } @@ -40,12 +60,12 @@ impl WikitextPage { Self { meta, wikitext } } - pub fn read(name: &Name) -> Result<Self, SiteError> { + pub fn read(name: &Name) -> Result<Self, PageError> { info!("input file: {}", name); let src = name.source_path(); - let data = std::fs::read(src).map_err(|e| SiteError::FileRead(src.into(), e))?; - let wikitext = String::from_utf8(data).map_err(|e| SiteError::Utf8(src.into(), e))?; + let data = std::fs::read(src).map_err(|e| PageError::FileRead(src.into(), e))?; + let wikitext = String::from_utf8(data).map_err(|e| PageError::Utf8(src.into(), e))?; let mtime = get_mtime(src)?; let meta = MetaBuilder::default() @@ -75,7 +95,7 @@ pub struct UnprocessedPage { } impl UnprocessedPage { - pub fn new(meta: PageMeta, parser: &mut WikitextParser) -> Result<Self, SiteError> { + pub fn new(meta: PageMeta, parser: &mut WikitextParser) -> Result<Self, PageError> { Ok(Self { meta, snippets: Self::snippets(parser)?, @@ -86,7 +106,7 @@ impl UnprocessedPage { &self.meta } - fn snippets(parser: &mut WikitextParser) -> Result<Vec<Snippet>, SiteError> { + fn snippets(parser: &mut WikitextParser) -> Result<Vec<Snippet>, PageError> { let mut snippets = vec![]; while let Some(snippet) = parser.parse()? { snippets.push(snippet); @@ -94,7 +114,7 @@ impl UnprocessedPage { Ok(snippets) } - pub fn prepare(&self, site: &mut Site) -> Result<(), SiteError> { + pub fn prepare(&self, site: &mut Site) -> Result<(), PageError> { trace!("UnprocessedPage: preparing snippets"); for snippet in self.snippets.iter() { snippet.prepare(site)?; @@ -102,7 +122,7 @@ impl UnprocessedPage { Ok(()) } - pub fn process(&self, site: &mut Site) -> Result<MarkdownPage, SiteError> { + pub fn process(&self, site: &mut Site) -> Result<MarkdownPage, PageError> { let mut meta = self.meta.clone(); let mut processed = vec![]; trace!("UnprocessedPage: processing snippets"); @@ -148,22 +168,20 @@ impl MarkdownPage { &self.meta } - pub fn body_to_html(&self) -> Result<HtmlPage, SiteError> { + pub fn body_to_html(&self) -> Result<HtmlPage, PageError> { let head = Element::new(ElementTag::Head); - let body = parse(self.markdown()) - .map_err(|e| SiteError::PageProblem(self.meta.path().into(), Box::new(e.into())))?; + let body = parse(self.markdown())?; Ok(HtmlPage::new(head, body)) } - pub fn to_html(&self) -> Result<HtmlPage, SiteError> { + pub fn to_html(&self) -> Result<HtmlPage, PageError> { 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()) - .map_err(|e| SiteError::PageProblem(self.meta.path().into(), Box::new(e.into())))?; + let body = parse(self.markdown())?; trace!("MarkdownPage::to_html: head={:?}", head); Ok(HtmlPage::new(head, body)) diff --git a/src/parser.rs b/src/parser.rs index 59ba111..5e8238e 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,10 +1,25 @@ -use crate::error::SiteError; use crate::token::{TokenKind, TokenParser, TokenPatterns}; -use crate::wikitext::{ParsedDirective, Snippet, WikiLink}; +use crate::wikitext::{ParsedDirective, Snippet, WikiLink, WikitextError}; use line_col::LineColLookup; use log::{debug, trace}; use std::collections::HashMap; +#[derive(Debug, thiserror::Error)] +pub enum ParserError { + #[error("failed to parse wikitext, line {0}, column {1}: {2:?}")] + WikitextSyntax(usize, usize, Vec<crate::token::TokenKind>), + + #[error(transparent)] + Wikitext(#[from] WikitextError), +} + +impl ParserError { + pub fn wikitext_syntax(line: usize, col: usize, tokens: &[crate::token::TokenKind]) -> Self { + let tokens = tokens.to_vec(); + crate::parser::ParserError::WikitextSyntax(line, col, tokens) + } +} + #[derive(Debug)] pub struct WikitextParser { tokens: Vec<TokenKind>, @@ -44,7 +59,7 @@ impl WikitextParser { self.tokens.is_empty() } - pub fn parse(&mut self) -> Result<Option<Snippet>, SiteError> { + pub fn parse(&mut self) -> Result<Option<Snippet>, ParserError> { if self.is_empty() { return Ok(None); } @@ -110,7 +125,9 @@ impl WikitextParser { self.drain(3); break; } - _ => return Err(SiteError::wikitext_syntax(line, col, &self.tokens[..5])), + _ => { + return Err(ParserError::wikitext_syntax(line, col, &self.tokens[..5])) + } } } if target.is_none() { @@ -163,7 +180,9 @@ impl WikitextParser { args.insert(value.to_string(), "".to_string()); self.drain(1); } - _ => return Err(SiteError::wikitext_syntax(line, col, &self.tokens[..5])), + _ => { + return Err(ParserError::wikitext_syntax(line, col, &self.tokens[..5])) + } } } Snippet::Directive(ParsedDirective::new(&name, args)?) @@ -199,7 +218,7 @@ impl WikitextParser { break; } _ => { - return Err(SiteError::wikitext_syntax( + return Err(ParserError::wikitext_syntax( line, col, &self.tokens[..std::cmp::min(5, self.tokens.len())], @@ -279,7 +298,7 @@ impl WikitextParser { self.drain(1); Snippet::Markdown("]]".into()) } - _ => return Err(SiteError::wikitext_syntax(line, col, &self.tokens[..5])), + _ => return Err(ParserError::wikitext_syntax(line, col, &self.tokens[..5])), }; Ok(Some(snippet)) } diff --git a/src/site.rs b/src/site.rs index 739cffb..4d2ce29 100644 --- a/src/site.rs +++ b/src/site.rs @@ -1,8 +1,7 @@ -use crate::error::SiteError; use crate::git::git_whatchanged; use crate::name::{Name, NameBuilder, Names}; -use crate::page::{MarkdownPage, Page, UnprocessedPage, WikitextPage}; -use crate::parser::WikitextParser; +use crate::page::{MarkdownPage, Page, PageError, UnprocessedPage, WikitextPage}; +use crate::parser::{ParserError, WikitextParser}; use crate::srcdir::{PathFilter, SourceDir}; use crate::token::TokenPatterns; use crate::util::make_relative_link; @@ -11,6 +10,36 @@ use std::collections::HashMap; use std::path::{Path, PathBuf}; use std::time::UNIX_EPOCH; +#[derive(Debug, thiserror::Error)] +pub enum SiteError { + #[error("link to missing page {1} on {0}")] + PageMissing(PathBuf, PathBuf), + + #[error(transparent)] + Page(#[from] Box<PageError>), + + #[error(transparent)] + Git(#[from] crate::git::GitError), + + #[error(transparent)] + Parser(#[from] Box<ParserError>), + + #[error(transparent)] + WalkDir(#[from] crate::srcdir::SourceDirError), +} + +impl From<PageError> for SiteError { + fn from(e: PageError) -> Self { + Self::Page(Box::new(e)) + } +} + +impl From<ParserError> for SiteError { + fn from(e: ParserError) -> Self { + Self::Parser(Box::new(e)) + } +} + pub struct Site { patterns: TokenPatterns, shortcuts: HashMap<String, Shortcut>, diff --git a/src/srcdir.rs b/src/srcdir.rs index 66d2a04..b1a9317 100644 --- a/src/srcdir.rs +++ b/src/srcdir.rs @@ -1,8 +1,13 @@ -use crate::error::SiteError; use log::trace; use std::path::{Path, PathBuf}; use walkdir::WalkDir; +#[derive(Debug, thiserror::Error)] +pub enum SourceDirError { + #[error("failed to list files in source directory: {0}")] + WalkDir(PathBuf, #[source] walkdir::Error), +} + pub struct SourceDir { path: PathBuf, files: Vec<PathBuf>, @@ -31,13 +36,13 @@ impl SourceDir { self.files.push(path.into()); } - pub fn scan(&mut self) -> Result<(), SiteError> { + pub fn scan(&mut self) -> Result<(), SourceDirError> { if self.artificial { trace!("SourceDir::scan: artificial mode, not actually scanning"); } else { trace!("SourceDir::scan: find files in {}", self.path.display()); for e in WalkDir::new(&self.path) { - let e = e.map_err(|err| SiteError::WalkDir(self.path.clone(), err))?; + let e = e.map_err(|err| SourceDirError::WalkDir(self.path.clone(), err))?; let path = e.path(); trace!("SourceDir::scan: found {}", path.display()); self.insert(path); diff --git a/src/time.rs b/src/time.rs index dce3ecc..a9f3502 100644 --- a/src/time.rs +++ b/src/time.rs @@ -1,4 +1,3 @@ -use crate::error::SiteError; use log::trace; use std::time::{Duration, SystemTime}; use time::{ @@ -9,7 +8,13 @@ use time::{ OffsetDateTime, PrimitiveDateTime, }; -pub fn parse_timestamp(timestamp: &str) -> Result<SystemTime, SiteError> { +#[derive(Debug, thiserror::Error)] +pub enum TimeError { + #[error("failed to parse date: {0:?}")] + UnknownTimestamp(String), +} + +pub fn parse_timestamp(timestamp: &str) -> Result<SystemTime, TimeError> { trace!("parsing timestamp {:?}", timestamp); let odt = parse(timestamp)?; let unix = odt.unix_timestamp(); @@ -24,7 +29,7 @@ fn system_time(unix: i64) -> SystemTime { SystemTime::UNIX_EPOCH.checked_add(offset).unwrap() } -fn parse(timestamp: &str) -> Result<OffsetDateTime, SiteError> { +fn parse(timestamp: &str) -> Result<OffsetDateTime, TimeError> { const SIMPLIFIED_ISO9601: &[FormatItem<'static>] = format_description!("[year]-[month]-[day] [hour]:[minute]:[second]"); const SIMPLIFIED_ISO9601_MIN: &[FormatItem<'static>] = @@ -51,7 +56,7 @@ fn parse(timestamp: &str) -> Result<OffsetDateTime, SiteError> { } else if let Ok(t) = parse_one_time_format(timestamp, "RFC2822", &Rfc2822) { Ok(t) } else { - Err(SiteError::UnknownTimestamp(timestamp.into())) + Err(TimeError::UnknownTimestamp(timestamp.into())) } } diff --git a/src/util.rs b/src/util.rs index bf78404..eac71f1 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,4 +1,3 @@ -use crate::error::SiteError; use libc::{timespec, utimensat, AT_FDCWD, AT_SYMLINK_NOFOLLOW}; use log::{debug, error, trace}; use std::ffi::CString; @@ -6,34 +5,58 @@ use std::os::unix::ffi::OsStrExt; use std::path::{Component, Path, PathBuf}; use std::time::{SystemTime, UNIX_EPOCH}; -pub fn canonicalize(path: &Path) -> Result<PathBuf, SiteError> { +#[derive(Debug, thiserror::Error)] +pub enum UtilError { + #[error("failed to canonicalize path {0}")] + Canonicalize(PathBuf, #[source] std::io::Error), + + #[error("failed to convert time to Unix time")] + UnixTime(#[source] std::time::SystemTimeError), + + #[error("failed to create directory {0}")] + CreateDir(PathBuf, #[source] std::io::Error), + + #[error("failed to copy {0} to {1}")] + CopyFile(PathBuf, PathBuf, #[source] std::io::Error), + + #[error("failed to get file metadata: {0}")] + FileMetadata(PathBuf, #[source] std::io::Error), + + #[error("failed to get file modification time: {0}")] + FileMtime(PathBuf, #[source] std::io::Error), + + #[error("failed to set modification time for file {0}")] + Utimensat(PathBuf, #[source] std::io::Error), +} + +pub fn canonicalize(path: &Path) -> Result<PathBuf, UtilError> { path.canonicalize() - .map_err(|e| SiteError::Canonicalize(path.into(), e)) + .map_err(|e| UtilError::Canonicalize(path.into(), e)) } -pub fn mkdir(path: &Path) -> Result<(), SiteError> { +pub fn mkdir(path: &Path) -> Result<(), UtilError> { debug!("creating directory {}", path.display()); - std::fs::create_dir_all(path).map_err(|e| SiteError::CreateDir(path.into(), e))?; + std::fs::create_dir_all(path).map_err(|e| UtilError::CreateDir(path.into(), e))?; Ok(()) } -pub fn copy(src: &Path, dest: &Path) -> Result<(), SiteError> { +pub fn copy(src: &Path, dest: &Path) -> Result<(), UtilError> { trace!("copying: {} -> {}", src.display(), dest.display()); - std::fs::copy(src, dest).map_err(|e| SiteError::CopyFile(src.into(), dest.into(), e))?; + std::fs::copy(src, dest).map_err(|e| UtilError::CopyFile(src.into(), dest.into(), e))?; let mtime = get_mtime(src)?; set_mtime(dest, mtime)?; Ok(()) } -pub fn get_mtime(src: &Path) -> Result<SystemTime, SiteError> { - let metadata = std::fs::metadata(src).map_err(|e| SiteError::FileMetadata(src.into(), e))?; +pub fn get_mtime(src: &Path) -> Result<SystemTime, UtilError> { + let metadata = std::fs::metadata(src).map_err(|e| UtilError::FileMetadata(src.into(), e))?; let mtime = metadata .modified() - .map_err(|e| SiteError::FileMtime(src.into(), e))?; + .map_err(|e| UtilError::FileMtime(src.into(), e))?; Ok(mtime) } -pub fn set_mtime(filename: &Path, mtime: SystemTime) -> Result<(), SiteError> { +pub fn set_mtime(filename: &Path, mtime: SystemTime) -> Result<(), UtilError> { trace!( "set_mtime: filename={} mtime={:?}", filename.display(), @@ -53,19 +76,19 @@ pub fn set_mtime(filename: &Path, mtime: SystemTime) -> Result<(), SiteError> { if utimensat(AT_FDCWD, path.as_ptr(), times, AT_SYMLINK_NOFOLLOW) == -1 { let error = std::io::Error::last_os_error(); error!("utimensat failed on {:?}", path); - return Err(SiteError::Utimensat(pathbuf, error)); + return Err(UtilError::Utimensat(pathbuf, error)); } } Ok(()) } -pub fn copy_file_from_source(filename: &Path, output: &Path) -> Result<(), SiteError> { +pub fn copy_file_from_source(filename: &Path, output: &Path) -> Result<(), UtilError> { debug!("copying {} -> {}", filename.display(), output.display()); if let Some(parent) = output.parent() { trace!("parent: {}", parent.display()); if !parent.exists() { trace!("create parent {}", parent.display()); - std::fs::create_dir_all(parent).map_err(|e| SiteError::CreateDir(parent.into(), e))?; + std::fs::create_dir_all(parent).map_err(|e| UtilError::CreateDir(parent.into(), e))?; } } else { trace!("does not have parent: {}", output.display()); @@ -116,10 +139,10 @@ pub fn make_path_absolute(path: &Path) -> PathBuf { Path::new("/").join(path) } -fn timespec(time: SystemTime) -> Result<timespec, SiteError> { +fn timespec(time: SystemTime) -> Result<timespec, UtilError> { let dur = time .duration_since(UNIX_EPOCH) - .map_err(SiteError::UnixTime)?; + .map_err(UtilError::UnixTime)?; let tv_sec = dur.as_secs() as libc::time_t; let tv_nsec = dur.subsec_nanos() as libc::c_long; Ok(timespec { tv_sec, tv_nsec }) diff --git a/src/version.rs b/src/version.rs new file mode 100644 index 0000000..4066ab5 --- /dev/null +++ b/src/version.rs @@ -0,0 +1,57 @@ +use git_testament::{GitModification, GitTestament}; + +#[derive(Debug, thiserror::Error)] +pub enum VersionError { + #[error("string formatting error: {0}")] + Format(#[from] std::fmt::Error), +} + +pub struct Version<'a> { + testament: &'a GitTestament<'a>, +} + +impl<'a> Version<'a> { + pub fn new(testament: &'a GitTestament<'a>) -> Self { + Self { testament } + } + + pub fn version(&self) -> Result<String, VersionError> { + use std::fmt::Write as _; + let mut ret = String::new(); + writeln!(ret, "{}", &self.testament)?; + writeln!( + ret, + "{} {}", + env!("CARGO_PKG_NAME"), + env!("CARGO_PKG_VERSION") + )?; + Ok(ret) + } + + pub fn long_version(&self) -> Result<String, VersionError> { + use std::fmt::Write as _; + let mut ret = String::new(); + writeln!(ret, "{}", &self.testament)?; + writeln!(ret, "Crate version: {}", env!("CARGO_PKG_VERSION"))?; + if let Some(branch) = self.testament.branch_name { + writeln!(ret, "Built from branch: {}", branch)?; + } else { + writeln!(ret, "Branch information is missing.")?; + } + writeln!(ret, "Commit info: {}", self.testament.commit)?; + if self.testament.modifications.is_empty() { + writeln!(ret, "Working tree is clean")?; + } else { + use GitModification::*; + for fmod in self.testament.modifications { + match fmod { + Added(f) => writeln!(ret, "Added: {}", String::from_utf8_lossy(f))?, + Removed(f) => writeln!(ret, "Removed: {}", String::from_utf8_lossy(f))?, + Modified(f) => writeln!(ret, "Modified: {}", String::from_utf8_lossy(f))?, + Untracked(f) => writeln!(ret, "Untracked: {}", String::from_utf8_lossy(f))?, + } + } + } + Ok(ret) + } +} diff --git a/src/wikitext.rs b/src/wikitext.rs index b78cd66..4d8578a 100644 --- a/src/wikitext.rs +++ b/src/wikitext.rs @@ -1,11 +1,19 @@ use crate::directive::{Directive, Processed}; -use crate::error::SiteError; use crate::page::PageMeta; use crate::site::Site; use log::trace; use std::collections::HashMap; use std::path::Path; +#[derive(Debug, thiserror::Error)] +pub enum WikitextError { + #[error(transparent)] + Directive(#[from] crate::directive::DirectiveError), + + #[error(transparent)] + Site(#[from] crate::site::SiteError), +} + #[derive(Debug, Eq, PartialEq)] pub enum Snippet { Markdown(String), @@ -14,7 +22,7 @@ pub enum Snippet { } impl Snippet { - pub fn prepare(&self, site: &mut Site) -> Result<(), SiteError> { + pub fn prepare(&self, site: &mut Site) -> Result<(), WikitextError> { trace!("prepare snippet {:?}", self); if let Snippet::Directive(p) = self { trace!("creating directive from parsed directive"); @@ -29,14 +37,16 @@ impl Snippet { Ok(()) } - pub fn process(&self, site: &mut Site, meta: &mut PageMeta) -> Result<Processed, SiteError> { + pub fn process( + &self, + site: &mut Site, + meta: &mut PageMeta, + ) -> Result<Processed, WikitextError> { trace!("Snippet::process: self={:?}", self); let processed = match self { Snippet::Markdown(text) => Processed::Markdown(text.into()), Snippet::WikiLink(w) => { - let resolved = site - .resolve(meta.path(), Path::new(w.target())) - .map_err(|e| SiteError::PageProblem(meta.path().into(), Box::new(e)))?; + let resolved = site.resolve(meta.path(), Path::new(w.target()))?; trace!("resolved {} to {}", w.target(), resolved.display()); let link = format!("[{}]({})", w.link_text(), resolved.display()); Processed::Markdown(link) @@ -44,14 +54,13 @@ impl Snippet { Snippet::Directive(p) => { let e = Directive::try_from(p); if let Ok(d) = e { - d.process(site, meta) - .map_err(|e| SiteError::PageProblem(meta.path().into(), Box::new(e)))? + d.process(site, meta)? } else if let Some(shortcut) = site.shortcut(p.name()) { let arg = p.unnamed_args().first().unwrap().to_string(); let link = format!("[{}]({})", shortcut.desc(&arg), shortcut.url(&arg)); Processed::Markdown(link) } else { - return Err(e.unwrap_err()); + return Err(e.unwrap_err().into()); } } }; @@ -89,7 +98,7 @@ pub struct ParsedDirective { } impl ParsedDirective { - pub fn new(name: &str, args: HashMap<String, String>) -> Result<Self, SiteError> { + pub fn new(name: &str, args: HashMap<String, String>) -> Result<Self, WikitextError> { trace!("ParsedDirective::new: name={:?} args={:?}", name, args); Ok(Self { name: name.into(), |