litcheck_core/variables/
mod.rs

1use std::{
2    borrow::{Borrow, Cow},
3    collections::BTreeMap,
4    fmt,
5};
6
7use crate::diagnostics::{self, DiagResult, Diagnostic, Label, Report, SourceSpan, Span, Spanned};
8
9pub trait ValueParser {
10    type Value<'a>;
11
12    fn try_parse(s: Span<&str>) -> DiagResult<Self::Value<'_>>;
13}
14impl ValueParser for str {
15    type Value<'a> = &'a str;
16
17    #[inline(always)]
18    fn try_parse(s: Span<&str>) -> DiagResult<Self::Value<'_>> {
19        Ok(s.into_inner())
20    }
21}
22impl ValueParser for &'static str {
23    type Value<'a> = &'static str;
24
25    #[inline(always)]
26    fn try_parse(s: Span<&str>) -> DiagResult<Self::Value<'_>> {
27        Ok(Box::leak::<'static>(s.to_string().into_boxed_str()))
28    }
29}
30impl ValueParser for String {
31    type Value<'a> = String;
32
33    #[inline(always)]
34    fn try_parse(s: Span<&str>) -> DiagResult<Self::Value<'_>> {
35        Ok(s.into_inner().to_string())
36    }
37}
38impl<'b> ValueParser for Cow<'b, str> {
39    type Value<'a> = Cow<'a, str>;
40
41    #[inline(always)]
42    fn try_parse(s: Span<&str>) -> DiagResult<Self::Value<'_>> {
43        Ok(Cow::Borrowed(s.into_inner()))
44    }
45}
46impl ValueParser for i64 {
47    type Value<'a> = i64;
48
49    #[inline(always)]
50    fn try_parse(s: Span<&str>) -> DiagResult<Self::Value<'_>> {
51        let (span, s) = s.into_parts();
52        s.parse::<i64>().map_err(|err| {
53            Report::new(diagnostics::Diag::new(format!("{err}")).with_label(Label::at(span)))
54        })
55    }
56}
57
58pub trait TypedVariable: Clone + Sized {
59    type Key<'a>;
60    type Value<'a>;
61
62    fn try_parse(input: Span<&str>) -> Result<Self, VariableError>;
63}
64
65#[derive(Diagnostic, Debug)]
66pub enum VariableError {
67    #[diagnostic()]
68    Empty(#[label] SourceSpan),
69    #[diagnostic()]
70    EmptyName(#[label] SourceSpan),
71    #[diagnostic(transparent)]
72    Name(Report),
73    #[diagnostic(transparent)]
74    Value(Report),
75    #[diagnostic()]
76    MissingEquals(#[label] SourceSpan),
77    #[diagnostic(transparent)]
78    Format(Report),
79}
80impl VariableError {
81    pub fn into_report(self) -> Report {
82        match self {
83            Self::Name(report) | Self::Value(report) | Self::Format(report) => report,
84            _ => Report::from(self),
85        }
86    }
87}
88impl std::error::Error for VariableError {
89    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
90        use std::error::Error;
91
92        match self {
93            Self::Name(ref report) | Self::Value(ref report) | Self::Format(ref report) => {
94                AsRef::<dyn Error>::as_ref(report).source()
95            }
96            _ => None,
97        }
98    }
99}
100impl fmt::Display for VariableError {
101    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
102        match self {
103            Self::Empty(_) => f.write_str("invalid variable definition: expected expression of the form `NAME(=VALUE)?`"),
104            Self::EmptyName(_) => f.write_str("invalid variable definition: name cannot be empty"),
105            Self::Name(_) => f.write_str("invalid variable name"),
106            Self::Value(_) => f.write_str("invalid variable value"),
107            Self::MissingEquals(_) => f.write_str(
108                "invalid variable definition: expected 'NAME=VALUE', but no '=' was found in the input",
109            ),
110            Self::Format(_) => f.write_str("invalid variable definition"),
111        }
112    }
113}
114
115#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
116pub enum VariableName<S = String> {
117    Pseudo(Span<S>),
118    Global(Span<S>),
119    User(Span<S>),
120}
121impl<S: Copy> Copy for VariableName<S> {}
122impl<S> ValueParser for VariableName<S>
123where
124    for<'a> S: ValueParser<Value<'a> = S>,
125    for<'a> <S as ValueParser>::Value<'a>: AsRef<str>,
126{
127    type Value<'a> = VariableName<<S as ValueParser>::Value<'a>>;
128
129    fn try_parse(input: Span<&str>) -> DiagResult<Self::Value<'_>> {
130        let (span, s) = input.into_parts();
131        let len = s.as_bytes().len();
132        let (prefix, unprefixed) = if let Some(name) = s.strip_prefix('$') {
133            (Some('$'), name)
134        } else if let Some(name) = s.strip_prefix('@') {
135            (Some('@'), name)
136        } else {
137            (None, s)
138        };
139        if !is_valid_variable_name(unprefixed) {
140            let offset = prefix.is_some() as usize;
141            return Err(miette::miette!(
142                labels = vec![Label::at(offset..len).into()],
143                help = "must be non-empty, and match the pattern `[A-Za-z_][A-Za-z0-9_]*`",
144                "invalid variable name"
145            ));
146        }
147
148        let name = <S as ValueParser>::try_parse(Span::new(span, unprefixed))?;
149        match prefix {
150            None => Ok(Self::User(Span::new(span, name))),
151            Some('$') => Ok(Self::Global(Span::new(span, name))),
152            Some(_) => Ok(Self::Pseudo(Span::new(span, name))),
153        }
154    }
155}
156impl<S> Spanned for VariableName<S> {
157    fn span(&self) -> SourceSpan {
158        match self {
159            Self::Pseudo(name) | Self::Global(name) | Self::User(name) => name.span(),
160        }
161    }
162}
163impl<S> VariableName<S> {
164    #[inline]
165    pub fn map<T, F>(self, f: F) -> VariableName<T>
166    where
167        F: FnMut(S) -> T,
168    {
169        match self {
170            Self::User(s) => VariableName::User(s.map(f)),
171            Self::Global(s) => VariableName::Global(s.map(f)),
172            Self::Pseudo(s) => VariableName::Pseudo(s.map(f)),
173        }
174    }
175}
176impl VariableName<String> {
177    pub fn as_string(&self) -> &String {
178        match self {
179            Self::Pseudo(ref s) | Self::Global(ref s) | Self::User(ref s) => s,
180        }
181    }
182}
183impl<S: AsRef<str>> VariableName<S> {
184    pub fn as_str(&self) -> &str {
185        match self {
186            Self::Pseudo(ref s) | Self::Global(ref s) | Self::User(ref s) => (**s).as_ref(),
187        }
188    }
189}
190impl<S> VariableName<S> {
191    pub fn into_inner(self) -> S {
192        match self {
193            Self::User(s) | Self::Global(s) | Self::Pseudo(s) => s.into_inner(),
194        }
195    }
196
197    pub fn to_global(self) -> Self {
198        match self {
199            global @ (Self::Global(_) | Self::Pseudo(_)) => global,
200            Self::User(name) => Self::Global(name),
201        }
202    }
203}
204impl<T: Borrow<str>, S: Borrow<T>> Borrow<T> for VariableName<S> {
205    fn borrow(&self) -> &T {
206        match self {
207            Self::Pseudo(ref s) | Self::Global(ref s) | Self::User(ref s) => s.borrow(),
208        }
209    }
210}
211impl<T: ?Sized, S: AsRef<T>> AsRef<T> for VariableName<S> {
212    fn as_ref(&self) -> &T {
213        match self {
214            Self::Pseudo(ref s) | Self::Global(ref s) | Self::User(ref s) => (**s).as_ref(),
215        }
216    }
217}
218
219#[derive(Debug, PartialEq, Eq)]
220pub struct Variable<K, V> {
221    pub name: VariableName<K>,
222    pub value: V,
223}
224impl<K, V> Clone for Variable<K, V>
225where
226    K: Clone,
227    V: Clone,
228{
229    fn clone(&self) -> Self {
230        Self {
231            name: self.name.clone(),
232            value: self.value.clone(),
233        }
234    }
235}
236unsafe impl<K: Send, V: Send> Send for Variable<K, V> {}
237unsafe impl<K: Send, V: Sync> Sync for Variable<K, V> {}
238impl<K, V> Variable<K, V> {
239    pub fn new<T>(name: VariableName<K>, value: T) -> Self
240    where
241        V: From<T>,
242    {
243        Self {
244            name,
245            value: V::from(value),
246        }
247    }
248
249    pub fn name(&self) -> &VariableName<K> {
250        &self.name
251    }
252
253    pub fn is_pseudo(&self) -> bool {
254        matches!(self.name, VariableName::Pseudo(_))
255    }
256
257    pub fn is_global(&self) -> bool {
258        matches!(self.name, VariableName::Global(_) | VariableName::Pseudo(_))
259    }
260}
261impl<K, V> TypedVariable for Variable<K, V>
262where
263    for<'a> VariableName<K>: ValueParser<Value<'a> = VariableName<K>> + AsRef<str> + Clone + 'a,
264    for<'a> K: Clone + 'a,
265    for<'a> V: ValueParser<Value<'a> = V> + Clone + 'a,
266{
267    type Key<'a> = K;
268    type Value<'a> = V;
269
270    fn try_parse(input: Span<&str>) -> Result<Self, VariableError> {
271        let (span, s) = input.into_parts();
272        let len = s.as_bytes().len();
273        if s.is_empty() {
274            Err(VariableError::Empty(span))
275        } else if let Some((k, v)) = s.split_once('=') {
276            if k.is_empty() {
277                return Err(VariableError::EmptyName(span));
278            }
279            let key_len = k.as_bytes().len();
280            let key_span = SourceSpan::from(0..key_len);
281            if !is_valid_variable_name(k) {
282                return Err(VariableError::Name(miette::miette!(
283                    labels = vec![Label::at(key_span).into()],
284                    help = "variable names must match the pattern `[A-Za-z_][A-Za-z0-9_]*`",
285                    "name contains invalid characters",
286                )));
287            }
288            let k = <VariableName<K> as ValueParser>::try_parse(Span::new(key_span, k))
289                .map_err(VariableError::Name)?;
290            let value_span = SourceSpan::from((key_len + 1)..len);
291            let v = <V as ValueParser>::try_parse(Span::new(value_span, v))
292                .map_err(VariableError::Value)?;
293            Ok(Self::new(k, v))
294        } else {
295            Err(VariableError::MissingEquals(span))
296        }
297    }
298}
299impl<K, V> clap::builder::ValueParserFactory for Variable<K, V>
300where
301    V: ValueParser,
302    K: Send + Sync + Clone,
303    for<'a> <V as ValueParser>::Value<'a>: Send + Sync + Clone,
304    for<'a> Variable<K, V>:
305        TypedVariable<Key<'a> = K, Value<'a> = V> + Send + Sync + Clone + 'static,
306{
307    type Parser = VariableParser<Variable<K, V>>;
308
309    fn value_parser() -> Self::Parser {
310        Default::default()
311    }
312}
313
314#[derive(Copy, Debug)]
315pub struct VariableParser<T>(core::marker::PhantomData<T>);
316impl<T> Clone for VariableParser<T> {
317    fn clone(&self) -> Self {
318        Self(core::marker::PhantomData)
319    }
320}
321unsafe impl<T: Send> Send for VariableParser<T> {}
322unsafe impl<T: Sync> Sync for VariableParser<T> {}
323impl<T> Default for VariableParser<T> {
324    fn default() -> Self {
325        Self(core::marker::PhantomData)
326    }
327}
328impl<T, K, V> clap::builder::TypedValueParser for VariableParser<T>
329where
330    K: Send + Sync + Clone + 'static,
331    V: Send + Sync + Clone + 'static,
332    for<'a> T: TypedVariable<Key<'a> = K, Value<'a> = V> + Send + Sync + Clone + 'static,
333{
334    type Value = T;
335
336    fn parse_ref(
337        &self,
338        _cmd: &clap::Command,
339        _arg: Option<&clap::Arg>,
340        value: &std::ffi::OsStr,
341    ) -> Result<Self::Value, clap::Error> {
342        use clap::error::{Error, ErrorKind};
343
344        let raw = value
345            .to_str()
346            .ok_or_else(|| Error::new(ErrorKind::InvalidUtf8))?;
347
348        let span = SourceSpan::from(0..raw.as_bytes().len());
349        <T as TypedVariable>::try_parse(Span::new(span, raw)).map_err(|err| {
350            let err = err.into_report().with_source_code(raw.to_string());
351            let diag = diagnostics::reporting::PrintDiagnostic::new(err);
352            Error::raw(ErrorKind::InvalidValue, format!("{diag}"))
353        })
354    }
355}
356
357pub struct Variables<K, V>(BTreeMap<VariableName<K>, V>)
358where
359    VariableName<K>: Eq + Ord;
360impl<K, V> FromIterator<Variable<K, V>> for Variables<K, V>
361where
362    VariableName<K>: Eq + Ord,
363    V: TypedVariable,
364{
365    fn from_iter<T>(iter: T) -> Self
366    where
367        T: IntoIterator<Item = Variable<K, V>>,
368    {
369        Self(iter.into_iter().map(|var| (var.name, var.value)).collect())
370    }
371}
372impl<K, V> Variables<K, V>
373where
374    VariableName<K>: Eq + Ord,
375    V: TypedVariable,
376{
377    pub fn is_defined<Q>(&self, k: &Q) -> bool
378    where
379        Q: Ord + Eq,
380        VariableName<K>: Borrow<Q>,
381    {
382        self.0.contains_key(k)
383    }
384
385    pub fn get<Q>(&self, k: &Q) -> Option<&V>
386    where
387        Q: Ord + Eq,
388        VariableName<K>: Borrow<Q>,
389    {
390        self.0.get(k)
391    }
392
393    pub fn define(&mut self, k: impl Into<VariableName<K>>, v: V) -> Option<V> {
394        self.0.insert(k.into(), v)
395    }
396
397    pub fn delete<Q>(&mut self, k: &Q) -> Option<Variable<K, V>>
398    where
399        Q: Ord + Eq,
400        VariableName<K>: Borrow<Q>,
401    {
402        self.0.remove_entry(k).map(|(k, v)| Variable::new(k, v))
403    }
404}
405
406pub fn is_valid_variable_name(name: &str) -> bool {
407    let mut chars = name.chars();
408    match chars.next() {
409        Some(c) if c == '_' || c.is_ascii_alphabetic() => {
410            for c in chars {
411                if c != '_' && !c.is_ascii_alphanumeric() {
412                    return false;
413                }
414            }
415        }
416        Some(_) | None => return false,
417    }
418
419    true
420}