+++ /dev/null
-mod array;
-mod pattern;
-mod source;
-mod variable;
-
-use pattern::*;
-use source::*;
-use variable::*;
-
-#[cfg(test)]
-mod tests;
-
-use crate::{log, MetaError, MetaFile, Scope};
-use eyre::Result;
-
-pub fn build_metafile(file: &MetaFile) -> Result<String, Box<MetaError>> {
- log!(file.opts, format!("building {}", file.path.display()), 1);
-
- if file.header.blank {
- return Ok(String::new());
- } else if file.header.ignore {
- return Err(Box::new(MetaError::Ignored));
- }
-
- let html = get_source_html(file).map_err(MetaError::from)?;
-
- let pattern = get_pattern("base", file).map_err(MetaError::from)?;
- let mut base = crate::parse_string(pattern, file.opts).map_err(MetaError::from)?;
-
- base.merge(file);
- base.patterns.insert(Scope::into_global("SOURCE"), html);
-
- let output = metafile_to_string(&base).map_err(MetaError::from)?;
-
- Ok(output)
-}
+++ /dev/null
-use crate::{log, MetaError, MetaFile, Scope, Src};
-use eyre::Result;
-use std::collections::HashMap;
-
-pub fn expand_arrays(input: String, file: &MetaFile) -> Result<String> {
- log!(
- file.opts,
- format!("expanding arrays in {}", file.path.display()),
- 2
- );
-
- let map: HashMap<String, &[String]> = file
- .source
- .iter()
- // filter out arrays from source vec
- .filter_map(|x| {
- if let Src::Arr(array) = x {
- Some(array)
- } else {
- None
- }
- })
- // make a hash map of [keys in source] -> [defined arrays]
- .map(|key| {
- // concat array to pattern name to get key in HashMap
- let name = file.name().unwrap_or_default();
- let long_key = name + "." + key;
-
- let value = if let Some(val) = file.get_arr(&Scope::into_global(&long_key)) {
- val
- } else if let Some(val) = file.get_arr(&Scope::into_local(&long_key)) {
- val
- } else if let Some(val) = file.get_arr(&Scope::into_global(key)) {
- val
- } else if let Some(val) = file.get_arr(&Scope::into_local(key)) {
- val
- } else if file.opts.undefined {
- panic!(
- "{}",
- MetaError::UndefinedExpand {
- val: key.to_string(),
- path: file.path.to_string_lossy().to_string(),
- }
- )
- } else {
- &[]
- };
- (key.to_string(), value)
- })
- .collect();
-
- // loop to duplicate the output template for each array member
- let mut expanded = String::new();
- let size = match get_array_size(&map, file.header.equal_arrays) {
- Ok(num) => Ok(num),
- Err(e) => match e.as_ref() {
- &MetaError::Array => Err(MetaError::UnequalArrays {
- path: file.path.to_string_lossy().to_string(),
- }),
- _ => Err(MetaError::Unknown),
- },
- }?;
- for i in 0..size {
- // get a fresh copy of the file
- let mut str = input.clone();
- // replace each key in the file
- for (key, val) in map.iter() {
- if let Some(value) = val.get(i) {
- str = str.replace(&format!("-{{{key}}}"), value);
- }
- }
- // concatenate to final file
- expanded.push_str(&str);
- }
-
- Ok(expanded)
-}
-
-fn get_array_size(
- map: &HashMap<String, &[String]>,
- same_size: bool,
-) -> Result<usize, Box<MetaError>> {
- if same_size {
- let mut size = (0, false);
- for val in map.values() {
- if !size.1 {
- size = (val.len(), true);
- } else if size.0 != val.len() {
- return Err(Box::new(MetaError::Array));
- }
- }
- return Ok(size.0);
- }
-
- let mut max = 0;
- for val in map.values() {
- if max < val.len() {
- max = val.len();
- }
- }
- Ok(max)
-}
+++ /dev/null
-use crate::{log, MetaError, MetaFile, Scope};
-use eyre::Result;
-use std::fs;
-
-pub fn get_pattern(key: &str, file: &MetaFile) -> Result<String> {
- log!(file.opts, format!("expanding {key}"), 2);
- // SOURCE is already expanded in the initial build_metafile() call
- if key == "SOURCE" {
- if let Some(source) = file.patterns.get(&Scope::into_global("SOURCE")) {
- return Ok(source.to_string());
- } else {
- return Ok(String::new());
- }
- }
-
- let mut filename = if let Some(name) = file.get_pat(&Scope::into_local(key)) {
- Ok(name.to_string())
- } else if let Some(name) = file.get_pat(&Scope::into_global(key)) {
- 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
- Ok("default".to_string())
- }?;
-
- // BLANK returns nothing, so no more processing needs to be done
- if filename == "BLANK" {
- return Ok(String::default());
- };
-
- // DEFAULT override for patterns overriding globals
- if filename == "DEFAULT" {
- filename = "default".to_string();
- }
-
- // if we're building the base pattern we need to wait on
- // parsing/expansion so we can build and convert source to html
- // 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);
- path.set_extension("meta");
-
- return match fs::read_to_string(&path) {
- Ok(str) => Ok(str),
- Err(_) => Err(MetaError::FileNotFound {
- path: path.to_string_lossy().to_string(),
- }
- .into()),
- };
- }
-
- let pattern_path = key.replace('.', "/") + "/" + &filename;
- let mut path = file.opts.pattern.join(pattern_path);
- path.set_extension("meta");
-
- let mut pattern = MetaFile::build(path, file.opts)?;
-
- // copy over maps for expanding contained variables
- pattern.merge(file);
-
- super::metafile_to_string(&pattern)
-}
+++ /dev/null
-use crate::{log, MetaError, MetaFile, Src};
-use eyre::Result;
-use pandoc::{InputFormat, InputKind, OutputFormat, OutputKind, Pandoc};
-
-use super::array::*;
-use super::*;
-
-pub fn get_source_html(file: &MetaFile) -> Result<String> {
- let string = metafile_to_string(file)?;
-
- if file.opts.no_pandoc || !file.header.pandoc || string.is_empty() {
- return Ok(string);
- }
-
- let input: InputFormat;
- let output: 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);
- }
-
- log!(file.opts, "calling pandoc", 3);
-
- let mut pandoc = Pandoc::new();
- pandoc
- .set_input(InputKind::Pipe(string))
- .set_output(OutputKind::Pipe)
- .set_input_format(input, vec![])
- .set_output_format(output, vec![]);
-
- if let pandoc::PandocOutput::ToBuffer(s) = pandoc.execute()? {
- Ok(s)
- } else {
- Err(MetaError::Pandoc { file: file.name()? }.into())
- }
-}
-
-pub fn metafile_to_string(file: &MetaFile) -> Result<String> {
- if file.header.blank {
- return Ok(String::new());
- }
-
- let mut output = String::default();
- let mut arrays = false;
-
- for section in file.source.iter() {
- let sec = match section {
- // concatenate any char sequences
- Src::Str(str) => str.to_string(),
- // expand all variables and recursively expand patterns
- Src::Var(key) => get_variable(key, file)?,
- Src::Pat(key) => get_pattern(key, file)?,
- Src::Arr(key) => {
- arrays = true;
- // comments have already been removed at this point,
- // so we use them to mark keys for array substitution
- format!("-{{{key}}}")
- }
- };
-
- output.push_str(&sec);
- }
-
- if arrays {
- expand_arrays(output, file)
- } else {
- Ok(output)
- }
-}
-
-fn get_pandoc_io(
- file: &MetaFile,
-) -> Result<(pandoc::InputFormat, pandoc::OutputFormat), Box<MetaError>> {
- 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;
- }
-
- let input = match source_type {
- "markdown" => Ok(InputFormat::Markdown),
- "html" => Ok(InputFormat::Html),
- "org" => Ok(InputFormat::Org),
- "json" => Ok(InputFormat::Json),
- "latex" => Ok(InputFormat::Latex),
- _ => Err(Box::new(MetaError::Filetype)),
- }?;
-
- let mut filetype = "";
- if !file.header.filetype.is_empty() {
- filetype = &file.header.filetype;
- } else if !file.opts.input.is_empty() {
- filetype = &file.opts.output;
- }
-
- let output = match filetype {
- "html" => Ok(OutputFormat::Html),
- "markdown" => Ok(OutputFormat::Markdown),
- "man" => Ok(OutputFormat::Man),
- "txt" => Ok(OutputFormat::Plain),
- "org" => Ok(OutputFormat::Org),
- "json" => Ok(OutputFormat::Json),
- "latex" => Ok(OutputFormat::Latex),
- "asciidoc" => Ok(OutputFormat::Asciidoc),
- "pdf" => Ok(OutputFormat::Pdf),
- _ => Err(Box::new(MetaError::Filetype)),
- }?;
-
- Ok((input, output))
-}
+++ /dev/null
-use crate::{log, MetaError, MetaFile, Scope};
-use eyre::Result;
-
-pub fn get_variable(key: &str, file: &MetaFile) -> Result<String> {
- log!(
- file.opts,
- format!("substituting {key} in {}", file.path.display()),
- 2
- );
- let long_key = file.name()? + "." + key;
- if let Some(val) = file.get_var(&Scope::into_local(&long_key)) {
- Ok(val.clone())
- } else if let Some(val) = file.get_var(&Scope::into_global(&long_key)) {
- Ok(val.clone())
- } else if let Some(val) = file.get_var(&Scope::into_local(key)) {
- Ok(val.clone())
- } else if let Some(val) = file.get_var(&Scope::into_global(key)) {
- Ok(val.clone())
- } 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())
- }
-}
#[macro_use]
extern crate pest_derive;
-mod builder;
mod error;
mod metafile;
mod options;
mod parser;
-pub use builder::*;
+#[cfg(test)]
+mod tests;
+
pub use error::*;
pub use metafile::*;
pub use options::*;
use eyre::Result;
use std::fs;
+#[macro_export]
+macro_rules! log {
+ ($opts:expr, $string:expr, $level:expr) => {
+ if $opts.verbose >= $level && !$opts.quiet {
+ println!("{}", $string);
+ }
+ };
+}
+
pub fn get_opts() -> Result<Options> {
let opts = Options::try_from(Opts::parse())?;
let file = parse_string(source, opts)?;
- Ok(build_metafile(&file)?)
+ Ok(file.construct()?)
}
pub fn new_site(opts: &Options) -> Result<()> {
log!(
- &opts,
+ opts,
format!("building new site skeleton in {}", opts.root.display()),
1
);
-use crate::{build_metafile, MetaError, Options};
+use crate::{MetaError, Options};
use eyre::Result;
use std::{fs, path::PathBuf};
pub fn build_files(&mut self) -> Result<()> {
for file in self.files.iter_mut() {
file.merge(&self.global);
- match build_metafile(file) {
+ match file.construct() {
Ok(str) => {
fs::write(file.dest()?, str)?;
}
-use crate::{parse_string, MetaError, Options};
+mod arrays;
+mod attributes;
+mod patterns;
+mod source;
+mod variables;
+
+use crate::{log, parse_string, MetaError, Options};
use eyre::Result;
+use pandoc::{InputFormat, InputKind, OutputFormat, OutputKind, Pandoc};
use std::{collections::HashMap, path::PathBuf};
use super::*;
}
impl<'a> MetaFile<'a> {
+ pub fn new(opts: &'a Options) -> Self {
+ Self {
+ opts,
+ path: PathBuf::new(),
+ header: Header::new(),
+ variables: HashMap::new(),
+ arrays: HashMap::new(),
+ patterns: HashMap::new(),
+ source: Vec::new(),
+ }
+ }
+
pub fn build(path: PathBuf, opts: &'a Options) -> Result<Self> {
let str = match std::fs::read_to_string(&path) {
Ok(str) => str,
Ok(metafile)
}
- pub fn new(opts: &'a Options) -> Self {
- Self {
- opts,
- path: PathBuf::new(),
- header: Header::new(),
- variables: HashMap::new(),
- arrays: HashMap::new(),
- patterns: HashMap::new(),
- source: Vec::new(),
- }
- }
-
- pub fn dest(&self) -> Result<PathBuf> {
- let mut path = self
- .opts
- .build
- .join(self.path.strip_prefix(&self.opts.source)?);
- path.set_extension(&self.header.filetype);
-
- Ok(path)
- }
+ pub fn construct(&self) -> Result<String, Box<MetaError>> {
+ log!(self.opts, format!("building {}", self.path.display()), 1);
- pub fn name(&self) -> Result<String> {
- if self.path.starts_with(&self.opts.source) {
- // in source dir, we want the file name without the '.meta' extension
- let name: String = self
- .path
- .strip_prefix(&self.opts.source)?
- .components()
- .map(|x| {
- x.as_os_str()
- .to_string_lossy()
- .to_string()
- .replace(".meta", "")
- })
- .collect::<Vec<String>>()
- .join(".");
- Ok(name)
- } else if self.path.starts_with(&self.opts.pattern) {
- // in pattern dir, we want the parent dir
- let name = self.path.strip_prefix(&self.opts.pattern)?;
- let name = name
- .parent()
- .map(|s| s.to_string_lossy().to_string().replace('/', "."))
- .unwrap_or_default();
- Ok(name)
- } else {
- Err(MetaError::Name {
- file: self.path.to_string_lossy().to_string(),
- }
- .into())
+ if self.header.blank {
+ return Ok(String::new());
+ } else if self.header.ignore {
+ return Err(Box::new(MetaError::Ignored));
}
- }
-
- pub fn get_var(&self, key: &Scope) -> Option<&String> {
- self.variables.get(key)
- }
- pub fn get_arr(&self, key: &Scope) -> Option<&[String]> {
- self.arrays.get(key).map(|a| &a[..])
- }
+ let html = self.to_html().map_err(MetaError::from)?;
- pub fn get_pat(&self, key: &Scope) -> Option<&String> {
- self.patterns.get(key)
- }
+ let pattern = self.get_pattern("base").map_err(MetaError::from)?;
+ let mut base = crate::parse_string(pattern, self.opts).map_err(MetaError::from)?;
- pub fn var_defined(&self, key: &str) -> bool {
- self.variables.contains_key(&Scope::into_local(key))
- || self.variables.contains_key(&Scope::into_global(key))
- }
+ base.merge(self);
+ base.patterns.insert(Scope::into_global("SOURCE"), html);
- pub fn arr_defined(&self, key: &str) -> bool {
- self.arrays.contains_key(&Scope::into_local(key))
- || self.arrays.contains_key(&Scope::into_global(key))
- }
+ let output = base.get_source().map_err(MetaError::from)?;
- pub fn pat_defined(&self, key: &str) -> bool {
- self.patterns.contains_key(&Scope::into_local(key))
- || self.patterns.contains_key(&Scope::into_global(key))
+ Ok(output)
}
pub fn merge(&mut self, other: &Self) {
--- /dev/null
+use super::*;
+
+impl<'a> MetaFile<'a> {
+ pub fn expand_arrays(&self, input: String) -> Result<String> {
+ log!(
+ self.opts,
+ format!("expanding arrays in {}", self.path.display()),
+ 2
+ );
+
+ let map: HashMap<String, &[String]> = self
+ .source
+ .iter()
+ // filter out arrays from source vec
+ .filter_map(|x| {
+ if let Src::Arr(array) = x {
+ Some(array)
+ } else {
+ None
+ }
+ })
+ // make a hash map of [keys in source] -> [defined arrays]
+ .map(|key| {
+ // concat array to pattern name to get key in HashMap
+ let name = self.name().unwrap_or_default();
+ let long_key = name + "." + key;
+
+ let value = if let Some(val) = self.arrays.get(&Scope::into_global(&long_key)) {
+ &val[..]
+ } else if let Some(val) = self.arrays.get(&Scope::into_local(&long_key)) {
+ &val[..]
+ } else if let Some(val) = self.arrays.get(&Scope::into_global(key)) {
+ &val[..]
+ } else if let Some(val) = self.arrays.get(&Scope::into_local(key)) {
+ &val[..]
+ } else if self.opts.undefined {
+ panic!(
+ "{}",
+ MetaError::UndefinedExpand {
+ val: key.to_string(),
+ path: self.path.to_string_lossy().to_string(),
+ }
+ )
+ } else {
+ &[]
+ };
+ (key.to_string(), value)
+ })
+ .collect();
+
+ // loop to duplicate the output template for each array member
+ let mut expanded = String::new();
+ let size = if self.header.equal_arrays {
+ let mut size = (0, false);
+ for val in map.values() {
+ if !size.1 {
+ size = (val.len(), true);
+ } else if size.0 != val.len() {
+ return Err(eyre::Error::from(MetaError::Array));
+ }
+ }
+ Ok::<usize, eyre::Error>(size.0)
+ } else {
+ let mut max = 0;
+ for val in map.values() {
+ if max < val.len() {
+ max = val.len();
+ }
+ }
+ Ok(max)
+ }?;
+
+ for i in 0..size {
+ // get a fresh copy of the file
+ let mut str = input.clone();
+ // replace each key in the file
+ for (key, val) in map.iter() {
+ if let Some(value) = val.get(i) {
+ str = str.replace(&format!("-{{{key}}}"), value);
+ }
+ }
+ // concatenate to final file
+ expanded.push_str(&str);
+ }
+
+ Ok(expanded)
+ }
+}
--- /dev/null
+use super::*;
+
+impl<'a> MetaFile<'a> {
+ pub fn dest(&self) -> Result<PathBuf> {
+ let mut path = self
+ .opts
+ .build
+ .join(self.path.strip_prefix(&self.opts.source)?);
+ path.set_extension(&self.header.filetype);
+
+ Ok(path)
+ }
+
+ pub fn name(&self) -> Result<String> {
+ if self.path.starts_with(&self.opts.source) {
+ // in source dir, we want the file name without the '.meta' extension
+ let name: String = self
+ .path
+ .strip_prefix(&self.opts.source)?
+ .components()
+ .map(|x| {
+ x.as_os_str()
+ .to_string_lossy()
+ .to_string()
+ .replace(".meta", "")
+ })
+ .collect::<Vec<String>>()
+ .join(".");
+ Ok(name)
+ } else if self.path.starts_with(&self.opts.pattern) {
+ // in pattern dir, we want the parent dir
+ let name = self.path.strip_prefix(&self.opts.pattern)?;
+ let name = name
+ .parent()
+ .map(|s| s.to_string_lossy().to_string().replace('/', "."))
+ .unwrap_or_default();
+ Ok(name)
+ } else {
+ Err(MetaError::Name {
+ file: self.path.to_string_lossy().to_string(),
+ }
+ .into())
+ }
+ }
+}
--- /dev/null
+use super::*;
+
+impl<'a> MetaFile<'a> {
+ pub fn get_pattern(&self, key: &str) -> Result<String> {
+ log!(self.opts, format!("expanding {key}"), 2);
+ // SOURCE is already expanded in the initial construct() call
+ if key == "SOURCE" {
+ if let Some(source) = self.patterns.get(&Scope::into_global("SOURCE")) {
+ return Ok(source.to_string());
+ } else {
+ return Ok(String::new());
+ }
+ }
+
+ let mut filename = if let Some(name) = self.patterns.get(&Scope::into_local(key)) {
+ Ok(name.to_string())
+ } else if let Some(name) = self.patterns.get(&Scope::into_global(key)) {
+ Ok(name.to_string())
+ } else if self.header.panic_default {
+ Err(MetaError::UndefinedDefault {
+ pattern: key.to_string(),
+ path: self.path.to_string_lossy().to_string(),
+ })
+ } else {
+ // anything not defined should have a default.meta file to fall back to
+ Ok("default".to_string())
+ }?;
+
+ // BLANK returns nothing, so no more processing needs to be done
+ if filename == "BLANK" {
+ return Ok(String::default());
+ };
+
+ // DEFAULT override for patterns overriding globals
+ if filename == "DEFAULT" {
+ filename = "default".to_string();
+ }
+
+ // if we're building the base pattern we need to wait on
+ // parsing/expansion so we can build and convert source to html
+ // 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 = self.opts.pattern.join(pattern_path);
+ path.set_extension("meta");
+
+ return match std::fs::read_to_string(&path) {
+ Ok(str) => Ok(str),
+ Err(_) => Err(MetaError::FileNotFound {
+ path: path.to_string_lossy().to_string(),
+ }
+ .into()),
+ };
+ }
+
+ let pattern_path = key.replace('.', "/") + "/" + &filename;
+ let mut path = self.opts.pattern.join(pattern_path);
+ path.set_extension("meta");
+
+ let mut pattern = MetaFile::build(path, self.opts)?;
+
+ // copy over maps for expanding contained variables
+ pattern.merge(self);
+
+ pattern.get_source()
+ }
+}
--- /dev/null
+use super::*;
+
+impl<'a> MetaFile<'a> {
+ pub fn to_html(&self) -> Result<String> {
+ let string = self.get_source()?;
+
+ if self.opts.no_pandoc || !self.header.pandoc || string.is_empty() {
+ return Ok(string);
+ }
+
+ let input: InputFormat;
+ let output: OutputFormat;
+ if let Ok(io) = self.pandoc_io() {
+ input = io.0;
+ output = io.1;
+ } else {
+ // don't run pandoc if a filetype that isn't supported gets requested
+ return Ok(string);
+ }
+
+ log!(self.opts, "calling pandoc", 3);
+
+ let mut pandoc = Pandoc::new();
+ pandoc
+ .set_input(InputKind::Pipe(string))
+ .set_output(OutputKind::Pipe)
+ .set_input_format(input, vec![])
+ .set_output_format(output, vec![]);
+
+ if let pandoc::PandocOutput::ToBuffer(s) = pandoc.execute()? {
+ Ok(s)
+ } else {
+ Err(MetaError::Pandoc { file: self.name()? }.into())
+ }
+ }
+
+ fn pandoc_io(&self) -> Result<(pandoc::InputFormat, pandoc::OutputFormat), Box<MetaError>> {
+ let mut source_type = "";
+ if !self.header.source.is_empty() {
+ source_type = &self.header.source;
+ } else if !self.opts.input.is_empty() {
+ source_type = &self.opts.input;
+ }
+
+ let input = match source_type {
+ "markdown" => Ok(InputFormat::Markdown),
+ "html" => Ok(InputFormat::Html),
+ "org" => Ok(InputFormat::Org),
+ "json" => Ok(InputFormat::Json),
+ "latex" => Ok(InputFormat::Latex),
+ _ => Err(Box::new(MetaError::Filetype)),
+ }?;
+
+ let mut filetype = "";
+ if !self.header.filetype.is_empty() {
+ filetype = &self.header.filetype;
+ } else if !self.opts.input.is_empty() {
+ filetype = &self.opts.output;
+ }
+
+ let output = match filetype {
+ "html" => Ok(OutputFormat::Html),
+ "markdown" => Ok(OutputFormat::Markdown),
+ "man" => Ok(OutputFormat::Man),
+ "txt" => Ok(OutputFormat::Plain),
+ "org" => Ok(OutputFormat::Org),
+ "json" => Ok(OutputFormat::Json),
+ "latex" => Ok(OutputFormat::Latex),
+ "asciidoc" => Ok(OutputFormat::Asciidoc),
+ "pdf" => Ok(OutputFormat::Pdf),
+ _ => Err(Box::new(MetaError::Filetype)),
+ }?;
+
+ Ok((input, output))
+ }
+
+ pub fn get_source(&self) -> Result<String> {
+ if self.header.blank {
+ return Ok(String::new());
+ }
+
+ let mut output = String::default();
+ let mut arrays = false;
+
+ for section in self.source.iter() {
+ let sec = match section {
+ // concatenate any char sequences
+ Src::Str(str) => str.to_string(),
+ // expand all variables and recursively expand patterns
+ Src::Var(key) => self.get_variable(key)?,
+ Src::Pat(key) => self.get_pattern(key)?,
+ Src::Arr(key) => {
+ arrays = true;
+ // comments have already been removed at this point,
+ // so we use them to mark keys for array substitution
+ format!("-{{{key}}}")
+ }
+ };
+
+ output.push_str(&sec);
+ }
+
+ if arrays {
+ self.expand_arrays(output)
+ } else {
+ Ok(output)
+ }
+ }
+}
--- /dev/null
+use super::*;
+
+impl<'a> MetaFile<'a> {
+ pub fn get_variable(&self, key: &str) -> Result<String> {
+ log!(
+ self.opts,
+ format!(
+ "substituting {} in {}",
+ key.to_string(),
+ self.path.display()
+ ),
+ 2
+ );
+ let long_key = self.name()? + "." + &key.to_string();
+ if let Some(val) = self.variables.get(&Scope::into_local(&long_key)) {
+ Ok(val.clone())
+ } else if let Some(val) = self.variables.get(&Scope::into_global(&long_key)) {
+ Ok(val.clone())
+ } else if let Some(val) = self.variables.get(&Scope::into_local(key)) {
+ Ok(val.clone())
+ } else if let Some(val) = self.variables.get(&Scope::into_global(key)) {
+ Ok(val.clone())
+ } else if self.opts.undefined || self.header.panic_undefined {
+ return Err(MetaError::UndefinedExpand {
+ val: key.to_string(),
+ path: self.name()?,
+ }
+ .into());
+ } else {
+ Ok(String::new())
+ }
+ }
+}
-pub mod arg_parser;
-pub mod opt_struct;
+use clap::Parser;
+use eyre::Result;
+use std::path::PathBuf;
-pub use arg_parser::*;
-pub use opt_struct::*;
+#[derive(Parser, Debug)]
+#[command(author = "Huck Boles")]
+#[command(version = "0.1.1")]
+#[command(about = "A customizable template driven static site generator")]
+#[command(long_about = None)]
+pub struct Opts {
+ /// root directory [current_dir]
+ #[arg(short, long, value_name = "ROOT_DIR")]
+ pub root: Option<String>,
+ /// source file directory [current_dir/source]
+ #[arg(short, long, value_name = "SOURCE_DIR")]
+ pub source: Option<String>,
+ /// build directory [current_dir/build]
+ #[arg(short, long, value_name = "BUILD_DIR")]
+ pub build: Option<String>,
+ /// pattern directory [current_dir/pattern]
+ #[arg(short, long, value_name = "PATTERN_DIR")]
+ pub pattern: Option<String>,
+ /// only build a single file
+ #[arg(short, long, value_name = "FILENAME")]
+ pub file: Option<String>,
+ /// create a new skeleton directory
+ #[arg(long, default_value_t = false)]
+ pub new: bool,
+ /// enable extra output. repeated flags give more info
+ #[arg(short, long, action = clap::ArgAction::Count)]
+ pub verbose: u8,
+ /// minimal output
+ #[arg(short, long, default_value_t = false)]
+ pub quiet: bool,
+ /// don't stop on file failure [false]
+ #[arg(long, default_value_t = false)]
+ pub force: bool,
+ /// stop on undefined variables and arrays [false]
+ #[arg(long, default_value_t = false)]
+ pub undefined: bool,
+ /// clean build directory before building site [false]
+ #[arg(long, default_value_t = false)]
+ pub clean: bool,
+ /// 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>,
+}
+
+#[derive(Debug, Clone, Default)]
+pub struct Options {
+ pub root: PathBuf,
+ pub source: PathBuf,
+ pub build: PathBuf,
+ pub pattern: PathBuf,
+ pub file: Option<PathBuf>,
+ pub input: String,
+ pub output: String,
+ pub verbose: u8,
+ pub quiet: bool,
+ pub force: bool,
+ pub undefined: bool,
+ pub clean: bool,
+ pub no_pandoc: bool,
+ pub new: bool,
+}
-#[macro_export]
-macro_rules! log {
- ($opts:expr, $string:expr, $level:expr) => {
- if $opts.verbose >= $level && !$opts.quiet {
- println!("{}", $string);
+impl Options {
+ pub fn new() -> Self {
+ Self {
+ root: PathBuf::new(),
+ source: PathBuf::new(),
+ build: PathBuf::new(),
+ pattern: PathBuf::new(),
+ file: None,
+ input: String::default(),
+ output: String::default(),
+ verbose: 0,
+ quiet: false,
+ force: false,
+ undefined: false,
+ clean: false,
+ no_pandoc: false,
+ new: false,
}
- };
+ }
+}
+
+impl TryFrom<crate::Opts> for Options {
+ type Error = eyre::Error;
+ fn try_from(value: crate::Opts) -> Result<Self, Self::Error> {
+ let mut opts = Options::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() {
+ opts.root = PathBuf::from(root).canonicalize()?;
+ } else {
+ opts.root = std::env::current_dir()?;
+ }
+
+ if let Some(source) = value.source.as_deref() {
+ opts.source = PathBuf::from(source).canonicalize()?;
+ } else {
+ opts.source = opts.root.join("source");
+ }
+
+ if let Some(build) = value.build.as_deref() {
+ opts.build = PathBuf::from(build).canonicalize()?;
+ } else {
+ opts.build = opts.root.join("build");
+ }
+
+ if let Some(pattern) = value.pattern.as_deref() {
+ opts.pattern = PathBuf::from(pattern).canonicalize()?;
+ } else {
+ opts.pattern = opts.root.join("pattern");
+ }
+
+ if let Some(file) = value.file.as_deref() {
+ opts.file = Some(PathBuf::from(file).canonicalize()?);
+ }
+
+ 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)
+ }
}
+++ /dev/null
-use clap::Parser;
-
-#[derive(Parser, Debug)]
-#[command(author = "Huck Boles")]
-#[command(version = "0.1.1")]
-#[command(about = "A customizable template driven static site generator")]
-#[command(long_about = None)]
-pub struct Opts {
- /// root directory [current_dir]
- #[arg(short, long, value_name = "ROOT_DIR")]
- pub root: Option<String>,
- /// source file directory [current_dir/source]
- #[arg(short, long, value_name = "SOURCE_DIR")]
- pub source: Option<String>,
- /// build directory [current_dir/build]
- #[arg(short, long, value_name = "BUILD_DIR")]
- pub build: Option<String>,
- /// pattern directory [current_dir/pattern]
- #[arg(short, long, value_name = "PATTERN_DIR")]
- pub pattern: Option<String>,
- /// only build a single file
- #[arg(short, long, value_name = "FILENAME")]
- pub file: Option<String>,
- /// create a new skeleton directory
- #[arg(long, default_value_t = false)]
- pub new: bool,
- /// enable extra output. repeated flags give more info
- #[arg(short, long, action = clap::ArgAction::Count)]
- pub verbose: u8,
- /// minimal output
- #[arg(short, long, default_value_t = false)]
- pub quiet: bool,
- /// don't stop on file failure [false]
- #[arg(long, default_value_t = false)]
- pub force: bool,
- /// stop on undefined variables and arrays [false]
- #[arg(long, default_value_t = false)]
- pub undefined: bool,
- /// clean build directory before building site [false]
- #[arg(long, default_value_t = false)]
- pub clean: bool,
- /// 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>,
-}
+++ /dev/null
-use eyre::Result;
-use std::path::PathBuf;
-
-#[derive(Debug, Clone, Default)]
-pub struct Options {
- pub root: PathBuf,
- pub source: PathBuf,
- pub build: PathBuf,
- pub pattern: PathBuf,
- pub file: Option<PathBuf>,
- pub input: String,
- pub output: String,
- pub verbose: u8,
- pub quiet: bool,
- pub force: bool,
- pub undefined: bool,
- pub clean: bool,
- pub no_pandoc: bool,
- pub new: bool,
-}
-
-impl Options {
- pub fn new() -> Self {
- Self {
- root: PathBuf::new(),
- source: PathBuf::new(),
- build: PathBuf::new(),
- pattern: PathBuf::new(),
- file: None,
- input: String::default(),
- output: String::default(),
- verbose: 0,
- quiet: false,
- force: false,
- undefined: false,
- clean: false,
- no_pandoc: false,
- new: false,
- }
- }
-}
-
-impl TryFrom<crate::Opts> for Options {
- type Error = eyre::Error;
- fn try_from(value: crate::Opts) -> Result<Self, Self::Error> {
- let mut opts = Options::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() {
- opts.root = PathBuf::from(root).canonicalize()?;
- } else {
- opts.root = std::env::current_dir()?;
- }
-
- if let Some(source) = value.source.as_deref() {
- opts.source = PathBuf::from(source).canonicalize()?;
- } else {
- opts.source = opts.root.join("source");
- }
-
- if let Some(build) = value.build.as_deref() {
- opts.build = PathBuf::from(build).canonicalize()?;
- } else {
- opts.build = opts.root.join("build");
- }
-
- if let Some(pattern) = value.pattern.as_deref() {
- opts.pattern = PathBuf::from(pattern).canonicalize()?;
- } else {
- opts.pattern = opts.root.join("pattern");
- }
-
- if let Some(file) = value.file.as_deref() {
- opts.file = Some(PathBuf::from(file).canonicalize()?);
- }
-
- 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::{build_metafile, MetaFile, Options};
+use crate::{MetaFile, Options};
use eyre::{Result, WrapErr};
use std::{error::Error, fs, path::PathBuf};
file_path.set_extension("meta");
let file = MetaFile::build(file_path, &opts)?;
- let output = build_metafile(&file).wrap_err_with(|| test.0.to_string())?;
+ let output = file.construct().wrap_err_with(|| test.0.to_string())?;
if output == test.1 {
Ok(())