use std::fmt;
use std::borrow::Cow;
use std::path::{Path, PathBuf};
use std::panic::Location;
use crate::Profile;
#[derive(Debug, Clone)]
pub struct Metadata {
pub name: Cow<'static, str>,
pub source: Option<Source>,
pub provide_location: Option<&'static Location<'static>>,
interpolater: Box<dyn Interpolator>,
}
impl Metadata {
#[inline(always)]
pub fn from<N, S>(name: N, source: S) -> Self
where N: Into<Cow<'static, str>>, S: Into<Source>
{
Metadata::named(name).source(source)
}
#[inline]
pub fn named<T: Into<Cow<'static, str>>>(name: T) -> Self {
Metadata { name: name.into(), ..Metadata::default() }
}
#[inline(always)]
pub fn source<S: Into<Source>>(mut self, source: S) -> Self {
self.source = Some(source.into());
self
}
#[inline(always)]
pub fn interpolater<I: Clone + Send + Sync + 'static>(mut self, f: I) -> Self
where I: Fn(&Profile, &[&str]) -> String
{
self.interpolater = Box::new(f);
self
}
pub fn interpolate<K: AsRef<str>>(&self, profile: &Profile, keys: &[K]) -> String {
let keys: Vec<_> = keys.iter().map(|k| k.as_ref()).collect();
(self.interpolater)(profile, &keys)
}
}
impl PartialEq for Metadata {
fn eq(&self, other: &Self) -> bool {
self.name == other.name && self.source == other.source
}
}
impl Default for Metadata {
fn default() -> Self {
Self {
name: "Default".into(),
source: None,
provide_location: None,
interpolater: Box::new(default_interpolater),
}
}
}
#[non_exhaustive]
#[derive(PartialEq, Debug, Clone)]
pub enum Source {
File(PathBuf),
Code(&'static Location<'static>),
Custom(String),
}
impl Source {
pub fn file_path(&self) -> Option<&Path> {
match self {
Source::File(ref p) => Some(p),
_ => None,
}
}
pub fn code_location(&self) -> Option<&'static Location<'static>> {
match self {
Source::Code(s) => Some(s),
_ => None
}
}
pub fn custom(&self) -> Option<&str> {
match self {
Source::Custom(ref c) => Some(c),
_ => None,
}
}
}
impl fmt::Display for Source {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Source::File(p) => {
use {std::env::current_dir, crate::util::diff_paths};
match current_dir().ok().and_then(|cwd| diff_paths(p, &cwd)) {
Some(r) if r.iter().count() < p.iter().count() => r.display().fmt(f),
Some(_) | None => p.display().fmt(f)
}
}
Source::Code(l) => l.fmt(f),
Source::Custom(c) => c.fmt(f),
}
}
}
impl From<&Path> for Source {
fn from(path: &Path) -> Source {
Source::File(path.into())
}
}
impl From<&'static Location<'static>> for Source {
fn from(location: &'static Location<'static>) -> Source {
Source::Code(location)
}
}
impl From<&str> for Source {
fn from(string: &str) -> Source {
Source::Custom(string.into())
}
}
impl From<String> for Source {
fn from(string: String) -> Source {
Source::Custom(string)
}
}
crate::util::cloneable_fn_trait!(
Interpolator: Fn(&Profile, &[&str]) -> String + Send + Sync + 'static
);
fn default_interpolater(profile: &Profile, keys: &[&str]) -> String {
format!("{}.{}", profile, keys.join("."))
}