# It is not intended for manual editing.
version = 3
-[[package]]
-name = "addr2line"
-version = "0.19.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97"
-dependencies = [
- "gimli",
-]
-
-[[package]]
-name = "adler"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
-
[[package]]
name = "anes"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
-[[package]]
-name = "backtrace"
-version = "0.3.67"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca"
-dependencies = [
- "addr2line",
- "cc",
- "cfg-if",
- "libc",
- "miniz_oxide",
- "object",
- "rustc-demangle",
-]
-
[[package]]
name = "bitflags"
version = "1.3.2"
[[package]]
name = "clap"
-version = "4.2.7"
+version = "4.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34d21f9bf1b425d2968943631ec91202fe5e837264063503708b83013f8fc938"
+checksum = "93aae7a4192245f70fe75dd9157fc7b4a5bf53e88d30bd4396f7d8f9284d5acc"
dependencies = [
"clap_builder",
"clap_derive",
[[package]]
name = "clap_builder"
-version = "4.2.7"
+version = "4.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "914c8c79fb560f238ef6429439a30023c862f7a28e688c58f7203f12b29970bd"
+checksum = "4f423e341edefb78c9caba2d9c7f7687d0e72e89df3ce3394554754393ac3990"
dependencies = [
"anstream",
"anstyle",
"bitflags",
- "clap_lex 0.4.1",
+ "clap_lex 0.5.0",
"strsim",
]
[[package]]
name = "clap_derive"
-version = "4.2.0"
+version = "4.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4"
+checksum = "191d9573962933b4027f932c600cd252ce27a8ad5979418fe78e43c07996f27b"
dependencies = [
"heck",
"proc-macro2",
[[package]]
name = "clap_lex"
-version = "0.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1"
-
-[[package]]
-name = "color-eyre"
-version = "0.5.11"
+version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f1885697ee8a177096d42f158922251a41973117f6d8a234cee94b9509157b7"
-dependencies = [
- "backtrace",
- "eyre",
- "indenter",
- "once_cell",
- "owo-colors",
-]
+checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
[[package]]
name = "colorchoice"
[[package]]
name = "digest"
-version = "0.10.6"
+version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
"version_check",
]
-[[package]]
-name = "gimli"
-version = "0.27.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4"
-
[[package]]
name = "half"
version = "1.8.2"
"cfg-if",
]
-[[package]]
-name = "memchr"
-version = "2.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
-
[[package]]
name = "memoffset"
version = "0.8.0"
name = "metaforge"
version = "0.1.1"
dependencies = [
- "clap 4.2.7",
- "color-eyre",
+ "clap 4.3.0",
"criterion",
+ "eyre",
"pandoc",
"pest",
"pest_derive",
-]
-
-[[package]]
-name = "miniz_oxide"
-version = "0.6.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa"
-dependencies = [
- "adler",
+ "thiserror",
]
[[package]]
"libc",
]
-[[package]]
-name = "object"
-version = "0.30.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439"
-dependencies = [
- "memchr",
-]
-
[[package]]
name = "once_cell"
version = "1.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267"
-[[package]]
-name = "owo-colors"
-version = "1.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2386b4ebe91c2f7f51082d4cefa145d030e33a1842a96b12e4885cc3c01f7a55"
-
[[package]]
name = "pandoc"
version = "0.8.10"
[[package]]
name = "proc-macro2"
-version = "1.0.57"
+version = "1.0.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4ec6d5fe0b140acb27c9a0444118cf55bfbb4e0b259739429abb4521dd67c16"
+checksum = "fa1fb82fc0c281dd9671101b66b771ebbe1eaf967b96ac8740dcba4b70005ca8"
dependencies = [
"unicode-ident",
]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c"
-[[package]]
-name = "rustc-demangle"
-version = "0.1.23"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
-
[[package]]
name = "rustix"
version = "0.37.19"
[dependencies]
clap = { version = "4", features = ["derive"] }
pandoc = "0.8"
-color-eyre = { version = "0.5", default-features = false }
+thiserror = "1"
+eyre = "0.6"
pest = "2"
pest_derive = "2"
#[cfg(test)]
mod tests;
-use crate::{MetaFile, Scope};
-use color_eyre::Result;
+use crate::{MetaError, MetaFile, Scope};
+use eyre::Result;
-pub fn build_metafile(file: &MetaFile) -> Result<String> {
+pub fn build_metafile(file: &MetaFile) -> Result<String, MetaError> {
if file.header.blank {
return Ok(String::new());
+ } else if file.header.ignore {
+ return Err(MetaError::Ignored);
}
let html = get_source_html(file)?;
use crate::{MetaFile, Scope, Src};
-use color_eyre::Result;
+use eyre::Result;
use std::collections::HashMap;
pub fn expand_arrays(input: String, file: &MetaFile) -> Result<String> {
-use crate::{MetaFile, Scope};
-use color_eyre::{eyre::bail, Result};
+use crate::{MetaError, MetaFile, Scope};
+use eyre::Result;
use std::fs;
pub fn get_pattern(key: &str, file: &MetaFile) -> Result<String> {
}
let mut filename = if let Some(name) = file.get_pat(&Scope::into_local(key)) {
- name.to_string()
+ Ok(name.to_string())
} else if let Some(name) = file.get_pat(&Scope::into_global(key)) {
- name.to_string()
+ Ok(name.to_string())
+ } else if file.header.panic_default {
+ Err(MetaError::UndefinedDefault {
+ pattern: key.to_string(),
+ path: file.path.to_string_lossy().to_string(),
+ })
} else {
// anything not defined should have a default.meta file to fall back to
- "default".to_string()
- };
+ Ok("default".to_string())
+ }?;
// BLANK returns nothing, so no more processing needs to be done
if filename == "BLANK" {
- return Ok(String::from(""));
+ return Ok(String::default());
};
// DEFAULT override for patterns overriding globals
filename = "default".to_string();
}
- // if we're building from base pattern we need to wait on
+ // if we're building the base pattern we need to wait on
// parsing/expansion so we can build and convert source to html
- // we just want to return the string right now
+ // for the SOURCE pattern. we just want to return the string right now
if key == "base" {
let pattern_path = key.to_string() + "/" + &filename;
let mut path = file.opts.pattern.join(pattern_path);
return match fs::read_to_string(&path) {
Ok(str) => Ok(str),
- Err(_) => bail!("could not find base file {}", path.display()),
+ Err(_) => Err(MetaError::FileNotFound {
+ path: path.to_string_lossy().to_string(),
+ }
+ .into()),
};
}
-use crate::{MetaFile, Src};
-use color_eyre::{eyre::bail, Result};
+use crate::{MetaError, MetaFile, Src};
+use eyre::Result;
use super::array::*;
use super::*;
return Ok(string);
}
+ let input: pandoc::InputFormat;
+ let output: pandoc::OutputFormat;
+ if let Ok(io) = get_pandoc_io(&file) {
+ input = io.0;
+ output = io.1;
+ } else {
+ // don't run pandoc if a filetype that isn't supported gets requested
+ return Ok(string);
+ }
+
let mut pandoc = pandoc::Pandoc::new();
pandoc
.set_input(pandoc::InputKind::Pipe(string))
.set_output(pandoc::OutputKind::Pipe)
- .set_input_format(pandoc::InputFormat::Markdown, vec![])
- .set_output_format(pandoc::OutputFormat::Html, vec![]);
+ .set_input_format(input, vec![])
+ .set_output_format(output, vec![]);
- if let Ok(pandoc::PandocOutput::ToBuffer(html)) = pandoc.execute() {
- Ok(html)
+ if let pandoc::PandocOutput::ToBuffer(s) = pandoc.execute()? {
+ Ok(s)
} else {
- bail!("pandoc could not write to buffer")
+ Err(MetaError::Pandoc { file: file.name()? }.into())
}
}
Ok(output)
}
}
+
+fn get_pandoc_io(
+ file: &MetaFile,
+) -> Result<(pandoc::InputFormat, pandoc::OutputFormat), MetaError> {
+ let input: pandoc::InputFormat;
+ let output: pandoc::OutputFormat;
+
+ let mut source_type = "";
+ if !file.header.source.is_empty() {
+ source_type = &file.header.source;
+ } else if !file.opts.input.is_empty() {
+ source_type = &file.opts.input;
+ }
+
+ match source_type {
+ "markdown" => input = pandoc::InputFormat::Markdown,
+ "html" => input = pandoc::InputFormat::Html,
+ "org" => input = pandoc::InputFormat::Org,
+ "json" => input = pandoc::InputFormat::Json,
+ "latex" => input = pandoc::InputFormat::Latex,
+ _ => return Err(MetaError::Filetype.into()),
+ }
+
+ let mut filetype = "";
+ if !file.header.filetype.is_empty() {
+ filetype = &file.header.filetype;
+ } else if !file.opts.input.is_empty() {
+ filetype = &file.opts.output;
+ }
+
+ match filetype {
+ "html" => output = pandoc::OutputFormat::Html,
+ "markdown" => output = pandoc::OutputFormat::Markdown,
+ "man" => output = pandoc::OutputFormat::Man,
+ "txt" => output = pandoc::OutputFormat::Plain,
+ "org" => output = pandoc::OutputFormat::Org,
+ "json" => output = pandoc::OutputFormat::Json,
+ "latex" => output = pandoc::OutputFormat::Latex,
+ "asciidoc" => output = pandoc::OutputFormat::Asciidoc,
+ "pdf" => output = pandoc::OutputFormat::Pdf,
+ _ => return Err(MetaError::Filetype.into()),
+ };
+
+ Ok((input, output))
+}
use crate::{build_metafile, MetaFile, Options};
-use color_eyre::{eyre::WrapErr, Result};
+use eyre::{Result, WrapErr};
use std::{error::Error, fs, path::PathBuf};
fn unit_test(test: (&str, &str)) -> Result<()> {
if output == test.1 {
Ok(())
} else {
- let err = color_eyre::eyre::eyre!("{} - failed", test.0);
+ let err = eyre::eyre!("{} - failed", test.0);
eprintln!("{:?}", err);
eprintln!("\nTEST:\n{}\nOUTPUT:\n{}", test.1, output);
Err(err)
for e in errs.iter() {
eprintln!("{}", e.to_string());
}
- return Err(color_eyre::eyre::eyre!("failed tests"));
+ return Err(eyre::eyre!("failed tests"));
}
Ok(())
-use crate::{MetaFile, Scope};
-use color_eyre::{eyre::bail, Result};
+use crate::{MetaError, MetaFile, Scope};
+use eyre::Result;
pub fn get_variable(key: &str, file: &MetaFile) -> Result<String> {
let long_key = file.name()? + "." + key;
Ok(val.clone())
} else if let Some(val) = file.get_var(&Scope::into_global(key)) {
Ok(val.clone())
- } else if file.opts.undefined {
- bail!("undefined variable: {}, {}", key.to_string(), &long_key)
+ } else if file.opts.undefined || file.header.panic_undefined {
+ return Err(MetaError::UndefinedExpand {
+ val: key.to_string(),
+ path: file.name()?,
+ }
+ .into());
} else {
Ok(String::new())
}
--- /dev/null
+use crate::Rule;
+use thiserror::Error;
+
+#[derive(Error, Debug)]
+pub enum MetaError {
+ #[error("unknown error")]
+ Unknown,
+ #[error("ignored")]
+ Ignored,
+ #[error("filetype")]
+ Filetype,
+ #[error("could not find {path}")]
+ FileNotFound { path: String },
+ #[error("could not determine name from {file}")]
+ Name { file: String },
+ #[error("pandoc could not write to buffer for {file}")]
+ Pandoc { file: String },
+ #[error("undefined expansion: {val}\n\tin {path}")]
+ UndefinedExpand { val: String, path: String },
+ #[error("undefined call to default.meta: {pattern}\n\tin {path}")]
+ UndefinedDefault { pattern: String, path: String },
+ #[error(transparent)]
+ MetaError(#[from] Box<MetaError>),
+ #[error(transparent)]
+ PandocError(#[from] pandoc::PandocError),
+ #[error(transparent)]
+ ParserError(#[from] pest::error::Error<Rule>),
+ #[error(transparent)]
+ Other(#[from] eyre::Error),
+}
extern crate pest_derive;
mod builder;
+mod error;
mod metafile;
mod options;
mod parser;
pub use builder::*;
+pub use error::*;
pub use metafile::*;
pub use options::*;
pub use parser::*;
use clap::Parser;
-use color_eyre::Result;
+use eyre::Result;
use std::fs;
pub fn get_opts() -> Result<Options> {
-fn main() -> color_eyre::Result<()> {
- color_eyre::install()?;
-
+fn main() -> eyre::Result<()> {
let opts = metaforge::get_opts()?;
if opts.new {
-use crate::{build_metafile, Options};
-use color_eyre::{eyre::eyre, Result};
+use crate::{build_metafile, MetaError, Options};
+use eyre::Result;
use std::{fs, path::PathBuf};
use super::*;
pub fn build(path: PathBuf, opts: &'a Options) -> Result<Self> {
assert!(path.is_dir() && path.exists());
+ // copy over directory structure from source dir
let build_dir = opts.build.join(path.strip_prefix(&opts.source)?);
if !build_dir.exists() {
fs::create_dir_all(build_dir)?;
fs::write(file.dest()?, str)?;
}
Err(e) => {
+ // print a line to stderr about failure but continue with other files
if self.opts.force {
- // print a line to stderr about failure but continue with other files
eprintln!("ignoring {}: {}", file.path.display(), e);
continue;
} else {
- return Err(e.wrap_err(eyre!("{}:", file.path.display())));
+ // we raise an ignored error to quickly abort any file parsing
+ if let MetaError::Ignored = e {
+ continue;
+ // anything else gets wrapped up and passed up the calling chain
+ } else {
+ return Err(e.into());
+ }
}
}
}
-use crate::{parse_string, Options};
-use color_eyre::{eyre::bail, Result};
+use crate::{parse_string, MetaError, Options};
+use eyre::Result;
use std::{collections::HashMap, path::PathBuf};
use super::*;
pub fn build(path: PathBuf, opts: &'a Options) -> Result<Self> {
let str = match std::fs::read_to_string(&path) {
Ok(str) => str,
- Err(_) => bail!("{} does not exist", path.display()),
+ Err(_) => {
+ return Err(MetaError::FileNotFound {
+ path: path.to_string_lossy().to_string(),
+ }
+ .into())
+ }
};
let mut metafile = parse_string(str, opts)?;
+
+ if metafile.header.ignore {
+ return Err(MetaError::Ignored.into());
+ }
+
metafile.path = path;
Ok(metafile)
}
.unwrap_or_default();
Ok(name)
} else {
- color_eyre::eyre::bail!("could not get name from: {}", self.path.display());
+ Err(MetaError::Name {
+ file: self.path.to_string_lossy().to_string(),
+ }
+ .into())
}
}
pub blank: bool,
pub panic_default: bool,
pub panic_undefined: bool,
+ pub equal_arrays: bool,
pub filetype: String,
+ pub source: String,
pub pandoc: bool,
+ pub ignore: bool,
}
impl Header {
blank: false,
panic_default: false,
panic_undefined: false,
+ equal_arrays: false,
filetype: String::from("html"),
+ source: String::from("markdown"),
pandoc: true,
+ ignore: false,
}
}
}
"blank" => header.blank = val == "true",
"panic_default" => header.panic_default = val == "true",
"panic_undefined" => header.panic_undefined = val == "true",
+ "equal_arrays" => header.equal_arrays = val == "true",
"pandoc" => header.pandoc = val == "true",
"filetype" => header.filetype = val.to_string(),
+ "source" => header.source = val.to_string(),
+ "ignore" => header.ignore = val == "true",
_ => continue,
}
}
use crate::Options;
-use color_eyre::Result;
+use eyre::Result;
use std::path::PathBuf;
use super::*;
/// Create a new skeleton directory
#[arg(long, default_value_t = false)]
pub new: bool,
- /// Enable extra output.
- /// Repeated flags give more info
+ /// Enable extra output. Repeated flags give more info
#[arg(short, long, action = clap::ArgAction::Count)]
pub verbose: u8,
/// Minimal output
/// Clean build directory before building site [FALSE]
#[arg(long, default_value_t = false)]
pub clean: bool,
- /// Don't convert markdown to html.
- /// Runs even if pandoc isn't installed [FALSE]
+ /// Don't call pandoc on source files
#[arg(long, default_value_t = false)]
pub no_pandoc: bool,
+ /// Output filetype [html]
+ #[arg(short, long, value_name = "OUTPUT_FILETYPE")]
+ pub output: Option<String>,
+ /// Input filetype [markdown]
+ #[arg(short, long, value_name = "INPUT_FILETYPE")]
+ pub input: Option<String>,
}
-use color_eyre::Result;
+use eyre::Result;
use std::path::PathBuf;
#[derive(Debug, Clone, Default)]
pub source: PathBuf,
pub build: PathBuf,
pub pattern: PathBuf,
+ pub input: String,
+ pub output: String,
pub verbose: u8,
pub quiet: bool,
pub force: bool,
source: PathBuf::new(),
build: PathBuf::new(),
pattern: PathBuf::new(),
+ input: String::default(),
+ output: String::default(),
verbose: 0,
quiet: false,
force: false,
}
impl TryFrom<crate::Opts> for Options {
- type Error = color_eyre::eyre::Error;
+ type Error = eyre::Error;
fn try_from(value: crate::Opts) -> Result<Self, Self::Error> {
- let mut options = Options::new();
+ let mut opts = Options::new();
- options.verbose = value.verbose;
- options.quiet = value.quiet;
- options.force = value.force;
- options.undefined = value.undefined;
- options.clean = value.clean;
- options.no_pandoc = value.no_pandoc;
- options.new = value.new;
+ opts.verbose = value.verbose;
+ opts.quiet = value.quiet;
+ opts.force = value.force;
+ opts.undefined = value.undefined;
+ opts.clean = value.clean;
+ opts.no_pandoc = value.no_pandoc;
+ opts.new = value.new;
if let Some(root) = value.root.as_deref() {
- options.root = PathBuf::from(root).canonicalize()?;
+ opts.root = PathBuf::from(root).canonicalize()?;
} else {
- options.root = std::env::current_dir()?;
+ opts.root = std::env::current_dir()?;
}
if let Some(source) = value.source.as_deref() {
- options.source = PathBuf::from(source).canonicalize()?;
+ opts.source = PathBuf::from(source).canonicalize()?;
} else {
- options.source = options.root.join("source");
+ opts.source = opts.root.join("source");
}
if let Some(build) = value.build.as_deref() {
- options.build = PathBuf::from(build).canonicalize()?;
+ opts.build = PathBuf::from(build).canonicalize()?;
} else {
- options.build = options.root.join("build");
+ opts.build = opts.root.join("build");
}
if let Some(pattern) = value.pattern.as_deref() {
- options.pattern = PathBuf::from(pattern).canonicalize()?;
+ opts.pattern = PathBuf::from(pattern).canonicalize()?;
} else {
- options.pattern = options.root.join("pattern");
+ opts.pattern = opts.root.join("pattern");
}
- Ok(options)
+ if let Some(input) = value.input {
+ opts.input = input;
+ } else {
+ opts.input = String::from("html");
+ }
+
+ if let Some(output) = value.output {
+ opts.output = output;
+ } else {
+ opts.output = String::from("markdown");
+ }
+
+ Ok(opts)
}
}
-use crate::{Header, MetaFile, Options};
-use color_eyre::{eyre::WrapErr, Result};
-use pest::{
- iterators::{Pair, Pairs},
- Parser,
-};
-
mod array;
mod def_block;
mod header;
#[cfg(test)]
mod tests;
+use crate::{Header, MetaFile, Options};
+use eyre::Result;
+use pest::{
+ iterators::{Pair, Pairs},
+ Parser,
+};
+
#[derive(Parser)]
#[grammar = "parser/meta.pest"]
pub struct MetaParser;
pub fn parse_string(file: String, opts: &Options) -> Result<MetaFile> {
- let meta_source = MetaParser::parse(Rule::file, &file)
- .wrap_err("parser error")?
- .next()
- .unwrap();
+ let meta_source = MetaParser::parse(Rule::file, &file)?.next().unwrap();
let metafile = parse_file(meta_source, opts);
Ok(metafile)
-use color_eyre::Result;
+use eyre::Result;
#[test]
fn build_test_site() -> Result<()> {