-use crate::{log, parse_file, MetaFile, Options, Source, Substitution};
-use color_eyre::{
- eyre::bail,
- eyre::{eyre, WrapErr},
- Result,
-};
+use crate::{log, parse_file, MetaFile, Options, Src, Sub};
+use color_eyre::{eyre::bail, Result};
use pandoc::{InputFormat, InputKind, OutputFormat, OutputKind, Pandoc, PandocOutput};
use std::{
collections::HashMap,
path::{Path, PathBuf},
};
-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()))?;
+pub fn build_metafile(file: &MetaFile) -> Result<String> {
+ let html = get_source_html(file, file.opts)?;
- 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 pattern = get_pattern("base", &file)?;
+ let mut base = parse_file(pattern, file.opts)?;
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", file.path.display()))?;
+ let output = metafile_to_string(&base)?;
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, html + "\n")
- .wrap_err_with(|| eyre!("could not write to: {}\n", dest.display()))?;
+ fs::write(&dest, html + "\n")?;
Ok(())
}
-fn metafile_to_string(file: &MetaFile, opts: &Options, name: Option<&str>) -> Result<String> {
+fn metafile_to_string(file: &MetaFile) -> Result<String> {
let mut output = String::default();
let mut arrays = false;
for section in file.source.iter() {
match section {
// concatenate any char sequences
- Source::Str(str) => {
+ Src::Str(str) => {
output.push_str(str);
}
// expand all variables and recursively expand patterns
- Source::Sub(sub) => {
+ Src::Sub(sub) => {
let expanded = match sub {
- Substitution::Variable(key) => file
- .get_var(key)
- // blank and default dont need to be processed
- .filter(|val| *val != "BLANK" && *val != "DEFAULT")
- .map(|val| val.to_string())
- .unwrap_or_default(),
- Substitution::Pattern(key) => get_pattern(key, file, opts)
- .wrap_err_with(|| eyre!("could not find pattern for: {}\n", key))?,
- // comments have already been removed at this point,
- // so we use them to mark keys for array substitution
- Substitution::Array(key) => {
+ Sub::Var(key) => get_variable(key, file)?,
+ Sub::Pat(key) => get_pattern(key, file)?,
+ Sub::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(&format!("\n{}\n", expanded));
+ output.push_str(&expanded);
}
}
}
if arrays {
- log!(opts, "\t\t\texpanding arrays", 4);
- expand_arrays(output, file, name)
+ log!(file.opts, "\t\t\texpanding arrays", 4);
+ expand_arrays(output, file)
} else {
Ok(output)
}
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 file = metafile_to_string(file)?;
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_input_format(InputFormat::Markdown, vec![])
.set_output_format(OutputFormat::Html, vec![]);
- log!(opts, "\t\t\texecuting pandoc command", 4);
if let Ok(PandocOutput::ToBuffer(html)) = pandoc.execute() {
Ok(html)
} else {
}
}
-fn get_pattern(key: &str, file: &MetaFile, opts: &Options) -> Result<String> {
+fn get_pattern(key: &str, file: &MetaFile) -> Result<String> {
// SOURCE is already expanded in the initial build_metafile() call
// we just need to return that
if key == "SOURCE" {
- log!(opts, "\t\t\treturning SOURCE", 4);
+ log!(file.opts, "\t\t\treturning SOURCE", 4);
if let Some(source) = file.patterns.get("SOURCE") {
return Ok(source.to_string());
}
}
- log!(opts, format!("\t\tpattern: {}", key), 3);
+ log!(file.opts, format!("\t\tpattern: {}", key), 3);
// anything not defined should have a default.meta file to fall back to
let mut filename: String;
if let Some(name) = file.get_pat(key) {
// 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);
+ log!(file.opts, "\t\t\treturning base", 4);
let pattern_path = key.to_string() + "/" + &filename;
- let mut path = opts.pattern.join(pattern_path);
+ let mut path = file.opts.pattern.join(pattern_path);
path.set_extension("meta");
- let base = fs::read_to_string(&path)
- .wrap_err_with(|| eyre!("base pattern does not exist: {}\n", path.display()))?;
+ let base = fs::read_to_string(&path)?;
return Ok(base);
}
// BLANK returns nothing, so no more processing needs to be done
if filename == "BLANK" {
- log!(opts, format!("\t\t\treturning blank: {}", key), 4);
return Ok(String::new());
};
// DEFAULT override for patterns defined higher in chain
if filename == "DEFAULT" {
- log!(opts, "\t\t\tdefault pattern", 4);
filename = "default".to_string();
}
- log!(opts, "\t\t\tbuilding path from key", 4);
let pattern_path = key.replace('.', "/") + "/" + &filename;
- let mut path = opts.pattern.join(pattern_path);
+ let mut path = file.opts.pattern.join(pattern_path);
path.set_extension("meta");
- log!(opts, "\t\t\tparsing file", 4);
- let mut pattern = MetaFile::build(path)?;
+ let mut pattern = MetaFile::build(path, file.opts)?;
// copy over maps for expanding contained variables
pattern.merge(&file);
- log!(opts, "\t\t\tbuilding pattern", 4);
- metafile_to_string(&pattern, opts, Some(key))
+ metafile_to_string(&pattern)
+}
+
+fn get_variable(key: &str, file: &MetaFile) -> Result<String> {
+ todo!()
}
fn find_dest(path: &Path, opts: &Options) -> Result<PathBuf> {
- let path = path
- .canonicalize()
- .wrap_err_with(|| eyre!("could not get absolute path: {}\n", path.display()))?;
+ let path = path.canonicalize()?;
let path = opts.build.join(path.strip_prefix(&opts.source)?);
let mut path = PathBuf::from(path);
Ok(path)
}
-fn expand_arrays(output: String, file: &MetaFile, name: Option<&str>) -> Result<String> {
+fn expand_arrays(input: String, file: &MetaFile) -> Result<String> {
let map: HashMap<String, &[String]> = file
.source
.iter()
// filter out arrays from source vec
- .filter_map(|section| {
- if let Source::Sub(Substitution::Array(array)) = section {
+ .filter_map(|x| {
+ if let Src::Sub(Sub::Arr(array)) = x {
Some(array)
} else {
None
}
})
// make a hash map of [keys in source] -> [defined arrays]
- .map(|array| {
- let key: String;
+ .map(|key| {
+ let y: String;
// concat array to pattern name to get key in HashMap
- if let Some(name) = name {
- key = name.to_owned() + "." + array;
+ let name = file.name().unwrap();
+ let long_key = name + "." + key;
+
+ let value: &[String];
+ if let Some(val) = file.get_arr(&long_key) {
+ value = val;
+ } else if let Some(val) = file.get_arr(key) {
+ value = val;
+ } else if file.opts.undefined {
+ panic!("undefined array called: {}, {}", key, long_key);
} else {
- // keys for arrays in this file don't have a preceding pattern
- key = array.to_string();
+ value = &[];
}
- let value = file.get_arr(&key).unwrap_or_default();
- (array.to_string(), value)
+
+ (key.to_string(), value)
})
.collect();
- let mut expanded = String::new();
// loop to duplicate the output template for each array member
+ let mut expanded = String::new();
for i in 0..get_max_size(&map) {
// get a fresh copy of the file
- let mut str = output.clone();
+ let mut str = input.clone();
// replace each key in the file
for (key, val) in map.iter() {
if let Some(value) = val.get(i) {
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)?;
+ build_metafile(&MetaFile::build(path, &opts)?)?;
assert_eq!(
std::fs::read_to_string(opts.build.join("dir1/sub_dir1/deep2/deep.html"))?,
expanded
#[test]
fn test_get_pattern() -> Result<()> {
let opts = build_options()?;
- let file = MetaFile::new();
- let pat = get_pattern("header", &file, &opts)?;
+ let file = MetaFile::new(&opts);
+ let pat = get_pattern("header", &file)?;
assert_eq!(pat, "<header>HEADER</header>");
Ok(())
}
use std::{fs, path::PathBuf};
#[derive(Debug, Clone)]
-pub struct MetaFile {
+pub struct MetaFile<'a> {
+ pub opts: &'a Options,
pub path: PathBuf,
+ pub header: HashMap<String, String>,
pub variables: HashMap<String, String>,
pub arrays: HashMap<String, Vec<String>>,
pub patterns: HashMap<String, String>,
- pub source: Vec<Source>,
+ pub source: Vec<Src>,
}
-impl MetaFile {
- pub fn build(path: PathBuf) -> Result<Self> {
+impl<'a> MetaFile<'a> {
+ pub fn build(path: PathBuf, opts: &'a Options) -> Result<Self> {
let str = fs::read_to_string(&path)?;
- let mut metafile = MetaFile::build_from_string(str)?;
+ let mut metafile = parse_file(str, opts)?;
metafile.path = path.to_path_buf();
+ metafile.opts = opts;
Ok(metafile)
}
- fn build_from_string(string: String) -> Result<Self> {
- let metafile = parse_file(string)?;
- Ok(metafile)
- }
-
- pub fn new() -> Self {
+ pub fn new(opts: &'a Options) -> Self {
Self {
+ opts,
path: PathBuf::new(),
+ header: HashMap::new(),
variables: HashMap::new(),
arrays: HashMap::new(),
patterns: HashMap::new(),
}
}
+ pub fn name(&self) -> Result<String> {
+ if self.path.starts_with(&self.opts.source) {
+ let name = self.path.strip_prefix(&self.opts.source)?;
+ let name = name.to_string_lossy().to_string().replace('/', ".");
+ Ok(name)
+ } else {
+ let name = self.path.strip_prefix(&self.opts.pattern)?;
+ let name = name.to_string_lossy().to_string().replace('/', ".");
+ Ok(name)
+ }
+ }
+
pub fn get_var(&self, key: &str) -> Option<&String> {
self.variables.get(key)
}
#[macro_export]
macro_rules! source (
- (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())};
+ (var($s:expr)) => { Src::Sub(Sub::Var($s.to_string()))};
+ (arr($s:expr)) => { Src::Sub(Sub::Arr($s.to_string()))};
+ (pat($s:expr)) => { Src::Sub(Sub::Pat($s.to_string()))};
+ ($s:expr) => { Src::Str($s.to_string())};
);
#[derive(Debug, Clone, PartialEq)]
-pub enum Source {
+pub enum Src {
Str(String),
- Sub(Substitution),
+ Sub(Sub),
}
#[derive(Debug, Clone, PartialEq)]
-pub enum Substitution {
- Variable(String),
- Array(String),
- Pattern(String),
+pub enum Sub {
+ Var(String),
+ Arr(String),
+ Pat(String),
}
pub struct DirNode<'a> {
path: PathBuf,
opts: &'a Options,
- global: MetaFile,
- files: Vec<MetaFile>,
+ global: MetaFile<'a>,
+ files: Vec<MetaFile<'a>>,
dirs: Vec<DirNode<'a>>,
}
let files: Vec<MetaFile> = Vec::new();
let dirs: Vec<DirNode> = Vec::new();
- let global = MetaFile::new();
+ let global = MetaFile::new(opts);
Ok(Self {
path: path.to_path_buf(),
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)?;
+ let mut new_global = MetaFile::build(file, self.opts)?;
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)?;
+ let file = MetaFile::build(file, self.opts)?;
self.files.push(file)
}
}
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 let Err(e) = build_metafile(file) {
if self.opts.force {
// print a line to stderr about failure but continue with other files
eprintln!("ignoring {}: {}", file.path.display(), e);