#[macro_use]
extern crate serde_derive;
use std::collections::BTreeMap;
use std::{
env, fs,
path::{Path, PathBuf},
};
#[derive(Debug)]
pub struct Dir(PathBuf);
impl Dir {
pub fn get_template(&self, path: PathBuf) -> PathBuf {
if path.exists() {
return path;
}
let template = self.0.join(path);
if template.exists() {
template
} else {
panic!("template not found in directory {:?}", self.0)
}
}
}
impl From<Option<&str>> for Dir {
fn from(p: Option<&str>) -> Self {
let root = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
Dir(p.map_or_else(|| root.join(DEFAULT_DIR), |v| root.join(v)))
}
}
#[derive(Debug, PartialEq)]
pub enum PrintConfig {
All,
Ast,
Code,
None,
}
impl From<Option<&str>> for PrintConfig {
fn from(s: Option<&str>) -> Self {
match s {
Some("all") => PrintConfig::All,
Some("ast") => PrintConfig::Ast,
Some("code") => PrintConfig::Code,
_ => PrintConfig::None,
}
}
}
#[derive(Debug)]
pub struct Config<'a> {
dir: Dir,
alias: BTreeMap<&'a str, &'a str>,
pub print_override: PrintConfig,
pub debug: PrintOption<'a>,
}
impl<'a> Config<'a> {
pub fn new(s: &str) -> Config {
let raw: RawConfig =
toml::from_str(&s).expect(&format!("invalid TOML in {}", CONFIG_FILE_NAME));
let (dir, print) = raw.main.map(|x| (x.dir, x.debug)).unwrap_or((None, None));
Config {
dir: Dir::from(dir),
print_override: PrintConfig::from(print),
debug: raw.debug.unwrap_or_default(),
alias: raw.partials.unwrap_or(BTreeMap::new()),
}
}
pub fn get_dir(&self) -> &PathBuf {
&self.dir.0
}
pub fn get_template(&self, ident: &str) -> (PathBuf, String) {
let path = self.dir.get_template(PathBuf::from(ident));
let src = get_source(path.as_path());
(path, src)
}
pub fn get_partial(&self, parent: &Path, ident: &str) -> (PathBuf, String) {
let path = self.resolve_partial(parent, ident);
let src = get_source(path.as_path());
(path, src)
}
pub fn resolve_partial(&self, parent: &Path, ident: &str) -> PathBuf {
let mut name = None;
for (k, v) in &self.alias {
if ident.starts_with(k) {
name = Some(PathBuf::from([v, &ident[k.len()..]].join("")));
break;
}
}
let (mut buf, is_alias) = name.map_or((PathBuf::from(ident), false), |s| (s, true));
if buf.extension().is_none() {
if let Some(ext) = parent.extension() {
buf = buf.with_extension(ext);
}
};
if is_alias {
self.dir
.get_template(buf)
.canonicalize()
.expect("valid path to partial")
} else {
let mut parent = parent.to_owned();
parent.pop();
parent.push(buf);
parent.canonicalize().expect("valid path to partial")
}
}
}
#[derive(Deserialize)]
struct RawConfig<'a> {
#[serde(borrow)]
main: Option<Main<'a>>,
#[serde(borrow)]
debug: Option<PrintOption<'a>>,
#[serde(borrow)]
partials: Option<BTreeMap<&'a str, &'a str>>,
}
#[derive(Deserialize)]
struct Main<'a> {
#[serde(borrow)]
dir: Option<&'a str>,
#[serde(borrow)]
debug: Option<&'a str>,
}
#[derive(Debug, Deserialize)]
pub struct PrintOption<'a> {
#[serde(borrow)]
pub theme: Option<&'a str>,
pub number_line: Option<bool>,
}
impl<'a> Default for PrintOption<'a> {
fn default() -> Self {
Self {
theme: None,
number_line: None,
}
}
}
pub fn read_config_file() -> String {
let filename = config_file_path();
if filename.exists() {
fs::read_to_string(&filename)
.expect(&format!("unable to read {}", filename.to_str().unwrap()))
} else {
"".to_string()
}
}
#[inline]
pub fn config_file_path() -> PathBuf {
PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()).join(CONFIG_FILE_NAME)
}
fn get_source(path: &Path) -> String {
match fs::read_to_string(path) {
Err(_) => panic!("unable to open template file '{:?}'", path),
Ok(mut source) => match source
.as_bytes()
.iter()
.enumerate()
.rev()
.find_map(|(j, x)| {
if x.is_ascii_whitespace() {
None
} else {
Some(j)
}
}) {
Some(j) => {
source.drain(j + 1..);
source
}
None => source,
},
}
}
static CONFIG_FILE_NAME: &str = "wearte.toml";
static DEFAULT_DIR: &str = "templates";