use std::{
borrow::Cow,
collections::BTreeMap,
fmt, mem,
path::{Path, PathBuf},
str::FromStr,
};
use serde::{Deserialize, Serialize};
use crate::error::Result;
#[allow(clippy::exhaustive_structs)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct Value<T> {
pub val: T,
#[serde(skip)]
pub definition: Option<Definition>,
}
impl Value<String> {
pub(crate) fn parse<T: FromStr>(self) -> Result<Value<T>, T::Err> {
Ok(Value { val: self.val.parse()?, definition: self.definition })
}
pub(crate) fn resolve_as_program_path(&self, current_dir: &Path) -> Cow<'_, Path> {
match &self.definition {
Some(def)
if !Path::new(&self.val).is_absolute()
&& (self.val.contains('/') || self.val.contains('\\')) =>
{
def.root(current_dir).join(&self.val).into()
}
_ => Path::new(&self.val).into(),
}
}
pub(crate) fn resolve_as_path(&self, current_dir: &Path) -> Cow<'_, Path> {
match &self.definition {
Some(def) if !Path::new(&self.val).is_absolute() => {
def.root(current_dir).join(&self.val).into()
}
_ => Path::new(&self.val).into(),
}
}
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum Definition {
Path(PathBuf),
Environment(Cow<'static, str>),
Cli(Option<PathBuf>),
}
impl Definition {
pub(crate) fn root<'a>(&'a self, current_dir: &'a Path) -> &'a Path {
match self {
Definition::Path(p) | Definition::Cli(Some(p)) => p.parent().unwrap().parent().unwrap(),
Definition::Environment(_) | Definition::Cli(None) => current_dir,
}
}
pub(crate) fn root_opt<'a>(&'a self, current_dir: Option<&'a Path>) -> Option<&'a Path> {
match self {
Definition::Path(p) | Definition::Cli(Some(p)) => {
Some(p.parent().unwrap().parent().unwrap())
}
Definition::Environment(_) | Definition::Cli(None) => current_dir,
}
}
}
impl fmt::Display for Definition {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Definition::Path(p) | Definition::Cli(Some(p)) => p.display().fmt(f),
Definition::Environment(key) => write!(f, "environment variable `{key}`"),
Definition::Cli(None) => write!(f, "--config cli option"),
}
}
}
impl PartialEq for Definition {
fn eq(&self, other: &Definition) -> bool {
mem::discriminant(self) == mem::discriminant(other)
}
}
pub(crate) trait SetPath {
fn set_path(&mut self, path: &Path);
}
impl<T: SetPath> SetPath for Option<T> {
fn set_path(&mut self, path: &Path) {
if let Some(v) = self {
v.set_path(path);
}
}
}
impl<T: SetPath> SetPath for Vec<T> {
fn set_path(&mut self, path: &Path) {
for v in self {
v.set_path(path);
}
}
}
impl<T: SetPath> SetPath for BTreeMap<String, T> {
fn set_path(&mut self, path: &Path) {
for v in self.values_mut() {
v.set_path(path);
}
}
}
impl<T> SetPath for Value<T> {
fn set_path(&mut self, path: &Path) {
self.definition = Some(Definition::Path(path.to_owned()));
}
}