1use super::vector::Vector;
8use super::{RError, RErrorKind, RValue};
9use crate::interpreter::environment::Environment;
10use crate::interpreter::BuiltinContext;
11
12#[derive(Debug, Clone)]
24pub enum RArg<T> {
25 Missing,
27 Null,
29 Value(T),
31}
32
33impl<T> RArg<T> {
34 pub fn is_missing(&self) -> bool {
35 matches!(self, RArg::Missing)
36 }
37
38 pub fn is_null(&self) -> bool {
39 matches!(self, RArg::Null)
40 }
41
42 pub fn value(&self) -> Option<&T> {
43 match self {
44 RArg::Value(v) => Some(v),
45 _ => None,
46 }
47 }
48
49 pub fn into_value(self) -> Option<T> {
50 match self {
51 RArg::Value(v) => Some(v),
52 _ => None,
53 }
54 }
55
56 pub fn optional(self) -> Option<T> {
58 self.into_value()
59 }
60
61 pub fn or_default(self, default: T) -> Self {
63 match self {
64 RArg::Missing => RArg::Value(default),
65 other => other,
66 }
67 }
68
69 pub fn unwrap_or(self, default: T) -> T {
71 match self {
72 RArg::Value(v) => v,
73 _ => default,
74 }
75 }
76
77 pub fn unwrap_or_else(self, f: impl FnOnce() -> T) -> T {
79 match self {
80 RArg::Value(v) => v,
81 _ => f(),
82 }
83 }
84
85 pub fn map<U>(self, f: impl FnOnce(T) -> U) -> RArg<U> {
86 match self {
87 RArg::Missing => RArg::Missing,
88 RArg::Null => RArg::Null,
89 RArg::Value(v) => RArg::Value(f(v)),
90 }
91 }
92}
93
94#[derive(Debug, Clone, Default)]
104pub struct Dots(pub Vec<RValue>);
105
106impl Dots {
107 pub fn len(&self) -> usize {
108 self.0.len()
109 }
110
111 pub fn is_empty(&self) -> bool {
112 self.0.is_empty()
113 }
114
115 pub fn iter(&self) -> std::slice::Iter<'_, RValue> {
116 self.0.iter()
117 }
118}
119
120impl IntoIterator for Dots {
121 type Item = RValue;
122 type IntoIter = std::vec::IntoIter<RValue>;
123 fn into_iter(self) -> Self::IntoIter {
124 self.0.into_iter()
125 }
126}
127
128impl<'a> IntoIterator for &'a Dots {
129 type Item = &'a RValue;
130 type IntoIter = std::slice::Iter<'a, RValue>;
131 fn into_iter(self) -> Self::IntoIter {
132 self.0.iter()
133 }
134}
135
136pub struct BuiltinInfo {
142 pub name: &'static str,
143 pub aliases: &'static [&'static str],
144 pub min_args: usize,
145 pub max_args: Option<usize>,
146 pub doc: &'static str,
147 pub params: &'static [&'static str],
149}
150
151pub trait FromArgs: Sized {
163 fn from_args(args: &[RValue], named: &[(String, RValue)]) -> Result<Self, RError>;
165
166 fn info() -> &'static BuiltinInfo;
168}
169
170pub trait Builtin: FromArgs {
175 fn call(self, ctx: &BuiltinContext) -> Result<RValue, RError>;
177}
178
179pub fn find_arg<'a>(
185 args: &'a [RValue],
186 named: &'a [(String, RValue)],
187 param_name: &str,
188 positional_index: usize,
189) -> Option<&'a RValue> {
190 if let Some(v) = find_named_arg(named, param_name) {
191 return Some(v);
192 }
193 args.get(positional_index)
195}
196
197pub fn find_named_arg<'a>(named: &'a [(String, RValue)], param_name: &str) -> Option<&'a RValue> {
200 for (name, val) in named {
202 if name == param_name {
203 return Some(val);
204 }
205 }
206 let candidates: Vec<_> = named
208 .iter()
209 .filter(|(name, _)| param_name.starts_with(name.as_str()))
210 .collect();
211 if candidates.len() == 1 {
212 return Some(&candidates[0].1);
213 }
214 None
215}
216
217pub trait CoerceArg: Sized {
223 fn coerce(val: &RValue, param_name: &str) -> Result<Self, RError>;
224}
225
226impl CoerceArg for f64 {
227 fn coerce(val: &RValue, param_name: &str) -> Result<Self, RError> {
228 val.as_vector()
229 .and_then(|v| v.as_double_scalar())
230 .ok_or_else(|| {
231 RError::new(
232 RErrorKind::Argument,
233 format!("'{}' must be numeric", param_name),
234 )
235 })
236 }
237}
238
239impl CoerceArg for i64 {
240 fn coerce(val: &RValue, param_name: &str) -> Result<Self, RError> {
241 val.as_vector()
242 .and_then(|v| v.as_integer_scalar())
243 .ok_or_else(|| {
244 RError::new(
245 RErrorKind::Argument,
246 format!("'{}' must be an integer", param_name),
247 )
248 })
249 }
250}
251
252impl CoerceArg for usize {
253 fn coerce(val: &RValue, param_name: &str) -> Result<Self, RError> {
254 let i = val
255 .as_vector()
256 .and_then(|v| v.as_integer_scalar())
257 .ok_or_else(|| {
258 RError::new(
259 RErrorKind::Argument,
260 format!("'{}' must be a non-negative integer", param_name),
261 )
262 })?;
263 usize::try_from(i).map_err(|_| {
264 RError::new(
265 RErrorKind::Argument,
266 format!("'{}' must be a non-negative integer, got {}", param_name, i),
267 )
268 })
269 }
270}
271
272impl CoerceArg for bool {
273 fn coerce(val: &RValue, param_name: &str) -> Result<Self, RError> {
274 val.as_vector()
275 .and_then(|v| v.as_logical_scalar())
276 .ok_or_else(|| {
277 RError::new(
278 RErrorKind::Argument,
279 format!("'{}' must be logical", param_name),
280 )
281 })
282 }
283}
284
285impl CoerceArg for String {
286 fn coerce(val: &RValue, param_name: &str) -> Result<Self, RError> {
287 val.as_vector()
288 .and_then(|v| v.as_character_scalar())
289 .ok_or_else(|| {
290 RError::new(
291 RErrorKind::Argument,
292 format!("'{}' must be a character string", param_name),
293 )
294 })
295 }
296}
297
298impl CoerceArg for RValue {
299 fn coerce(val: &RValue, _param_name: &str) -> Result<Self, RError> {
300 Ok(val.clone())
301 }
302}
303
304impl CoerceArg for Vector {
305 fn coerce(val: &RValue, param_name: &str) -> Result<Self, RError> {
306 val.as_vector().cloned().ok_or_else(|| {
307 RError::new(
308 RErrorKind::Argument,
309 format!("'{}' must be a vector", param_name),
310 )
311 })
312 }
313}
314
315impl CoerceArg for Environment {
316 fn coerce(val: &RValue, param_name: &str) -> Result<Self, RError> {
317 match val {
318 RValue::Environment(env) => Ok(env.clone()),
319 _ => Err(RError::new(
320 RErrorKind::Argument,
321 format!("'{}' must be an environment", param_name),
322 )),
323 }
324 }
325}
326
327impl<T: CoerceArg> CoerceArg for Option<T> {
330 fn coerce(val: &RValue, param_name: &str) -> Result<Self, RError> {
331 if matches!(val, RValue::Null) {
332 return Ok(None);
333 }
334 T::coerce(val, param_name).map(Some)
335 }
336}
337
338pub fn coerce_arg<T: CoerceArg>(val: &RValue, param_name: &str) -> Result<T, RError> {
340 T::coerce(val, param_name)
341}
342
343pub fn coerce_arg_three_way<T: CoerceArg>(
346 val: Option<&RValue>,
347 param_name: &str,
348) -> Result<RArg<T>, RError> {
349 match val {
350 None => Ok(RArg::Missing),
351 Some(RValue::Null) => Ok(RArg::Null),
352 Some(v) => T::coerce(v, param_name).map(RArg::Value),
353 }
354}
355
356