Skip to main content

objectiveai_sdk/functions/expression/
input_value.rs

1//! Input types for Function expressions.
2//!
3//! Defines the data structures that can be passed as input to Functions,
4//! along with schema types for validation.
5
6use crate::agent;
7use indexmap::IndexMap;
8use schemars::JsonSchema;
9use serde::{Deserialize, Serialize};
10use starlark::values::dict::DictRef as StarlarkDictRef;
11use starlark::values::float::UnpackFloat;
12use starlark::values::list::ListRef as StarlarkListRef;
13use starlark::values::{
14    Heap as StarlarkHeap, UnpackValue, Value as StarlarkValue,
15};
16
17/// A concrete input value (post-compilation).
18///
19/// Represents any JSON-like value that can be passed to a Function,
20/// including rich content types (images, audio, video, files).
21#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, JsonSchema)]
22#[serde(untagged)]
23#[schemars(rename = "functions.expression.InputValue")]
24pub enum InputValue {
25    /// Rich content (image, audio, video, file).
26    #[schemars(title = "RichContentPart")]
27    RichContentPart(agent::completions::message::RichContentPart),
28    /// An object with string keys.
29    #[schemars(title = "Object")]
30    Object(IndexMap<String, InputValue>),
31    /// An array of values.
32    #[schemars(title = "Array")]
33    Array(Vec<InputValue>),
34    /// A string value.
35    #[schemars(title = "String")]
36    String(String),
37    /// An integer value.
38    #[schemars(title = "Integer")]
39    Integer(i64),
40    /// A floating-point number.
41    #[schemars(title = "Number")]
42    Number(f64),
43    /// A boolean value.
44    #[schemars(title = "Boolean")]
45    Boolean(bool),
46}
47
48// `InputValue::extract_to_files` was relocated to
49// `objectiveai_cli::logs::functions::expression::input_value::extract_to_files`.
50
51impl super::ToStarlarkValue for InputValue {
52    fn to_starlark_value<'v>(
53        &self,
54        heap: &'v StarlarkHeap,
55    ) -> StarlarkValue<'v> {
56        match self {
57            InputValue::String(s) => s.to_starlark_value(heap),
58            InputValue::Integer(i) => i.to_starlark_value(heap),
59            InputValue::Number(f) => f.to_starlark_value(heap),
60            InputValue::Boolean(b) => b.to_starlark_value(heap),
61            InputValue::Object(map) => map.to_starlark_value(heap),
62            InputValue::Array(arr) => arr.to_starlark_value(heap),
63            InputValue::RichContentPart(part) => part.to_starlark_value(heap),
64        }
65    }
66}
67
68impl super::FromStarlarkValue for InputValue {
69    fn from_starlark_value(
70        value: &StarlarkValue,
71    ) -> Result<Self, super::ExpressionError> {
72        if value.is_none() {
73            return Err(super::ExpressionError::StarlarkConversionError(
74                "Input: expected value".into(),
75            ));
76        }
77        if let Ok(Some(b)) = bool::unpack_value(*value) {
78            return Ok(InputValue::Boolean(b));
79        }
80        if let Ok(Some(i)) = i64::unpack_value(*value) {
81            return Ok(InputValue::Integer(i));
82        }
83        if let Ok(Some(UnpackFloat(f))) = UnpackFloat::unpack_value(*value) {
84            return Ok(InputValue::Number(f));
85        }
86        if let Ok(Some(s)) = <&str as UnpackValue>::unpack_value(*value) {
87            return Ok(InputValue::String(s.to_owned()));
88        }
89        if let Some(list) = StarlarkListRef::from_value(*value) {
90            let mut items = Vec::with_capacity(list.len());
91            for v in list.iter() {
92                items.push(InputValue::from_starlark_value(&v)?);
93            }
94            return Ok(InputValue::Array(items));
95        }
96        if let Some(dict) = StarlarkDictRef::from_value(*value) {
97            // Try RichContentPart first if "type" matches a known variant
98            let mut type_value = None;
99            for (k, v) in dict.iter() {
100                if let Ok(Some("type")) = <&str as UnpackValue>::unpack_value(k)
101                {
102                    type_value =
103                        <&str as UnpackValue>::unpack_value(v).ok().flatten();
104                    break;
105                }
106            }
107            if matches!(
108                type_value,
109                Some(
110                    "text"
111                        | "image_url"
112                        | "input_audio"
113                        | "input_video"
114                        | "video_url"
115                        | "file"
116                )
117            ) {
118                if let Ok(part) = agent::completions::message::RichContentPart::from_starlark_value(value) {
119                    return Ok(InputValue::RichContentPart(part));
120                }
121            }
122            let mut map = IndexMap::with_capacity(dict.len());
123            for (k, v) in dict.iter() {
124                let key = <&str as UnpackValue>::unpack_value(k)
125                    .map_err(|e| {
126                        super::ExpressionError::StarlarkConversionError(
127                            e.to_string(),
128                        )
129                    })?
130                    .ok_or_else(|| {
131                        super::ExpressionError::StarlarkConversionError(
132                            "Input: expected string key".into(),
133                        )
134                    })?
135                    .to_owned();
136                map.insert(key, InputValue::from_starlark_value(&v)?);
137            }
138            return Ok(InputValue::Object(map));
139        }
140        Err(super::ExpressionError::StarlarkConversionError(format!(
141            "Input: unsupported type: {}",
142            value.get_type()
143        )))
144    }
145}
146
147impl super::FromSpecial for InputValue {
148    fn from_special(
149        special: &super::Special,
150        params: &super::Params,
151    ) -> Result<Self, super::ExpressionError> {
152        match special {
153            super::Special::Input => {
154                let input = match params {
155                    super::Params::Owned(o) => &o.input,
156                    super::Params::Ref(r) => r.input,
157                };
158                Ok(input.clone())
159            }
160            super::Special::InputItemsOptionalContextMerge => {
161                // Expects input to be an array of objects, each with 'items' (array)
162                // and optionally 'context'. Merges all items into one array and takes
163                // context from the first element.
164                let input = match params {
165                    super::Params::Owned(o) => &o.input,
166                    super::Params::Ref(r) => r.input,
167                };
168                let InputValue::Array(arr) = input else {
169                    return Err(super::ExpressionError::UnsupportedSpecial);
170                };
171                let mut merged_items = Vec::new();
172                let mut context = None;
173                for (i, elem) in arr.iter().enumerate() {
174                    let InputValue::Object(obj) = elem else {
175                        return Err(super::ExpressionError::UnsupportedSpecial);
176                    };
177                    if let Some(InputValue::Array(items)) = obj.get("items") {
178                        merged_items.extend(items.iter().cloned());
179                    }
180                    if i == 0 {
181                        context = obj.get("context").cloned();
182                    }
183                }
184                let mut result = IndexMap::new();
185                result.insert(
186                    "items".to_string(),
187                    InputValue::Array(merged_items),
188                );
189                if let Some(ctx) = context {
190                    result.insert("context".to_string(), ctx);
191                }
192                Ok(InputValue::Object(result))
193            }
194            _ => Err(super::ExpressionError::UnsupportedSpecial),
195        }
196    }
197}
198
199impl super::FromSpecial for Vec<InputValue> {
200    fn from_special(
201        special: &super::Special,
202        params: &super::Params,
203    ) -> Result<Self, super::ExpressionError> {
204        match special {
205            super::Special::InputItemsOptionalContextSplit => {
206                // Expects input to be an object with 'items' (array) and optionally
207                // 'context'. Returns an array of objects, each with a 1-element 'items'
208                // array and a duplicated 'context' if present.
209                let input = match params {
210                    super::Params::Owned(o) => &o.input,
211                    super::Params::Ref(r) => r.input,
212                };
213                let InputValue::Object(obj) = input else {
214                    return Err(super::ExpressionError::UnsupportedSpecial);
215                };
216                let Some(InputValue::Array(items)) = obj.get("items") else {
217                    return Err(super::ExpressionError::UnsupportedSpecial);
218                };
219                let context = obj.get("context");
220                let mut result = Vec::with_capacity(items.len());
221                for item in items {
222                    let mut sub = IndexMap::new();
223                    sub.insert(
224                        "items".to_string(),
225                        InputValue::Array(vec![item.clone()]),
226                    );
227                    if let Some(ctx) = context {
228                        sub.insert("context".to_string(), ctx.clone());
229                    }
230                    result.push(InputValue::Object(sub));
231                }
232                Ok(result)
233            }
234            _ => Err(super::ExpressionError::UnsupportedSpecial),
235        }
236    }
237}
238
239impl Eq for InputValue {}
240
241impl std::hash::Hash for InputValue {
242    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
243        std::mem::discriminant(self).hash(state);
244        match self {
245            InputValue::RichContentPart(p) => p.hash(state),
246            InputValue::Object(map) => {
247                map.len().hash(state);
248                for (k, v) in map {
249                    k.hash(state);
250                    v.hash(state);
251                }
252            }
253            InputValue::Array(arr) => arr.hash(state),
254            InputValue::String(s) => s.hash(state),
255            InputValue::Integer(i) => i.hash(state),
256            InputValue::Number(f) => canonical_f64_bits(*f).hash(state),
257            InputValue::Boolean(b) => b.hash(state),
258        }
259    }
260}
261
262impl<'a> arbitrary::Arbitrary<'a> for InputValue {
263    fn arbitrary(
264        u: &mut arbitrary::Unstructured<'a>,
265    ) -> arbitrary::Result<Self> {
266        if u.arbitrary().unwrap_or(false) {
267            // Compound value
268            if u.arbitrary()? {
269                let mut map = IndexMap::new();
270                while u.arbitrary().unwrap_or(false) {
271                    map.insert(u.arbitrary::<String>()?, u.arbitrary()?);
272                }
273                Ok(InputValue::Object(map))
274            } else {
275                let mut arr = Vec::new();
276                while u.arbitrary().unwrap_or(false) {
277                    arr.push(InputValue::arbitrary(u)?);
278                }
279                Ok(InputValue::Array(arr))
280            }
281        } else {
282            // Leaf value
283            match u.int_in_range(0..=4)? {
284                0 => Ok(InputValue::RichContentPart(u.arbitrary()?)),
285                1 => Ok(InputValue::String(u.arbitrary()?)),
286                2 => Ok(InputValue::Integer(
287                    crate::arbitrary_util::arbitrary_i64(u)?,
288                )),
289                3 => Ok(InputValue::Number(
290                    crate::arbitrary_util::arbitrary_f64(u)?,
291                )),
292                _ => Ok(InputValue::Boolean(u.arbitrary()?)),
293            }
294        }
295    }
296}
297
298/// Normalizes f64 to canonical bits for consistent hashing.
299///
300/// - NaN (any bit pattern) → single canonical value
301/// - -0.0 → +0.0
302/// - Everything else (including ±inf) → to_bits()
303fn canonical_f64_bits(f: f64) -> u64 {
304    if f.is_nan() {
305        // All NaN patterns hash the same
306        0x7FF8_0000_0000_0000 // canonical quiet NaN
307    } else if f == 0.0 {
308        // +0.0 and -0.0 hash the same
309        0u64
310    } else {
311        f.to_bits()
312    }
313}
314
315impl InputValue {
316    /// Converts the input to a sequence of rich content parts.
317    ///
318    /// This is used to render structured input data as formatted JSON
319    /// in multimodal messages.
320    pub fn to_rich_content_parts(
321        self,
322        depth: usize,
323    ) -> impl Iterator<Item = agent::completions::message::RichContentPart>
324    {
325        enum Iter {
326            RichContentPart(RichContentPartIter),
327            Object(Box<ObjectIter>),
328            Array(Box<ArrayIter>),
329            Primitive(Option<String>),
330        }
331        impl Iter {
332            pub fn new(input: InputValue, depth: usize) -> Self {
333                match input {
334                    InputValue::RichContentPart(rich_content_part) => {
335                        Iter::RichContentPart(RichContentPartIter {
336                            first: true,
337                            part: Some(rich_content_part),
338                            last: true,
339                        })
340                    }
341                    InputValue::Object(object) => {
342                        Iter::Object(Box::new(ObjectIter {
343                            object: object.into_iter(),
344                            first: true,
345                            child: None,
346                            depth,
347                        }))
348                    }
349                    InputValue::Array(array) => {
350                        Iter::Array(Box::new(ArrayIter {
351                            array: array.into_iter(),
352                            first: true,
353                            child: None,
354                            depth,
355                        }))
356                    }
357                    InputValue::String(string) => Iter::Primitive(Some(
358                        format!("\"{}\"", json_escape::escape_str(&string),),
359                    )),
360                    InputValue::Integer(integer) => {
361                        Iter::Primitive(Some(integer.to_string()))
362                    }
363                    InputValue::Number(number) => {
364                        Iter::Primitive(Some(number.to_string()))
365                    }
366                    InputValue::Boolean(boolean) => {
367                        Iter::Primitive(Some(boolean.to_string()))
368                    }
369                }
370            }
371        }
372        impl Iterator for Iter {
373            type Item = agent::completions::message::RichContentPart;
374            fn next(&mut self) -> Option<Self::Item> {
375                match self {
376                    Iter::RichContentPart(rich_content_part_iter) => {
377                        rich_content_part_iter.next()
378                    }
379                    Iter::Object(object_iter) => object_iter.next(),
380                    Iter::Array(array_iter) => array_iter.next(),
381                    Iter::Primitive(primitive_option) => {
382                        primitive_option.take().map(|text| {
383                            agent::completions::message::RichContentPart::Text {
384                                text,
385                            }
386                        })
387                    }
388                }
389            }
390        }
391        struct RichContentPartIter {
392            first: bool,
393            part: Option<agent::completions::message::RichContentPart>,
394            last: bool,
395        }
396        impl Iterator for RichContentPartIter {
397            type Item = agent::completions::message::RichContentPart;
398            fn next(&mut self) -> Option<Self::Item> {
399                if self.first {
400                    self.first = false;
401                    Some(agent::completions::message::RichContentPart::Text {
402                        text: '"'.to_string(),
403                    })
404                } else if let Some(part) = self.part.take() {
405                    Some(part)
406                } else if self.last {
407                    self.last = false;
408                    Some(agent::completions::message::RichContentPart::Text {
409                        text: '"'.to_string(),
410                    })
411                } else {
412                    None
413                }
414            }
415        }
416        struct ObjectIter {
417            object: indexmap::map::IntoIter<String, InputValue>,
418            first: bool,
419            child: Option<Iter>,
420            depth: usize,
421        }
422        impl Iterator for ObjectIter {
423            type Item = agent::completions::message::RichContentPart;
424            fn next(&mut self) -> Option<Self::Item> {
425                if self.first {
426                    self.first = false;
427                    if let Some((key, input)) = self.object.next() {
428                        self.child = Some(Iter::new(input, self.depth + 1));
429                        Some(
430                            agent::completions::message::RichContentPart::Text {
431                                text: format!(
432                                    "{{\n{}\"{}\": ",
433                                    "    ".repeat(self.depth + 1),
434                                    key,
435                                ),
436                            },
437                        )
438                    } else {
439                        Some(
440                            agent::completions::message::RichContentPart::Text {
441                                text: format!("{{}}"),
442                            },
443                        )
444                    }
445                } else if let Some(child) = &mut self.child {
446                    if let Some(part) = child.next() {
447                        Some(part)
448                    } else if let Some((key, input)) = self.object.next() {
449                        self.child = Some(Iter::new(input, self.depth + 1));
450                        Some(
451                            agent::completions::message::RichContentPart::Text {
452                                text: format!(
453                                    ",\n{}\"{}\": ",
454                                    "    ".repeat(self.depth + 1),
455                                    key,
456                                ),
457                            },
458                        )
459                    } else {
460                        self.child = None;
461                        Some(
462                            agent::completions::message::RichContentPart::Text {
463                                text: format!(
464                                    "\n{}}}",
465                                    "    ".repeat(self.depth)
466                                ),
467                            },
468                        )
469                    }
470                } else {
471                    None
472                }
473            }
474        }
475        struct ArrayIter {
476            array: std::vec::IntoIter<InputValue>,
477            first: bool,
478            child: Option<Iter>,
479            depth: usize,
480        }
481        impl Iterator for ArrayIter {
482            type Item = agent::completions::message::RichContentPart;
483            fn next(&mut self) -> Option<Self::Item> {
484                if self.first {
485                    self.first = false;
486                    if let Some(input) = self.array.next() {
487                        self.child = Some(Iter::new(input, self.depth + 1));
488                        Some(
489                            agent::completions::message::RichContentPart::Text {
490                                text: format!(
491                                    "[\n{}",
492                                    "    ".repeat(self.depth + 1)
493                                ),
494                            },
495                        )
496                    } else {
497                        Some(
498                            agent::completions::message::RichContentPart::Text {
499                                text: format!("[]"),
500                            },
501                        )
502                    }
503                } else if let Some(child) = &mut self.child {
504                    if let Some(part) = child.next() {
505                        Some(part)
506                    } else if let Some(input) = self.array.next() {
507                        self.child = Some(Iter::new(input, self.depth + 1));
508                        Some(
509                            agent::completions::message::RichContentPart::Text {
510                                text: format!(
511                                    ",\n{}",
512                                    "    ".repeat(self.depth + 1),
513                                ),
514                            },
515                        )
516                    } else {
517                        self.child = None;
518                        Some(
519                            agent::completions::message::RichContentPart::Text {
520                                text: format!(
521                                    "\n{}]",
522                                    "    ".repeat(self.depth)
523                                ),
524                            },
525                        )
526                    }
527                } else {
528                    None
529                }
530            }
531        }
532        Iter::new(self, depth)
533    }
534}
535
536/// An input value that may contain expressions (pre-compilation).
537///
538/// Similar to [`InputValue`] but object values and array elements can be
539/// expressions (JMESPath or Starlark) that are evaluated during compilation.
540#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
541#[serde(untagged)]
542#[schemars(rename = "functions.expression.InputValueExpression")]
543pub enum InputValueExpression {
544    /// Rich content (image, audio, video, file).
545    #[schemars(title = "RichContentPart")]
546    RichContentPart(agent::completions::message::RichContentPart),
547    /// An object with values that may be expressions.
548    #[schemars(title = "Object")]
549    Object(IndexMap<String, super::WithExpression<InputValueExpression>>),
550    /// An array with elements that may be expressions.
551    #[schemars(title = "Array")]
552    Array(Vec<super::WithExpression<InputValueExpression>>),
553    /// A string value.
554    #[schemars(title = "String")]
555    String(String),
556    /// An integer value.
557    #[schemars(title = "Integer")]
558    Integer(i64),
559    /// A floating-point number.
560    #[schemars(title = "Number")]
561    Number(f64),
562    /// A boolean value.
563    #[schemars(title = "Boolean")]
564    Boolean(bool),
565}
566
567impl InputValueExpression {
568    /// Compiles the expression into a concrete [`InputValue`].
569    pub fn compile(
570        self,
571        params: &super::Params,
572    ) -> Result<InputValue, super::ExpressionError> {
573        match self {
574            InputValueExpression::RichContentPart(rich_content_part) => {
575                Ok(InputValue::RichContentPart(rich_content_part))
576            }
577            InputValueExpression::Object(object) => {
578                let mut compiled_object = IndexMap::with_capacity(object.len());
579                for (key, value) in object {
580                    compiled_object.insert(
581                        key,
582                        value.compile_one(params)?.compile(params)?,
583                    );
584                }
585                Ok(InputValue::Object(compiled_object))
586            }
587            InputValueExpression::Array(array) => {
588                let mut compiled_array = Vec::with_capacity(array.len());
589                for item in array {
590                    match item.compile_one_or_many(params)? {
591                        super::OneOrMany::One(one_item) => {
592                            compiled_array.push(one_item.compile(params)?);
593                        }
594                        super::OneOrMany::Many(many_items) => {
595                            for item in many_items {
596                                compiled_array.push(item.compile(params)?);
597                            }
598                        }
599                    }
600                }
601                Ok(InputValue::Array(compiled_array))
602            }
603            InputValueExpression::String(string) => {
604                Ok(InputValue::String(string))
605            }
606            InputValueExpression::Integer(integer) => {
607                Ok(InputValue::Integer(integer))
608            }
609            InputValueExpression::Number(number) => {
610                Ok(InputValue::Number(number))
611            }
612            InputValueExpression::Boolean(boolean) => {
613                Ok(InputValue::Boolean(boolean))
614            }
615        }
616    }
617}
618
619impl super::FromStarlarkValue for InputValueExpression {
620    fn from_starlark_value(
621        value: &StarlarkValue,
622    ) -> Result<Self, super::ExpressionError> {
623        if value.is_none() {
624            return Err(super::ExpressionError::StarlarkConversionError(
625                "InputValueExpression: expected value".into(),
626            ));
627        }
628        if let Ok(Some(b)) = bool::unpack_value(*value) {
629            return Ok(InputValueExpression::Boolean(b));
630        }
631        if let Ok(Some(i)) = i64::unpack_value(*value) {
632            return Ok(InputValueExpression::Integer(i));
633        }
634        if let Ok(Some(UnpackFloat(f))) = UnpackFloat::unpack_value(*value) {
635            return Ok(InputValueExpression::Number(f));
636        }
637        if let Ok(Some(s)) = <&str as UnpackValue>::unpack_value(*value) {
638            return Ok(InputValueExpression::String(s.to_owned()));
639        }
640        if let Some(list) = StarlarkListRef::from_value(*value) {
641            let mut items = Vec::with_capacity(list.len());
642            for v in list.iter() {
643                items.push(super::WithExpression::Value(
644                    InputValueExpression::from_starlark_value(&v)?,
645                ));
646            }
647            return Ok(InputValueExpression::Array(items));
648        }
649        if let Some(dict) = StarlarkDictRef::from_value(*value) {
650            // Try RichContentPart first if "type" matches a known variant
651            let mut type_value = None;
652            for (k, v) in dict.iter() {
653                if let Ok(Some("type")) = <&str as UnpackValue>::unpack_value(k)
654                {
655                    type_value =
656                        <&str as UnpackValue>::unpack_value(v).ok().flatten();
657                    break;
658                }
659            }
660            if matches!(
661                type_value,
662                Some(
663                    "text"
664                        | "image_url"
665                        | "input_audio"
666                        | "input_video"
667                        | "video_url"
668                        | "file"
669                )
670            ) {
671                if let Ok(part) = agent::completions::message::RichContentPart::from_starlark_value(value) {
672                    return Ok(InputValueExpression::RichContentPart(part));
673                }
674            }
675            let mut map = IndexMap::with_capacity(dict.len());
676            for (k, v) in dict.iter() {
677                let key = <&str as UnpackValue>::unpack_value(k)
678                    .map_err(|e| {
679                        super::ExpressionError::StarlarkConversionError(
680                            e.to_string(),
681                        )
682                    })?
683                    .ok_or_else(|| {
684                        super::ExpressionError::StarlarkConversionError(
685                            "InputValueExpression: expected string key".into(),
686                        )
687                    })?
688                    .to_owned();
689                map.insert(
690                    key,
691                    super::WithExpression::Value(
692                        InputValueExpression::from_starlark_value(&v)?,
693                    ),
694                );
695            }
696            return Ok(InputValueExpression::Object(map));
697        }
698        Err(super::ExpressionError::StarlarkConversionError(format!(
699            "InputValueExpression: unsupported type: {}",
700            value.get_type()
701        )))
702    }
703}
704
705impl<'a> arbitrary::Arbitrary<'a> for InputValueExpression {
706    fn arbitrary(
707        u: &mut arbitrary::Unstructured<'a>,
708    ) -> arbitrary::Result<Self> {
709        if u.arbitrary().unwrap_or(false) {
710            // Compound value
711            if u.arbitrary()? {
712                let mut map = IndexMap::new();
713                while u.arbitrary().unwrap_or(false) {
714                    map.insert(u.arbitrary::<String>()?, u.arbitrary()?);
715                }
716                Ok(InputValueExpression::Object(map))
717            } else {
718                let mut arr = Vec::new();
719                while u.arbitrary().unwrap_or(false) {
720                    arr.push(u.arbitrary()?);
721                }
722                Ok(InputValueExpression::Array(arr))
723            }
724        } else {
725            // Leaf value
726            match u.int_in_range(0..=4)? {
727                0 => Ok(InputValueExpression::RichContentPart(u.arbitrary()?)),
728                1 => Ok(InputValueExpression::String(u.arbitrary()?)),
729                2 => Ok(InputValueExpression::Integer(
730                    crate::arbitrary_util::arbitrary_i64(u)?,
731                )),
732                3 => Ok(InputValueExpression::Number(
733                    crate::arbitrary_util::arbitrary_f64(u)?,
734                )),
735                _ => Ok(InputValueExpression::Boolean(u.arbitrary()?)),
736            }
737        }
738    }
739}
740
741impl super::FromSpecial for InputValueExpression {
742    fn from_special(
743        special: &super::Special,
744        params: &super::Params,
745    ) -> Result<Self, super::ExpressionError> {
746        let input = InputValue::from_special(special, params)?;
747        Ok(input_to_input_expression(input))
748    }
749}
750
751fn input_to_input_expression(input: InputValue) -> InputValueExpression {
752    match input {
753        InputValue::RichContentPart(p) => {
754            InputValueExpression::RichContentPart(p)
755        }
756        InputValue::Object(map) => InputValueExpression::Object(
757            map.into_iter()
758                .map(|(k, v)| {
759                    (
760                        k,
761                        super::WithExpression::Value(
762                            input_to_input_expression(v),
763                        ),
764                    )
765                })
766                .collect(),
767        ),
768        InputValue::Array(arr) => InputValueExpression::Array(
769            arr.into_iter()
770                .map(|v| {
771                    super::WithExpression::Value(input_to_input_expression(v))
772                })
773                .collect(),
774        ),
775        InputValue::String(s) => InputValueExpression::String(s),
776        InputValue::Integer(i) => InputValueExpression::Integer(i),
777        InputValue::Number(n) => InputValueExpression::Number(n),
778        InputValue::Boolean(b) => InputValueExpression::Boolean(b),
779    }
780}