summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2023-01-07 11:30:19 +0000
committerLars Wirzenius <liw@liw.fi>2023-01-07 11:30:19 +0000
commit2ac4193a30c5ef6186666269c171b88fabd86143 (patch)
tree2b0abcee8c9b2b7bc89acc40d14cac6b3e8ec9de
parent01c7f770bef9bd67c00cd5cd0f23495c68994a79 (diff)
parent883aca2e45efd82aaee843f381df6303315d628e (diff)
downloadriki-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.rs66
-rw-r--r--src/directive/calendar.rs7
-rw-r--r--src/directive/format.rs7
-rw-r--r--src/directive/graph.rs7
-rw-r--r--src/directive/img.rs9
-rw-r--r--src/directive/inline.rs7
-rw-r--r--src/directive/map.rs7
-rw-r--r--src/directive/meta.rs5
-rw-r--r--src/directive/mod.rs57
-rw-r--r--src/directive/pagestats.rs7
-rw-r--r--src/directive/shortcut.rs7
-rw-r--r--src/directive/sidebar.rs7
-rw-r--r--src/directive/table.rs5
-rw-r--r--src/directive/tag.rs5
-rw-r--r--src/directive/toc.rs7
-rw-r--r--src/directive/traillink.rs7
-rw-r--r--src/error.rs110
-rw-r--r--src/git.rs25
-rw-r--r--src/lib.rs1
-rw-r--r--src/page.rs48
-rw-r--r--src/parser.rs33
-rw-r--r--src/site.rs35
-rw-r--r--src/srcdir.rs11
-rw-r--r--src/time.rs13
-rw-r--r--src/util.rs55
-rw-r--r--src/version.rs57
-rw-r--r--src/wikitext.rs29
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),
}
diff --git a/src/git.rs b/src/git.rs
index 89e84db..598b330 100644
--- a/src/git.rs
+++ b/src/git.rs
@@ -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)?;
diff --git a/src/lib.rs b/src/lib.rs
index 04dffa6..be6e4a9 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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(),