From: Huck Boles Date: Sun, 14 May 2023 02:26:56 +0000 (-0500) Subject: switch to dirnodes from walkdir X-Git-Url: https://git.huck.website/?a=commitdiff_plain;h=cec5ba3f1750bb61846de383aeff785ff8471239;p=metaforge.git switch to dirnodes from walkdir --- diff --git a/Cargo.lock b/Cargo.lock index 8afe109..68a34d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -596,7 +596,6 @@ dependencies = [ "pest", "pest_derive", "pretty_assertions", - "walkdir", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 6f9a987..a8716f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/bacon.toml b/bacon.toml index a4c3c37..7ebef15 100644 --- a/bacon.toml +++ b/bacon.toml @@ -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" diff --git a/benches/build_site.rs b/benches/build_site.rs index 78739bd..07e4ca1 100644 --- a/benches/build_site.rs +++ b/benches/build_site.rs @@ -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); diff --git a/src/builder.rs b/src/builder.rs index fa7e297..7a353ed 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -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 = 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 { + 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 { 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 { // 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 { 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 { - 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 { } fn expand_arrays(output: String, file: &MetaFile, name: Option<&str>) -> Result { - let map: HashMap<&str, &[&str]> = file + let map: HashMap = 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) -> usize { let mut max = 0; for val in map.values() { if max < val.len() { @@ -285,17 +235,12 @@ mod tests { fn build_options() -> Result { 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 = "\n\nGOOD\n\n\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
"); + Ok(()) + } } diff --git a/src/main.rs b/src/main.rs index 08a863b..a23cfdf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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") } diff --git a/src/metafile.rs b/src/metafile.rs index 409b2b9..d1a349a 100644 --- a/src/metafile.rs +++ b/src/metafile.rs @@ -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>, +#[derive(Debug, Clone)] +pub struct MetaFile { + pub path: PathBuf, + pub variables: HashMap, + pub arrays: HashMap>, + pub patterns: HashMap, + pub source: Vec, } -impl<'a> MetaFile<'a> { +impl MetaFile { + pub fn build(path: PathBuf) -> Result { + 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 { + 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, + dirs: Vec>, +} + +impl<'a> DirNode<'a> { + pub fn build(path: PathBuf, opts: &'a Options) -> Result { + assert!(path.is_dir() && path.exists()); + + fs::create_dir(opts.build.join(path.strip_prefix(&opts.source)?))?; + + let files: Vec = Vec::new(); + let dirs: Vec = 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(()) + } } diff --git a/src/options.rs b/src/options.rs index 7cbaf34..b9137f0 100644 --- a/src/options.rs +++ b/src/options.rs @@ -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 for Options { + type Error = color_eyre::eyre::Error; + fn try_from(value: Opts) -> Result { + let mut options = Options::new(); -pub fn parse_opts() -> Result { - 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); + } + }; } diff --git a/src/parser.rs b/src/parser.rs index 11d5d19..281e0dd 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -10,13 +10,14 @@ use std::collections::HashMap; #[grammar = "meta.pest"] pub struct MetaParser; -pub fn parse_file(file: &str) -> Result { - let meta_source = MetaParser::parse(Rule::file, file) +pub fn parse_file<'a>(file: String) -> Result { + 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) -> MetaFile { @@ -41,23 +42,23 @@ fn parse_pair(pair: Pair) -> MetaFile { meta_file } -fn parse_defs(pairs: Pairs) -> HashMap<&'_ str, &'_ str> { +fn parse_defs(pairs: Pairs) -> HashMap { 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) -> HashMap<&str, Vec<&str>> { +fn parse_array_defs(pairs: Pairs) -> HashMap> { 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) -> Vec { 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) -> Vec { vec } -fn parse_sub(pair: Pair) -> &'_ str { +fn parse_sub(pair: Pair) -> &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) -> &'_ str { } } -fn parse_assign(pair: Pair) -> (&'_ str, &'_ str) { +fn parse_assign(pair: Pair) -> (&str, &str) { let mut key = ""; let mut val = ""; @@ -122,7 +123,7 @@ fn parse_assign(pair: Pair) -> (&'_ str, &'_ str) { (key, val) } -fn parse_assign_array(pair: Pair) -> (&str, Vec<&str>) { +fn parse_assign_array(pair: Pair) -> (String, Vec) { let mut key = ""; let mut val = Vec::default(); @@ -135,11 +136,11 @@ fn parse_assign_array(pair: Pair) -> (&str, Vec<&str>) { } } - (key, val) + (key.to_string(), val) } -fn parse_array(pairs: Pairs) -> Vec<&str> { - let mut vec: Vec<&str> = Vec::default(); +fn parse_array(pairs: Pairs) -> Vec { + let mut vec: Vec = Vec::default(); for pair in pairs { if Rule::string == pair.as_rule() { @@ -147,7 +148,7 @@ fn parse_array(pairs: Pairs) -> 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(); }; );