use std::{ borrow::{Borrow, Cow}, collections::BTreeMap, fmt, }; use crate::diagnostics::{self, DiagResult, Diagnostic, Label, Report, SourceSpan, Span, Spanned}; pub trait ValueParser { type Value<'a>; fn try_parse(s: Span<&str>) -> DiagResult>; } impl ValueParser for str { type Value<'a> = &'a str; #[inline(always)] fn try_parse(s: Span<&str>) -> DiagResult> { Ok(s.into_inner()) } } impl ValueParser for &'static str { type Value<'a> = &'static str; #[inline(always)] fn try_parse(s: Span<&str>) -> DiagResult> { Ok(Box::leak::<'static>(s.to_string().into_boxed_str())) } } impl ValueParser for String { type Value<'a> = String; #[inline(always)] fn try_parse(s: Span<&str>) -> DiagResult> { Ok(s.into_inner().to_string()) } } impl<'b> ValueParser for Cow<'b, str> { type Value<'a> = Cow<'a, str>; #[inline(always)] fn try_parse(s: Span<&str>) -> DiagResult> { Ok(Cow::Borrowed(s.into_inner())) } } impl ValueParser for i64 { type Value<'a> = i64; #[inline(always)] fn try_parse(s: Span<&str>) -> DiagResult> { let (span, s) = s.into_parts(); s.parse::().map_err(|err| { Report::new(diagnostics::Diag::new(format!("{err}")).with_label(Label::at(span))) }) } } pub trait TypedVariable: Clone + Sized { type Key<'a>; type Value<'a>; fn try_parse(input: Span<&str>) -> Result; } #[derive(Diagnostic, Debug)] pub enum VariableError { #[diagnostic()] Empty(#[label] SourceSpan), #[diagnostic()] EmptyName(#[label] SourceSpan), #[diagnostic(transparent)] Name(Report), #[diagnostic(transparent)] Value(Report), #[diagnostic()] MissingEquals(#[label] SourceSpan), #[diagnostic(transparent)] Format(Report), } impl VariableError { pub fn into_report(self) -> Report { match self { Self::Name(report) | Self::Value(report) | Self::Format(report) => report, _ => Report::from(self), } } } impl std::error::Error for VariableError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { use std::error::Error; match self { Self::Name(ref report) | Self::Value(ref report) | Self::Format(ref report) => { AsRef::::as_ref(report).source() } _ => None, } } } impl fmt::Display for VariableError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Self::Empty(_) => f.write_str("invalid variable definition: expected expression of the form `NAME(=VALUE)?`"), Self::EmptyName(_) => f.write_str("invalid variable definition: name cannot be empty"), Self::Name(_) => f.write_str("invalid variable name"), Self::Value(_) => f.write_str("invalid variable value"), Self::MissingEquals(_) => f.write_str( "invalid variable definition: expected 'NAME=VALUE', but no '=' was found in the input", ), Self::Format(_) => f.write_str("invalid variable definition"), } } } #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub enum VariableName { Pseudo(Span), Global(Span), User(Span), } impl Copy for VariableName {} impl ValueParser for VariableName where for<'a> S: ValueParser = S>, for<'a> ::Value<'a>: AsRef, { type Value<'a> = VariableName<::Value<'a>>; fn try_parse(input: Span<&str>) -> DiagResult> { let (span, s) = input.into_parts(); let len = s.as_bytes().len(); let (prefix, unprefixed) = if let Some(name) = s.strip_prefix('$') { (Some('$'), name) } else if let Some(name) = s.strip_prefix('@') { (Some('@'), name) } else { (None, s) }; if !is_valid_variable_name(unprefixed) { let offset = prefix.is_some() as usize; return Err(miette::miette!( labels = vec![Label::at(offset..len).into()], help = "must be non-empty, and match the pattern `[A-Za-z_][A-Za-z0-9_]*`", "invalid variable name" )); } let name = ::try_parse(Span::new(span, unprefixed))?; match prefix { None => Ok(Self::User(Span::new(span, name))), Some('$') => Ok(Self::Global(Span::new(span, name))), Some(_) => Ok(Self::Pseudo(Span::new(span, name))), } } } impl Spanned for VariableName { fn span(&self) -> SourceSpan { match self { Self::Pseudo(name) | Self::Global(name) | Self::User(name) => name.span(), } } } impl VariableName { #[inline] pub fn map(self, f: F) -> VariableName where F: FnMut(S) -> T, { match self { Self::User(s) => VariableName::User(s.map(f)), Self::Global(s) => VariableName::Global(s.map(f)), Self::Pseudo(s) => VariableName::Pseudo(s.map(f)), } } } impl VariableName { pub fn as_string(&self) -> &String { match self { Self::Pseudo(ref s) | Self::Global(ref s) | Self::User(ref s) => s, } } } impl> VariableName { pub fn as_str(&self) -> &str { match self { Self::Pseudo(ref s) | Self::Global(ref s) | Self::User(ref s) => (**s).as_ref(), } } } impl VariableName { pub fn into_inner(self) -> S { match self { Self::User(s) | Self::Global(s) | Self::Pseudo(s) => s.into_inner(), } } pub fn to_global(self) -> Self { match self { global @ (Self::Global(_) | Self::Pseudo(_)) => global, Self::User(name) => Self::Global(name), } } } impl, S: Borrow> Borrow for VariableName { fn borrow(&self) -> &T { match self { Self::Pseudo(ref s) | Self::Global(ref s) | Self::User(ref s) => s.borrow(), } } } impl> AsRef for VariableName { fn as_ref(&self) -> &T { match self { Self::Pseudo(ref s) | Self::Global(ref s) | Self::User(ref s) => (**s).as_ref(), } } } #[derive(Debug, PartialEq, Eq)] pub struct Variable { pub name: VariableName, pub value: V, } impl Clone for Variable where K: Clone, V: Clone, { fn clone(&self) -> Self { Self { name: self.name.clone(), value: self.value.clone(), } } } unsafe impl Send for Variable {} unsafe impl Sync for Variable {} impl Variable { pub fn new(name: VariableName, value: T) -> Self where V: From, { Self { name, value: V::from(value), } } pub fn name(&self) -> &VariableName { &self.name } pub fn is_pseudo(&self) -> bool { matches!(self.name, VariableName::Pseudo(_)) } pub fn is_global(&self) -> bool { matches!(self.name, VariableName::Global(_) | VariableName::Pseudo(_)) } } impl TypedVariable for Variable where for<'a> VariableName: ValueParser = VariableName> + AsRef + Clone + 'a, for<'a> K: Clone + 'a, for<'a> V: ValueParser = V> + Clone + 'a, { type Key<'a> = K; type Value<'a> = V; fn try_parse(input: Span<&str>) -> Result { let (span, s) = input.into_parts(); let len = s.as_bytes().len(); if s.is_empty() { Err(VariableError::Empty(span)) } else if let Some((k, v)) = s.split_once('=') { if k.is_empty() { return Err(VariableError::EmptyName(span)); } let key_len = k.as_bytes().len(); let key_span = SourceSpan::from(0..key_len); if !is_valid_variable_name(k) { return Err(VariableError::Name(miette::miette!( labels = vec![Label::at(key_span).into()], help = "variable names must match the pattern `[A-Za-z_][A-Za-z0-9_]*`", "name contains invalid characters", ))); } let k = as ValueParser>::try_parse(Span::new(key_span, k)) .map_err(VariableError::Name)?; let value_span = SourceSpan::from((key_len + 1)..len); let v = ::try_parse(Span::new(value_span, v)) .map_err(VariableError::Value)?; Ok(Self::new(k, v)) } else { Err(VariableError::MissingEquals(span)) } } } impl clap::builder::ValueParserFactory for Variable where V: ValueParser, K: Send + Sync + Clone, for<'a> ::Value<'a>: Send + Sync + Clone, for<'a> Variable: TypedVariable = K, Value<'a> = V> + Send + Sync + Clone + 'static, { type Parser = VariableParser>; fn value_parser() -> Self::Parser { Default::default() } } #[derive(Copy, Debug)] pub struct VariableParser(core::marker::PhantomData); impl Clone for VariableParser { fn clone(&self) -> Self { Self(core::marker::PhantomData) } } unsafe impl Send for VariableParser {} unsafe impl Sync for VariableParser {} impl Default for VariableParser { fn default() -> Self { Self(core::marker::PhantomData) } } impl clap::builder::TypedValueParser for VariableParser where K: Send + Sync + Clone + 'static, V: Send + Sync + Clone + 'static, for<'a> T: TypedVariable = K, Value<'a> = V> + Send + Sync + Clone + 'static, { type Value = T; fn parse_ref( &self, _cmd: &clap::Command, _arg: Option<&clap::Arg>, value: &std::ffi::OsStr, ) -> Result { use clap::error::{Error, ErrorKind}; let raw = value .to_str() .ok_or_else(|| Error::new(ErrorKind::InvalidUtf8))?; let span = SourceSpan::from(0..raw.as_bytes().len()); ::try_parse(Span::new(span, raw)).map_err(|err| { let err = err.into_report().with_source_code(raw.to_string()); let diag = diagnostics::reporting::PrintDiagnostic::new(err); Error::raw(ErrorKind::InvalidValue, format!("{diag}")) }) } } pub struct Variables(BTreeMap, V>) where VariableName: Eq + Ord; impl FromIterator> for Variables where VariableName: Eq + Ord, V: TypedVariable, { fn from_iter(iter: T) -> Self where T: IntoIterator>, { Self(iter.into_iter().map(|var| (var.name, var.value)).collect()) } } impl Variables where VariableName: Eq + Ord, V: TypedVariable, { pub fn is_defined(&self, k: &Q) -> bool where Q: Ord + Eq, VariableName: Borrow, { self.0.contains_key(k) } pub fn get(&self, k: &Q) -> Option<&V> where Q: Ord + Eq, VariableName: Borrow, { self.0.get(k) } pub fn define(&mut self, k: impl Into>, v: V) -> Option { self.0.insert(k.into(), v) } pub fn delete(&mut self, k: &Q) -> Option> where Q: Ord + Eq, VariableName: Borrow, { self.0.remove_entry(k).map(|(k, v)| Variable::new(k, v)) } } pub fn is_valid_variable_name(name: &str) -> bool { let mut chars = name.chars(); match chars.next() { Some(c) if c == '_' || c.is_ascii_alphabetic() => { for c in chars { if c != '_' && !c.is_ascii_alphanumeric() { return false; } } } Some(_) | None => return false, } true }