]> git.huck.website - metaforge.git/commitdiff
switch to dirnodes from walkdir
authorHuck Boles <huck@huck.website>
Sun, 14 May 2023 02:26:56 +0000 (21:26 -0500)
committerHuck Boles <huck@huck.website>
Sun, 14 May 2023 02:26:56 +0000 (21:26 -0500)
Cargo.lock
Cargo.toml
bacon.toml
benches/build_site.rs
src/builder.rs
src/main.rs
src/metafile.rs
src/options.rs
src/parser.rs

index 8afe109c17514293a61de40f107d13d088eeeb0e..68a34d0bef3afb6a3fe9cbdacd8eaccca6af0f0d 100644 (file)
@@ -596,7 +596,6 @@ dependencies = [
  "pest",
  "pest_derive",
  "pretty_assertions",
- "walkdir",
 ]
 
 [[package]]
index 6f9a9878680d8d85176e8c319f69f96828c8113c..a8716f3f7b21c7658b62f23e60ec2354bc1b34d7 100644 (file)
@@ -7,7 +7,6 @@ edition = "2021"
 clap = { version = "4", features = ["derive"] }
 pandoc = "0.8"
 color-eyre =  { version = "0.5", default-features = false }
-walkdir = "2"
 pest = "2"
 pest_derive = "2"
 
index a4c3c37f49bea017e3519804c71e797c81ba8458..7ebef15e1d458e287be9c13a0b81a9292bd80c4c 100644 (file)
@@ -51,9 +51,21 @@ command = [
 need_stdout = true
 allow_warnings = true
 
+[jobs.help]
+command = [ "cargo", "run", "--", "-h" ]
+need_stdout = true
+allow_warnings = true
+
+[jobs.very_verbose]
+command = [ "cargo", "run", "--", "-vvvv", "-r", "/home/huck/repos/metaforge/files/site/" ]
+need_stdout = true
+allow_warnings = true
+
 # You may define here keybindings that would be specific to
 # a project, for example a shortcut to launch a specific job.
 # Shortcuts to internal functions (scrolling, toggling, etc.)
 # should go in your personal global prefs.toml file instead.
 [keybindings]
 # alt-m = "job:my-job"
+alt-h = "job:help"
+alt-v = "job:very_verbose"
index 78739bd38526590f94e8ddee212686f3a53dce00..07e4ca13a028972260bee2bbc55752f4b49e1cc4 100644 (file)
@@ -1,26 +1,22 @@
 use criterion::{black_box, criterion_group, criterion_main, Criterion};
-use metaforge::{build_site, Options};
+use metaforge::Options;
 
 pub fn build_site_benchmark(c: &mut Criterion) {
     let dir = std::path::PathBuf::from("files/site")
         .canonicalize()
         .unwrap();
 
-    let opts = Options {
-        root: dir.clone(),
-        source: dir.join("source"),
-        build: dir.join("build"),
-        pattern: dir.join("pattern"),
-        verbose: 0,
-        quiet: false,
-        force: false,
-        undefined: false,
-        clean: true,
-    };
+    let mut opts = Options::new();
+    opts.root = dir.clone();
+    opts.source = dir.join("source");
+    opts.build = dir.join("build");
+    opts.pattern = dir.join("pattern");
+    opts.clean = true;
 
-    c.bench_function("build test site", |b| {
-        b.iter(|| build_site(black_box(&opts)))
-    });
+    todo!("implement with DirNode")
+    // c.bench_function("build test site", |b| {
+    //     b.iter(|| build_site(black_box(&opts)))
+    // });
 }
 
 criterion_group!(benches, build_site_benchmark);
index fa7e297b5fe5007582b5fc7ba1ffac369911eacd..7a353ed12dfa340b2a08442a18c9f14b3d796ce6 100644 (file)
@@ -10,80 +10,27 @@ use std::{
     fs,
     path::{Path, PathBuf},
 };
-use walkdir::WalkDir;
-
-pub fn build_site(opts: &Options) -> Result<()> {
-    log!(opts, "finding files", 2);
-    let files: Vec<PathBuf> = WalkDir::new(&opts.source)
-        .into_iter()
-        .filter_map(|file| {
-            if file.as_ref().ok()?.file_type().is_dir() {
-                // need to create directories in build dir
-                let path = file.unwrap().into_path();
-                let path = path.strip_prefix(&opts.source).ok()?;
-                let path = opts.build.join(path);
-                log!(opts, format!("\tcreating dir: {}", path.display()), 3);
-                std::fs::create_dir(path).ok()?;
-                // don't need them for any further operations so we filter them out
-                None
-            } else if let Ok(file) = file {
-                log!(opts, format!("\tadding file: {}", file.path().display()), 3);
-                Some(file.into_path())
-            } else {
-                None
-            }
-        })
-        .collect();
 
-    log!(opts, "building files", 2);
-    for file in files.iter() {
-        match build_metafile(file, opts) {
-            Ok(_) => continue,
-            Err(e) => {
-                if opts.force {
-                    // print a line to stderr about failure but continue with other files
-                    eprintln!("{}: {}", file.display(), e);
-                    continue;
-                } else {
-                    return Err(e.wrap_err(eyre!("{}:", file.display())));
-                }
-            }
-        }
-    }
-
-    Ok(())
-}
-
-fn build_metafile(path: &Path, opts: &Options) -> Result<()> {
-    log!(opts, format!("\t{}", path.display()), 1);
-    let file =
-        fs::read_to_string(path).wrap_err_with(|| eyre!("failed to read: {}\n", path.display()))?;
-
-    log!(opts, "\tparsing", 2);
-    let file =
-        parse_file(&file).wrap_err_with(|| eyre!("failed to parse: {}\n", path.display()))?;
-
-    let html = get_source_html(&file, opts)
-        .wrap_err_with(|| eyre!("failed converting to html: {}\n", path.display()))?;
+pub fn build_metafile(file: &MetaFile, opts: &Options) -> Result<String> {
+    let html = get_source_html(file, opts)
+        .wrap_err_with(|| eyre!("failed converting to html: {}\n", file.path.display()))?;
 
     let pattern = get_pattern("base", &file, opts).wrap_err("failed to get base pattern\n")?;
-    let mut base = parse_file(&pattern).wrap_err("failed to parse base pattern\n")?;
+    let mut base = parse_file(pattern).wrap_err("failed to parse base pattern\n")?;
 
-    base.variables = file.variables;
-    base.arrays = file.arrays;
-    base.patterns = file.patterns;
-
-    base.patterns.insert("SOURCE", &html);
+    base.merge(&file);
+    base.patterns.insert("SOURCE".to_string(), html);
 
     let output = metafile_to_string(&base, opts, Some("base"))
-        .wrap_err_with(|| eyre!("failed to build: {}\n", path.display()))?;
+        .wrap_err_with(|| eyre!("failed to build: {}\n", file.path.display()))?;
 
-    log!(opts, "\twriting", 2);
-    let dest = find_dest(path, opts)
-        .wrap_err_with(|| format!("could not find destination file: {}\n", path.display()))?;
+    Ok(output)
+}
 
+pub fn write_file(path: &Path, html: String, opts: &Options) -> Result<()> {
+    let dest = find_dest(path, opts)?;
     // want newline to end file
-    fs::write(&dest, output + "\n")
+    fs::write(&dest, html + "\n")
         .wrap_err_with(|| eyre!("could not write to: {}\n", dest.display()))?;
     Ok(())
 }
@@ -132,16 +79,21 @@ fn metafile_to_string(file: &MetaFile, opts: &Options, name: Option<&str>) -> Re
 fn get_source_html(file: &MetaFile, opts: &Options) -> Result<String> {
     log!(opts, "\tbuilding source", 2);
     let file = metafile_to_string(file, opts, Some("SOURCE")).wrap_err("failed building source")?;
-    let mut pandoc = Pandoc::new();
 
-    log!(opts, "\t\tsetting up pandoc", 3);
+    if opts.no_pandoc {
+        return Ok(file);
+    }
+
+    log!(opts, "\t\tcalling pandoc", 3);
+    log!(opts, "\t\t\tbuilding pandoc command", 4);
+    let mut pandoc = Pandoc::new();
     pandoc
         .set_input(InputKind::Pipe(file))
         .set_output(OutputKind::Pipe)
         .set_input_format(InputFormat::Markdown, vec![])
         .set_output_format(OutputFormat::Html, vec![]);
 
-    log!(opts, "\t\texecuting pandoc command", 3);
+    log!(opts, "\t\t\texecuting pandoc command", 4);
     if let Ok(PandocOutput::ToBuffer(html)) = pandoc.execute() {
         Ok(html)
     } else {
@@ -154,25 +106,31 @@ fn get_pattern(key: &str, file: &MetaFile, opts: &Options) -> Result<String> {
     // we just need to return that
     if key == "SOURCE" {
         log!(opts, "\t\t\treturning SOURCE", 4);
-        let source = file.patterns.get("SOURCE").unwrap_or(&"");
-        return Ok(source.to_string());
+        if let Some(source) = file.patterns.get("SOURCE") {
+            return Ok(source.to_string());
+        }
     }
 
     log!(opts, format!("\t\tpattern: {}", key), 3);
     // anything not defined should have a default.meta file to fall back to
-    let mut filename = file.get_pat(key).unwrap_or("default");
+    let mut filename: String;
+    if let Some(name) = file.get_pat(key) {
+        filename = name.to_string();
+    } else {
+        filename = "default".to_string()
+    }
 
     // if we're building from 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
     if key == "base" {
         log!(opts, "\t\t\treturning base", 4);
-        let pattern_path = key.to_string() + "/" + filename;
+        let pattern_path = key.to_string() + "/" + &filename;
         let mut path = opts.pattern.join(pattern_path);
         path.set_extension("meta");
 
         let base = fs::read_to_string(&path)
-            .wrap_err_with(|| eyre!("could not read: {}\n", path.display()))?;
+            .wrap_err_with(|| eyre!("base pattern does not exist: {}\n", path.display()))?;
         return Ok(base);
     }
 
@@ -182,43 +140,33 @@ fn get_pattern(key: &str, file: &MetaFile, opts: &Options) -> Result<String> {
         return Ok(String::new());
     };
 
-    // DEFAULT override for variables defined higher in chain
+    // DEFAULT override for patterns defined higher in chain
     if filename == "DEFAULT" {
         log!(opts, "\t\t\tdefault pattern", 4);
-        filename = "default";
+        filename = "default".to_string();
     }
 
     log!(opts, "\t\t\tbuilding path from key", 4);
-    let pattern_path = key.replace('.', "/") + "/" + filename;
+    let pattern_path = key.replace('.', "/") + "/" + &filename;
     let mut path = opts.pattern.join(pattern_path);
     path.set_extension("meta");
 
     log!(opts, "\t\t\tparsing file", 4);
-    let pattern = &fs::read_to_string(&path)
-        .wrap_err_with(|| eyre!("could not read: {}\n", path.display()))?;
-    let mut pattern =
-        parse_file(pattern).wrap_err_with(|| eyre!("could not parse: {}\n", path.display()))?;
+    let mut pattern = MetaFile::build(path)?;
 
     // copy over maps for expanding contained variables
-    // TODO: Make this a merge so patterns can define/override their own variables
-    pattern.variables = file.variables.clone();
-    pattern.arrays = file.arrays.clone();
-    pattern.patterns = file.patterns.clone();
+    pattern.merge(&file);
 
     log!(opts, "\t\t\tbuilding pattern", 4);
     metafile_to_string(&pattern, opts, Some(key))
 }
 
 fn find_dest(path: &Path, opts: &Options) -> Result<PathBuf> {
-    log!(opts, "\t\tfinding destination", 3);
-    let source = opts.source.to_string_lossy();
-    let build = opts.build.to_string_lossy();
-
     let path = path
         .canonicalize()
         .wrap_err_with(|| eyre!("could not get absolute path: {}\n", path.display()))?;
-    let path = path.to_string_lossy();
-    let path = path.replace(&*source, &build);
+
+    let path = opts.build.join(path.strip_prefix(&opts.source)?);
     let mut path = PathBuf::from(path);
 
     path.set_extension("html");
@@ -227,7 +175,7 @@ fn find_dest(path: &Path, opts: &Options) -> Result<PathBuf> {
 }
 
 fn expand_arrays(output: String, file: &MetaFile, name: Option<&str>) -> Result<String> {
-    let map: HashMap<&str, &[&str]> = file
+    let map: HashMap<String, &[String]> = file
         .source
         .iter()
         // filter out arrays from source vec
@@ -249,7 +197,7 @@ fn expand_arrays(output: String, file: &MetaFile, name: Option<&str>) -> Result<
                 key = array.to_string();
             }
             let value = file.get_arr(&key).unwrap_or_default();
-            (*array, value)
+            (array.to_string(), value)
         })
         .collect();
 
@@ -260,7 +208,9 @@ fn expand_arrays(output: String, file: &MetaFile, name: Option<&str>) -> Result<
         let mut str = output.clone();
         // replace each key in the file
         for (key, val) in map.iter() {
-            str = str.replace(&format!("-{{{key}}}"), val.get(i).unwrap_or(&""));
+            if let Some(value) = val.get(i) {
+                str = str.replace(&format!("-{{{key}}}"), value);
+            }
         }
         // concatenate to final file
         expanded.push_str(&str);
@@ -269,7 +219,7 @@ fn expand_arrays(output: String, file: &MetaFile, name: Option<&str>) -> Result<
     Ok(expanded)
 }
 
-fn get_max_size(map: &HashMap<&str, &[&str]>) -> usize {
+fn get_max_size(map: &HashMap<String, &[String]>) -> usize {
     let mut max = 0;
     for val in map.values() {
         if max < val.len() {
@@ -285,17 +235,12 @@ mod tests {
     fn build_options() -> Result<Options> {
         let dir = PathBuf::from("files/site").canonicalize()?;
 
-        let opts = Options {
-            root: dir.clone(),
-            source: dir.join("source"),
-            build: dir.join("build"),
-            pattern: dir.join("pattern"),
-            verbose: 0,
-            quiet: false,
-            force: false,
-            undefined: false,
-            clean: true,
-        };
+        let mut opts = Options::new();
+        opts.root = dir.clone();
+        opts.source = dir.join("source");
+        opts.build = dir.join("build");
+        opts.pattern = dir.join("pattern");
+        opts.clean = true;
 
         Ok(opts)
     }
@@ -307,4 +252,26 @@ mod tests {
         assert_eq!(find_dest(&path, &opts)?, opts.build.join("dir1/dir.html"));
         Ok(())
     }
+
+    #[test]
+    fn test_build_metafile() -> Result<()> {
+        let opts = build_options()?;
+        let path = opts.source.join("dir1/sub_dir1/deep2/deep.meta");
+        let expanded = "<html>\n<body>\nGOOD\n</body>\n</html>\n";
+        build_metafile(&MetaFile::build(path)?, &opts)?;
+        assert_eq!(
+            std::fs::read_to_string(opts.build.join("dir1/sub_dir1/deep2/deep.html"))?,
+            expanded
+        );
+        Ok(())
+    }
+
+    #[test]
+    fn test_get_pattern() -> Result<()> {
+        let opts = build_options()?;
+        let file = MetaFile::new();
+        let pat = get_pattern("header", &file, &opts)?;
+        assert_eq!(pat, "<header>HEADER</header>");
+        Ok(())
+    }
 }
index 08a863b04328b042c792f80c81a432d5998fc7ee..a23cfdfa76404003abdb62ba00e1a35260c78877 100644 (file)
@@ -1,10 +1,11 @@
+use clap::Parser;
 use color_eyre::Result;
-use metaforge::{build_site, log, parse_opts};
+use metaforge::{log, Options, Opts};
 
 fn main() -> Result<()> {
     color_eyre::install()?;
 
-    let opts = parse_opts()?;
+    let opts = Options::try_from(Opts::parse())?;
 
     log!(
         opts,
@@ -19,5 +20,5 @@ fn main() -> Result<()> {
         std::fs::create_dir(&opts.build)?;
     }
 
-    build_site(&opts)
+    todo!("implement DirNode chain")
 }
index 409b2b981c03fd5b15ed2820a0c516b2caf0b470..d1a349aa623cd04c9fd2c7ba98469e05b7f952fc 100644 (file)
@@ -1,16 +1,33 @@
+use crate::{build_metafile, parse_file, Options};
+use color_eyre::{eyre::eyre, Result};
 use std::collections::HashMap;
+use std::{fs, path::PathBuf};
 
-#[derive(Debug, Default, Clone)]
-pub struct MetaFile<'a> {
-    pub variables: HashMap<&'a str, &'a str>,
-    pub arrays: HashMap<&'a str, Vec<&'a str>>,
-    pub patterns: HashMap<&'a str, &'a str>,
-    pub source: Vec<Source<'a>>,
+#[derive(Debug, Clone)]
+pub struct MetaFile {
+    pub path: PathBuf,
+    pub variables: HashMap<String, String>,
+    pub arrays: HashMap<String, Vec<String>>,
+    pub patterns: HashMap<String, String>,
+    pub source: Vec<Source>,
 }
 
-impl<'a> MetaFile<'a> {
+impl MetaFile {
+    pub fn build(path: PathBuf) -> Result<Self> {
+        let str = fs::read_to_string(&path)?;
+        let mut metafile = MetaFile::build_from_string(str)?;
+        metafile.path = path.to_path_buf();
+        Ok(metafile)
+    }
+
+    fn build_from_string(string: String) -> Result<Self> {
+        let metafile = parse_file(string)?;
+        Ok(metafile)
+    }
+
     pub fn new() -> Self {
         Self {
+            path: PathBuf::new(),
             variables: HashMap::new(),
             arrays: HashMap::new(),
             patterns: HashMap::new(),
@@ -18,36 +35,133 @@ impl<'a> MetaFile<'a> {
         }
     }
 
-    pub fn get_var(&self, key: &str) -> Option<&str> {
-        self.variables.get(key).copied()
+    pub fn get_var(&self, key: &str) -> Option<&String> {
+        self.variables.get(key)
     }
 
-    pub fn get_arr(&self, key: &str) -> Option<&[&str]> {
-        self.arrays.get(key).map(|val| &val[..])
+    pub fn get_arr(&self, key: &str) -> Option<&[String]> {
+        self.arrays.get(key).map(|a| &a[..])
     }
 
-    pub fn get_pat(&self, key: &str) -> Option<&str> {
-        self.patterns.get(key).copied()
+    pub fn get_pat(&self, key: &str) -> Option<&String> {
+        self.patterns.get(key)
+    }
+
+    pub fn merge(&mut self, other: &Self) {
+        for (key, val) in other.variables.iter() {
+            match self.variables.get(key) {
+                Some(_) => continue,
+                None => self.variables.insert(key.to_string(), val.to_string()),
+            };
+        }
+        for (key, val) in other.arrays.iter() {
+            match self.arrays.get(key) {
+                Some(_) => continue,
+                None => self.arrays.insert(key.to_string(), val.to_vec()),
+            };
+        }
+        for (key, val) in other.patterns.iter() {
+            match self.patterns.get(key) {
+                Some(_) => continue,
+                None => self.patterns.insert(key.to_string(), val.to_string()),
+            };
+        }
     }
 }
 
 #[macro_export]
 macro_rules! source (
-    (var($s:expr)) => { Source::Sub(Substitution::Variable($s))};
-    (arr($s:expr)) => { Source::Sub(Substitution::Array($s))};
-    (pat($s:expr)) => { Source::Sub(Substitution::Pattern($s))};
-    ($s:expr) => { Source::Str($s)};
+    (var($s:expr)) => { Source::Sub(Substitution::Variable($s.to_string()))};
+    (arr($s:expr)) => { Source::Sub(Substitution::Array($s.to_string()))};
+    (pat($s:expr)) => { Source::Sub(Substitution::Pattern($s.to_string()))};
+    ($s:expr) => { Source::Str($s.to_string())};
 );
 
 #[derive(Debug, Clone, PartialEq)]
-pub enum Source<'a> {
-    Str(&'a str),
-    Sub(Substitution<'a>),
+pub enum Source {
+    Str(String),
+    Sub(Substitution),
 }
 
 #[derive(Debug, Clone, PartialEq)]
-pub enum Substitution<'a> {
-    Variable(&'a str),
-    Array(&'a str),
-    Pattern(&'a str),
+pub enum Substitution {
+    Variable(String),
+    Array(String),
+    Pattern(String),
+}
+
+pub struct DirNode<'a> {
+    path: PathBuf,
+    opts: &'a Options,
+    global: MetaFile,
+    files: Vec<MetaFile>,
+    dirs: Vec<DirNode<'a>>,
+}
+
+impl<'a> DirNode<'a> {
+    pub fn build(path: PathBuf, opts: &'a Options) -> Result<Self> {
+        assert!(path.is_dir() && path.exists());
+
+        fs::create_dir(opts.build.join(path.strip_prefix(&opts.source)?))?;
+
+        let files: Vec<MetaFile> = Vec::new();
+        let dirs: Vec<DirNode> = Vec::new();
+        let global = MetaFile::new();
+
+        Ok(Self {
+            path: path.to_path_buf(),
+            opts,
+            global,
+            files,
+            dirs,
+        })
+    }
+
+    // parses all contained files and directories and pushes
+    // parsed structures into the files and directories vectors
+    pub fn map(&mut self, global: &'a MetaFile) -> Result<()> {
+        for f in fs::read_dir(&self.path)?.into_iter() {
+            let file = f?.path();
+
+            if file.is_dir() {
+                let dir = DirNode::build(file, self.opts)?;
+                self.dirs.push(dir);
+            } else if file.file_name().and_then(|f| f.to_str()) == Some("default.meta") {
+                let mut new_global = MetaFile::build(file)?;
+                new_global.merge(global);
+                self.global = new_global;
+            } else if file.extension().and_then(|f| f.to_str()) == Some("meta") {
+                let file = MetaFile::build(file)?;
+                self.files.push(file)
+            }
+        }
+
+        Ok(())
+    }
+
+    pub fn build_files(&mut self) -> Result<()> {
+        for file in self.files.iter_mut() {
+            file.merge(&self.global);
+            if let Err(e) = build_metafile(file, self.opts) {
+                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())));
+                }
+            }
+        }
+        Ok(())
+    }
+
+    pub fn build_dir(&mut self) -> Result<()> {
+        self.build_files()?;
+
+        for dir in self.dirs.iter_mut() {
+            dir.build_dir()?;
+        }
+
+        Ok(())
+    }
 }
index 7cbaf343b09437ec41bcdf35ab30789514b25fb3..b9137f0af0133b0fec4615847c414c2a8d848682 100644 (file)
@@ -36,6 +36,9 @@ pub struct Opts {
     /// Clean build directory before building site [FALSE]
     #[arg(long, default_value_t = false)]
     clean: bool,
+    /// Don't convert markdown to html. Runs even if pandoc is not installed [FALSE]
+    #[arg(long, default_value_t = false)]
+    no_pandoc: bool,
 }
 
 #[derive(Debug, Clone, Default)]
@@ -49,6 +52,7 @@ pub struct Options {
     pub force: bool,
     pub undefined: bool,
     pub clean: bool,
+    pub no_pandoc: bool,
 }
 
 impl Options {
@@ -63,52 +67,56 @@ impl Options {
             force: false,
             undefined: false,
             clean: false,
+            no_pandoc: false,
         }
     }
 }
 
-#[macro_export]
-macro_rules! log {
-    ($opts:ident, $string:expr, $level:expr) => {
-        if $opts.verbose >= $level && !$opts.quiet {
-            println!("{}", $string);
-        }
-    };
-}
+impl TryFrom<Opts> for Options {
+    type Error = color_eyre::eyre::Error;
+    fn try_from(value: Opts) -> Result<Self, Self::Error> {
+        let mut options = Options::new();
 
-pub fn parse_opts() -> Result<Options> {
-    let opts = Opts::parse();
+        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;
 
-    let mut options = Options::new();
-    options.verbose = opts.verbose;
-    options.quiet = opts.quiet;
-    options.force = opts.force;
-    options.undefined = opts.undefined;
-    options.clean = opts.clean;
+        if let Some(root) = value.root.as_deref() {
+            options.root = PathBuf::from(root).canonicalize()?;
+        } else {
+            options.root = std::env::current_dir()?;
+        }
 
-    if let Some(root) = opts.root.as_deref() {
-        options.root = PathBuf::from(root).canonicalize()?;
-    } else {
-        options.root = std::env::current_dir()?;
-    }
+        if let Some(source) = value.source.as_deref() {
+            options.source = PathBuf::from(source).canonicalize()?;
+        } else {
+            options.source = options.root.join("source");
+        }
 
-    if let Some(source) = opts.source.as_deref() {
-        options.source = PathBuf::from(source).canonicalize()?;
-    } else {
-        options.source = options.root.join("source");
-    }
+        if let Some(build) = value.build.as_deref() {
+            options.build = PathBuf::from(build).canonicalize()?;
+        } else {
+            options.build = options.root.join("build");
+        }
 
-    if let Some(build) = opts.build.as_deref() {
-        options.build = PathBuf::from(build).canonicalize()?;
-    } else {
-        options.build = options.root.join("build");
-    }
+        if let Some(pattern) = value.pattern.as_deref() {
+            options.pattern = PathBuf::from(pattern).canonicalize()?;
+        } else {
+            options.pattern = options.root.join("pattern");
+        }
 
-    if let Some(pattern) = opts.pattern.as_deref() {
-        options.pattern = PathBuf::from(pattern).canonicalize()?;
-    } else {
-        options.pattern = options.root.join("pattern");
+        Ok(options)
     }
+}
 
-    Ok(options)
+#[macro_export]
+macro_rules! log {
+    ($opts:ident, $string:expr, $level:expr) => {
+        if $opts.verbose >= $level && !$opts.quiet {
+            println!("{}", $string);
+        }
+    };
 }
index 11d5d19d22e4906f3ab1ff8485213f8529ec8070..281e0dd31b9ab3629558b26e9f7b582e10ea0841 100644 (file)
@@ -10,13 +10,14 @@ use std::collections::HashMap;
 #[grammar = "meta.pest"]
 pub struct MetaParser;
 
-pub fn parse_file(file: &str) -> Result<MetaFile> {
-    let meta_source = MetaParser::parse(Rule::file, file)
+pub fn parse_file<'a>(file: String) -> Result<MetaFile> {
+    let meta_source = MetaParser::parse(Rule::file, &file)
         .wrap_err("parser error")?
         .next()
         .unwrap();
 
-    Ok(parse_pair(meta_source))
+    let metafile = parse_pair(meta_source);
+    Ok(metafile)
 }
 
 fn parse_pair(pair: Pair<Rule>) -> MetaFile {
@@ -41,23 +42,23 @@ fn parse_pair(pair: Pair<Rule>) -> MetaFile {
     meta_file
 }
 
-fn parse_defs(pairs: Pairs<Rule>) -> HashMap<&'_ str, &'_ str> {
+fn parse_defs(pairs: Pairs<Rule>) -> HashMap<String, String> {
     let mut map = HashMap::new();
     for pair in pairs {
         if Rule::assign == pair.as_rule() {
             let (key, val) = parse_assign(pair);
-            map.insert(key, val);
+            map.insert(key.to_string(), val.to_string());
         }
     }
     map
 }
 
-fn parse_array_defs(pairs: Pairs<Rule>) -> HashMap<&str, Vec<&str>> {
+fn parse_array_defs(pairs: Pairs<Rule>) -> HashMap<String, Vec<String>> {
     let mut map = HashMap::new();
     for pair in pairs {
         if Rule::assign == pair.as_rule() {
             let (key, val) = parse_assign_array(pair);
-            map.insert(key, val);
+            map.insert(key.to_string(), val);
         }
     }
     map
@@ -70,7 +71,7 @@ fn parse_source(pairs: Pairs<Rule>) -> Vec<Source> {
             Rule::var_sub => vec.push(source!(var(parse_sub(pair)))),
             Rule::arr_sub => vec.push(source!(arr(parse_sub(pair)))),
             Rule::pat_sub => vec.push(source!(pat(parse_sub(pair)))),
-            Rule::char_seq => vec.push(source!(pair.as_str())),
+            Rule::char_seq => vec.push(source!(pair)),
             // anything that isn't a substitution is a char_seq inside source
             _ => unreachable!(),
         }
@@ -79,7 +80,7 @@ fn parse_source(pairs: Pairs<Rule>) -> Vec<Source> {
     vec
 }
 
-fn parse_sub(pair: Pair<Rule>) -> &'_ str {
+fn parse_sub(pair: Pair<Rule>) -> &str {
     match pair.as_rule() {
         Rule::var_sub | Rule::arr_sub | Rule::pat_sub => {
             let str = pair.as_str();
@@ -97,7 +98,7 @@ fn parse_sub(pair: Pair<Rule>) -> &'_ str {
     }
 }
 
-fn parse_assign(pair: Pair<Rule>) -> (&'_ str, &'_ str) {
+fn parse_assign(pair: Pair<Rule>) -> (&str, &str) {
     let mut key = "";
     let mut val = "";
 
@@ -122,7 +123,7 @@ fn parse_assign(pair: Pair<Rule>) -> (&'_ str, &'_ str) {
     (key, val)
 }
 
-fn parse_assign_array(pair: Pair<Rule>) -> (&str, Vec<&str>) {
+fn parse_assign_array(pair: Pair<Rule>) -> (String, Vec<String>) {
     let mut key = "";
     let mut val = Vec::default();
 
@@ -135,11 +136,11 @@ fn parse_assign_array(pair: Pair<Rule>) -> (&str, Vec<&str>) {
         }
     }
 
-    (key, val)
+    (key.to_string(), val)
 }
 
-fn parse_array(pairs: Pairs<Rule>) -> Vec<&str> {
-    let mut vec: Vec<&str> = Vec::default();
+fn parse_array(pairs: Pairs<Rule>) -> Vec<String> {
+    let mut vec: Vec<String> = Vec::default();
 
     for pair in pairs {
         if Rule::string == pair.as_rule() {
@@ -147,7 +148,7 @@ fn parse_array(pairs: Pairs<Rule>) -> Vec<&str> {
             // remove surrounding quotes from values
             // see parse_assign() for reasoning
             let val = &tmp[1..tmp.len() - 1];
-            vec.push(val);
+            vec.push(val.to_string());
         }
     }
 
@@ -160,7 +161,7 @@ mod tests {
 
     macro_rules! test_str (
         ($s: expr) => {
-            let str = $s;
+            let str = $s.to_string();
             parse_file(str).unwrap();
         };
     );