litcheck_filecheck/expr/
cli.rs

1use std::str::FromStr;
2
3use litcheck::variables::{self, TypedVariable, VariableError};
4
5use crate::common::*;
6
7use super::{parser::NumericVarParser, Var};
8
9#[derive(Debug, Clone)]
10pub struct CliVariable {
11    pub name: variables::VariableName<Box<str>>,
12    pub value: Value<'static>,
13}
14impl FromStr for CliVariable {
15    type Err = VariableError;
16
17    fn from_str(input: &str) -> Result<Self, Self::Err> {
18        let span = SourceSpan::from(0..input.as_bytes().len());
19        <CliVariable as TypedVariable>::try_parse(Span::new(span, input))
20    }
21}
22impl CliVariable {
23    pub(crate) fn from_numeric_var(var: Var<'_>, interner: &StringInterner) -> Self {
24        let name = var
25            .name
26            .map(|sym| interner.resolve(sym).to_string().into_boxed_str());
27        let value = match var.value {
28            Value::Undef => Value::Undef,
29            Value::Str(s) => Value::Str(Cow::Owned(s.to_string())),
30            Value::Num(n) => Value::Num(n),
31        };
32        Self { name, value }
33    }
34}
35impl TypedVariable for CliVariable {
36    type Key<'a> = Box<str>;
37    type Value<'a> = Value<'static>;
38
39    fn try_parse(input: Span<&str>) -> Result<Self, VariableError> {
40        let (span, s) = input.into_parts();
41        if s.is_empty() {
42            Err(VariableError::Empty(span))
43        } else if s.starts_with('#') {
44            let mut interner = StringInterner::new();
45            let mut parser = NumericVarParser::new(&mut interner);
46            let var = parser
47                .parse(s)
48                .map_err(|err| VariableError::Format(Report::new(err)))?;
49            Ok(CliVariable::from_numeric_var(var, &interner))
50        } else if let Some((k, v)) = s.split_once('=') {
51            if k.is_empty() {
52                return Err(VariableError::EmptyName(span));
53            }
54            if !variables::is_valid_variable_name(k) {
55                return Err(VariableError::Name(miette::miette!(
56                    labels = vec![Label::at(0..k.as_bytes().len()).into()],
57                    help = "must be non-empty, and match the pattern `[A-Za-z_][A-Za-z0-9_]*`",
58                    "invalid variable name"
59                )));
60            }
61            let k = k.to_string().into_boxed_str();
62            let v = if v.is_empty() {
63                Value::Undef
64            } else {
65                Value::Str(Cow::Owned(v.to_string()))
66            };
67            let span = SourceSpan::from(0..(k.as_bytes().len()));
68            Ok(CliVariable {
69                name: variables::VariableName::User(Span::new(span, k)),
70                value: v,
71            })
72        } else {
73            Err(VariableError::MissingEquals(span))
74        }
75    }
76}