use super::{GlobalContext, StringList, Value};
use regex::Regex;
use serde::{Deserialize, de::Error};
use std::path::PathBuf;
#[derive(Debug, Deserialize, PartialEq, Clone)]
#[serde(transparent)]
pub struct ConfigRelativePath(Value<String>);
impl ConfigRelativePath {
pub fn new(path: Value<String>) -> ConfigRelativePath {
ConfigRelativePath(path)
}
pub fn value(&self) -> &Value<String> {
&self.0
}
pub fn raw_value(&self) -> &str {
&self.0.val
}
pub fn resolve_path(&self, gctx: &GlobalContext) -> PathBuf {
self.0.definition.root(gctx.cwd()).join(&self.0.val)
}
pub fn resolve_templated_path(
&self,
gctx: &GlobalContext,
replacements: impl IntoIterator<Item = (impl AsRef<str>, impl AsRef<str>)>,
) -> Result<PathBuf, ResolveTemplateError> {
let mut value = self.0.val.clone();
for (from, to) in replacements {
value = value.replace(from.as_ref(), to.as_ref());
}
let re = Regex::new(r"\{(.*)\}").unwrap();
if let Some(caps) = re.captures(&value) {
return Err(ResolveTemplateError::UnexpectedVariable {
variable: caps[1].to_string(),
raw_template: self.0.val.clone(),
});
};
if value.contains("{") {
return Err(ResolveTemplateError::UnexpectedBracket {
bracket_type: BracketType::Opening,
raw_template: self.0.val.clone(),
});
}
if value.contains("}") {
return Err(ResolveTemplateError::UnexpectedBracket {
bracket_type: BracketType::Closing,
raw_template: self.0.val.clone(),
});
}
Ok(self.0.definition.root(gctx.cwd()).join(&value))
}
pub fn resolve_program(&self, gctx: &GlobalContext) -> PathBuf {
gctx.string_to_path(&self.0.val, &self.0.definition)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct PathAndArgs {
pub path: ConfigRelativePath,
pub args: Vec<String>,
}
impl<'de> serde::Deserialize<'de> for PathAndArgs {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let vsl = Value::<StringList>::deserialize(deserializer)?;
let mut strings = vsl.val.0;
if strings.is_empty() {
return Err(D::Error::invalid_length(0, &"at least one element"));
}
let first = strings.remove(0);
let crp = Value {
val: first,
definition: vsl.definition,
};
Ok(PathAndArgs {
path: ConfigRelativePath(crp),
args: strings,
})
}
}
impl PathAndArgs {
pub fn from_whitespace_separated_string(p: &Value<String>) -> PathAndArgs {
let mut iter = p.val.split_ascii_whitespace().map(str::to_string);
let val = iter.next().unwrap_or_default();
let args = iter.collect();
let crp = Value {
val,
definition: p.definition.clone(),
};
PathAndArgs {
path: ConfigRelativePath(crp),
args,
}
}
}
#[derive(Debug)]
pub enum ResolveTemplateError {
UnexpectedVariable {
variable: String,
raw_template: String,
},
UnexpectedBracket {
bracket_type: BracketType,
raw_template: String,
},
}
#[derive(Debug)]
pub enum BracketType {
Opening,
Closing,
}