litcheck_filecheck/expr/
cli.rs1use 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}