compose_library/foundations/
args.rs

1use crate::diag::{bail, error, At, SourceDiagnostic, SourceResult, Spanned};
2use crate::foundations::cast::FromValue;
3use crate::foundations::IntoValue;
4use crate::{Trace, Value};
5use compose_library::UntypedRef;
6use compose_syntax::Span;
7use ecow::{eco_vec, EcoVec};
8
9#[derive(Debug, Clone)]
10pub struct Args {
11    pub span: Span,
12    pub items: EcoVec<Arg>,
13}
14
15impl Args {
16    pub fn new<T: IntoValue>(span: Span, items: impl IntoIterator<Item = T>) -> Self {
17        let items = items
18            .into_iter()
19            .map(|value| Arg {
20                span,
21                name: None,
22                value: Spanned::new(value.into_value(), span),
23            })
24            .collect();
25        Self { span, items }
26    }
27    pub fn insert(&mut self, index: i32, span: Span, value: Value) {
28        self.items.insert(
29            index as usize,
30            Arg {
31                span,
32                name: None,
33                value: Spanned::new(value, span),
34            },
35        );
36    }
37    pub fn push(&mut self, span: Span, value: Value) {
38        self.items.push(Arg {
39            span,
40            name: None,
41            value: Spanned::new(value, span),
42        });
43    }
44
45    pub fn spanned(mut self, span: Span) -> Args {
46        if self.span.is_detached() {
47            self.span = span;
48        }
49        self
50    }
51
52    pub fn eat<T>(&mut self) -> SourceResult<Option<T>>
53    where
54        T: FromValue<Spanned<Value>>,
55    {
56        for (i, item) in self.items.iter().enumerate() {
57            if item.name.is_some() {
58                // Only return positional args
59                continue;
60            }
61            let value = self.items.remove(i).value;
62            let span = value.span;
63            return T::from_value(value).at(span).map(Some)
64        }
65        Ok(None)
66    }
67
68    pub fn expect<T>(&mut self, what: &str) -> SourceResult<T>
69    where
70        T: FromValue<Spanned<Value>>,
71    {
72        match self.eat()? {
73            Some(v) => Ok(v),
74            None => bail!(self.missing_argument(what)),
75        }
76    }
77
78    /// Remove all args with the given name and return the last
79    /// or an error if the conversion fails.
80    ///
81    /// If no such arg exists, then Ok(None) is returned
82    pub fn named<T>(&mut self, name: &str) -> SourceResult<Option<T>>
83    where
84        T: FromValue<Spanned<Value>>,
85    {
86        let mut found = None;
87
88        for i in (0..self.items.len()).rev() {
89            if self.items[i].name.as_deref() == Some(name) {
90                let value = self.items.remove(i).value;
91                let span = value.span;
92                let casted = T::from_value(value).at(span)?;
93                
94                if found.is_none() {
95                    found = Some(casted);
96                }
97            }
98        }
99
100        Ok(found)
101    }
102
103    /// Find and consume all castable positional arguments
104    pub fn all<T>(&mut self) -> SourceResult<Vec<T>>
105    where
106        T: FromValue<Spanned<Value>>,
107    {
108        let mut vals = Vec::new();
109        let mut errors = eco_vec![];
110
111        self.items.retain(|item| {
112            if item.name.is_some() {
113                return true;
114            }
115
116            let span = item.value.span;
117            let spanned = Spanned::new(std::mem::take(&mut item.value.value), span);
118            match T::from_value(spanned).at(span) {
119                Ok(v) => vals.push(v),
120                Err(e) => errors.extend(e),
121            }
122            false
123        });
124
125        if !errors.is_empty() {
126            return Err(errors);
127        }
128
129        Ok(vals)
130    }
131
132    fn missing_argument(&self, what: &str) -> SourceDiagnostic {
133        // TODO: Handle named arguments
134        error!(self.span, "missing argument `{}`", what)
135    }
136
137    /// Take out all arguments into a new instance.
138    pub fn take(&mut self) -> Self {
139        Self {
140            span: self.span,
141            items: std::mem::take(&mut self.items),
142        }
143    }
144
145    /// Return an "unexpected argument" error if there is any remaining
146    /// argument.
147    pub fn finish(self) -> SourceResult<()> {
148        if let Some(arg) = self.items.first() {
149            // TODO: Handle named arguments
150            bail!(arg.span, "unexpected argument");
151        }
152        Ok(())
153    }
154}
155
156#[derive(Debug, Clone)]
157pub struct Arg {
158    pub span: Span,
159    pub name: Option<String>,
160    pub value: Spanned<Value>,
161}
162
163pub struct Meta(u8);
164
165impl Meta {
166    const REF: Meta = Meta(1);
167    const MUT: Meta = Meta(2);
168    
169    pub fn new() -> Meta {
170        Meta(0)
171    }
172
173    pub fn is_ref(&self) -> bool {
174        (self.0 & Meta::REF.0) > 0
175    }
176
177    pub fn is_mut(&self) -> bool {
178        (self.0 & Meta::MUT.0) > 0
179    }
180
181    pub fn join(&self, other: Meta) -> Meta {
182        Meta(self.0 | other.0)
183    }
184}
185
186impl Trace for Args {
187    fn visit_refs(&self, f: &mut dyn FnMut(UntypedRef)) {
188        for arg in &self.items {
189            arg.value.value.visit_refs(f);
190        }
191    }
192}