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}