compose_library/foundations/
args.rs1use 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 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 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 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 error!(self.span, "missing argument `{}`", what)
135 }
136
137 pub fn take(&mut self) -> Self {
139 Self {
140 span: self.span,
141 items: std::mem::take(&mut self.items),
142 }
143 }
144
145 pub fn finish(self) -> SourceResult<()> {
148 if let Some(arg) = self.items.first() {
149 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}