Skip to main content

foundation_models/
content.rs

1//! [`GeneratedContent`] — structured model output represented as JSON.
2
3use core::ffi::c_char;
4use core::fmt;
5use std::collections::BTreeMap;
6use std::ffi::{CStr, CString};
7
8use serde::de::DeserializeOwned;
9use serde::{Deserialize, Serialize};
10use serde_json::{Map, Number, Value};
11
12use crate::error::FMError;
13use crate::ffi;
14use crate::schema::{DynamicGenerationSchema, Generable, GenerationSchema};
15
16/// Rust analogue of FoundationModels' `ConvertibleFromGeneratedContent`.
17pub trait FromGeneratedContent: Sized {
18    /// Decode a Rust value from generated content.
19    fn from_generated_content(content: &GeneratedContent) -> Result<Self, FMError>;
20}
21
22/// Rust analogue of FoundationModels' `ConvertibleToGeneratedContent`.
23pub trait ToGeneratedContent {
24    /// Convert a Rust value into generated content.
25    fn to_generated_content(&self) -> Result<GeneratedContent, FMError>;
26}
27
28#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
29pub(crate) struct BridgeGenerationId {
30    pub(crate) token: String,
31    pub(crate) description: String,
32}
33
34#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
35pub(crate) struct BridgeGeneratedContent {
36    pub(crate) json: String,
37    #[serde(rename = "generationID")]
38    pub(crate) generation_id: Option<BridgeGenerationId>,
39}
40
41fn owned_string(ptr: *mut c_char) -> String {
42    if ptr.is_null() {
43        return String::new();
44    }
45    let value = unsafe { CStr::from_ptr(ptr) }
46        .to_string_lossy()
47        .into_owned();
48    unsafe { ffi::fm_string_free(ptr) };
49    value
50}
51
52fn call_string_bridge<F>(call: F) -> Result<String, FMError>
53where
54    F: FnOnce(*mut *mut c_char, *mut *mut c_char) -> i32,
55{
56    let mut output: *mut c_char = core::ptr::null_mut();
57    let mut error: *mut c_char = core::ptr::null_mut();
58    let status = call(&mut output, &mut error);
59    if status != ffi::status::OK {
60        if !output.is_null() {
61            unsafe { ffi::fm_string_free(output) };
62        }
63        return Err(crate::error::from_swift(status, error));
64    }
65    if output.is_null() {
66        return Err(FMError::Unknown {
67            code: ffi::status::UNKNOWN,
68            message: "Swift bridge returned success without a payload".into(),
69        });
70    }
71    Ok(owned_string(output))
72}
73
74/// Rust wrapper for FoundationModels' opaque `GenerationID`.
75#[derive(Debug, Clone, PartialEq, Eq, Hash)]
76pub struct GenerationId {
77    token: String,
78    description: String,
79}
80
81impl GenerationId {
82    /// Create a fresh opaque generation identifier.
83    ///
84    /// # Errors
85    ///
86    /// Returns an [`FMError`] if the Swift bridge cannot create the identifier.
87    pub fn new() -> Result<Self, FMError> {
88        let json = call_string_bridge(|output, error| unsafe {
89            ffi::fm_generation_id_create(output, error)
90        })?;
91        let bridge: BridgeGenerationId = serde_json::from_str(&json)
92            .map_err(|error| FMError::DecodingFailure(error.to_string()))?;
93        Ok(Self::from_bridge(bridge))
94    }
95
96    /// Best-effort string representation of the opaque identifier.
97    #[must_use]
98    pub fn best_effort_string(&self) -> &str {
99        &self.description
100    }
101
102    pub(crate) fn to_bridge(&self) -> BridgeGenerationId {
103        BridgeGenerationId {
104            token: self.token.clone(),
105            description: self.description.clone(),
106        }
107    }
108
109    pub(crate) fn from_bridge(bridge: BridgeGenerationId) -> Self {
110        Self {
111            token: bridge.token,
112            description: bridge.description,
113        }
114    }
115}
116
117impl fmt::Display for GenerationId {
118    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119        f.write_str(self.best_effort_string())
120    }
121}
122
123/// Rust wrapper for Foundation's `Decimal` generable/content surface.
124#[derive(Debug, Clone, PartialEq, Eq, Hash)]
125pub struct Decimal {
126    value: String,
127}
128
129impl Decimal {
130    /// Create a decimal wrapper from its canonical string form.
131    #[must_use]
132    pub fn new(value: impl Into<String>) -> Self {
133        Self {
134            value: value.into(),
135        }
136    }
137
138    /// Borrow the decimal's string form.
139    #[must_use]
140    pub fn as_str(&self) -> &str {
141        &self.value
142    }
143}
144
145impl From<String> for Decimal {
146    fn from(value: String) -> Self {
147        Self::new(value)
148    }
149}
150
151impl From<&str> for Decimal {
152    fn from(value: &str) -> Self {
153        Self::new(value)
154    }
155}
156
157impl AsRef<str> for Decimal {
158    fn as_ref(&self) -> &str {
159        self.as_str()
160    }
161}
162
163impl fmt::Display for Decimal {
164    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165        f.write_str(self.as_str())
166    }
167}
168
169/// Rust analogue of `GeneratedContent.Kind`.
170#[derive(Debug, Clone, PartialEq)]
171pub enum GeneratedContentKind {
172    Null,
173    Bool(bool),
174    Number(f64),
175    String(String),
176    Array(Vec<GeneratedContent>),
177    Structure {
178        properties: BTreeMap<String, GeneratedContent>,
179        ordered_keys: Vec<String>,
180    },
181}
182
183impl GeneratedContentKind {
184    fn into_value(self) -> Result<Value, FMError> {
185        match self {
186            Self::Null => Ok(Value::Null),
187            Self::Bool(value) => Ok(Value::Bool(value)),
188            Self::Number(value) => Number::from_f64(value).map(Value::Number).ok_or_else(|| {
189                FMError::InvalidArgument("generated content number must be finite".into())
190            }),
191            Self::String(value) => Ok(Value::String(value)),
192            Self::Array(values) => Ok(Value::Array(
193                values
194                    .into_iter()
195                    .map(GeneratedContent::into_raw_value)
196                    .collect(),
197            )),
198            Self::Structure {
199                properties,
200                ordered_keys,
201            } => {
202                let mut object = Map::new();
203                for key in ordered_keys {
204                    if let Some(value) = properties.get(&key) {
205                        object.insert(key, value.raw_value().clone());
206                    }
207                }
208                for (key, value) in properties {
209                    if !object.contains_key(&key) {
210                        object.insert(key, value.into_raw_value());
211                    }
212                }
213                Ok(Value::Object(object))
214            }
215        }
216    }
217}
218
219/// A piece of generated structured content.
220///
221/// Apple models structured generations as `GeneratedContent`; the Rust wrapper
222/// stores the JSON value plus the metadata that Apple's streaming API exposes.
223#[derive(Debug, Clone, PartialEq)]
224pub struct GeneratedContent {
225    value: Value,
226    generation_id: Option<GenerationId>,
227    is_complete: bool,
228}
229
230impl GeneratedContent {
231    /// Parse a JSON string into generated content.
232    ///
233    /// # Errors
234    ///
235    /// Returns [`FMError::InvalidArgument`] if `json` is not valid JSON.
236    pub fn from_json_str(json: &str) -> Result<Self, FMError> {
237        Self::from_json_str_with_id(json, None)
238    }
239
240    /// Parse a JSON string into generated content with an attached generation ID.
241    ///
242    /// # Errors
243    ///
244    /// Returns [`FMError::InvalidArgument`] if `json` is not valid JSON.
245    pub fn from_json_str_with_id(
246        json: &str,
247        generation_id: impl Into<Option<GenerationId>>,
248    ) -> Result<Self, FMError> {
249        let value = serde_json::from_str(json).map_err(|error| {
250            FMError::InvalidArgument(format!("generated content JSON is invalid: {error}"))
251        })?;
252        Ok(Self {
253            value,
254            generation_id: generation_id.into(),
255            is_complete: true,
256        })
257    }
258
259    /// Convert a serializable Rust value into generated content.
260    ///
261    /// # Errors
262    ///
263    /// Returns [`FMError::InvalidArgument`] if `value` cannot be encoded as JSON.
264    pub fn from_value<T>(value: T) -> Result<Self, FMError>
265    where
266        T: Serialize,
267    {
268        Self::from_value_with_id(value, None)
269    }
270
271    /// Convert a serializable Rust value into generated content with an ID.
272    ///
273    /// # Errors
274    ///
275    /// Returns [`FMError::InvalidArgument`] if `value` cannot be encoded as JSON.
276    pub fn from_value_with_id<T>(
277        value: T,
278        generation_id: impl Into<Option<GenerationId>>,
279    ) -> Result<Self, FMError>
280    where
281        T: Serialize,
282    {
283        let value = serde_json::to_value(value).map_err(|error| {
284            FMError::InvalidArgument(format!(
285                "generated content value is not JSON-serializable: {error}"
286            ))
287        })?;
288        Ok(Self {
289            value,
290            generation_id: generation_id.into(),
291            is_complete: true,
292        })
293    }
294
295    /// Build generated content from a `GeneratedContentKind` value.
296    ///
297    /// # Errors
298    ///
299    /// Returns [`FMError::InvalidArgument`] if the kind cannot be represented as valid JSON.
300    pub fn from_kind(kind: GeneratedContentKind) -> Result<Self, FMError> {
301        Self::from_kind_with_id(kind, None)
302    }
303
304    /// Build generated content from a `GeneratedContentKind` value with an ID.
305    ///
306    /// # Errors
307    ///
308    /// Returns [`FMError::InvalidArgument`] if the kind cannot be represented as valid JSON.
309    pub fn from_kind_with_id(
310        kind: GeneratedContentKind,
311        generation_id: impl Into<Option<GenerationId>>,
312    ) -> Result<Self, FMError> {
313        Ok(Self {
314            value: kind.into_value()?,
315            generation_id: generation_id.into(),
316            is_complete: true,
317        })
318    }
319
320    /// Build generated content from a sequence of values.
321    ///
322    /// # Errors
323    ///
324    /// Returns [`FMError`] if any element cannot be converted.
325    pub fn from_elements<T>(elements: impl IntoIterator<Item = T>) -> Result<Self, FMError>
326    where
327        T: ToGeneratedContent,
328    {
329        Self::from_elements_with_id(elements, None)
330    }
331
332    /// Build generated content from a sequence of values with an ID.
333    ///
334    /// # Errors
335    ///
336    /// Returns [`FMError`] if any element cannot be converted.
337    pub fn from_elements_with_id<T>(
338        elements: impl IntoIterator<Item = T>,
339        generation_id: impl Into<Option<GenerationId>>,
340    ) -> Result<Self, FMError>
341    where
342        T: ToGeneratedContent,
343    {
344        let values = elements
345            .into_iter()
346            .map(|value| {
347                value
348                    .to_generated_content()
349                    .map(GeneratedContent::into_raw_value)
350            })
351            .collect::<Result<Vec<_>, _>>()?;
352        Ok(Self {
353            value: Value::Array(values),
354            generation_id: generation_id.into(),
355            is_complete: true,
356        })
357    }
358
359    /// Build generated content from key/value properties.
360    ///
361    /// Later duplicates overwrite earlier ones.
362    ///
363    /// # Errors
364    ///
365    /// Returns [`FMError`] if any value cannot be converted.
366    pub fn from_properties<K, V>(
367        properties: impl IntoIterator<Item = (K, V)>,
368    ) -> Result<Self, FMError>
369    where
370        K: Into<String>,
371        V: ToGeneratedContent,
372    {
373        Self::from_properties_with_id(properties, None)
374    }
375
376    /// Build generated content from key/value properties with an ID.
377    ///
378    /// Later duplicates overwrite earlier ones.
379    ///
380    /// # Errors
381    ///
382    /// Returns [`FMError`] if any value cannot be converted.
383    pub fn from_properties_with_id<K, V>(
384        properties: impl IntoIterator<Item = (K, V)>,
385        generation_id: impl Into<Option<GenerationId>>,
386    ) -> Result<Self, FMError>
387    where
388        K: Into<String>,
389        V: ToGeneratedContent,
390    {
391        let object = properties
392            .into_iter()
393            .map(|(key, value)| {
394                value
395                    .to_generated_content()
396                    .map(|content| (key.into(), content.into_raw_value()))
397            })
398            .collect::<Result<Map<String, Value>, _>>()?;
399        Ok(Self {
400            value: Value::Object(object),
401            generation_id: generation_id.into(),
402            is_complete: true,
403        })
404    }
405
406    /// Build generated content from key/value properties, combining duplicates.
407    ///
408    /// # Errors
409    ///
410    /// Returns [`FMError`] if any value cannot be converted or `combine` fails.
411    pub fn from_properties_with<K, V, F>(
412        properties: impl IntoIterator<Item = (K, V)>,
413        generation_id: impl Into<Option<GenerationId>>,
414        mut combine: F,
415    ) -> Result<Self, FMError>
416    where
417        K: Into<String>,
418        V: ToGeneratedContent,
419        F: FnMut(GeneratedContent, GeneratedContent) -> Result<GeneratedContent, FMError>,
420    {
421        let mut object = Map::new();
422        for (key, value) in properties {
423            let key = key.into();
424            let value = value.to_generated_content()?;
425            if let Some(existing) = object.remove(&key) {
426                let combined = combine(GeneratedContent::try_from(existing)?, value)?;
427                object.insert(key, combined.into_raw_value());
428            } else {
429                object.insert(key, value.into_raw_value());
430            }
431        }
432        Ok(Self {
433            value: Value::Object(object),
434            generation_id: generation_id.into(),
435            is_complete: true,
436        })
437    }
438
439    /// Build a value from bridge metadata.
440    pub(crate) fn from_bridge_payload(
441        payload: BridgeGeneratedContent,
442        is_complete: bool,
443    ) -> Result<Self, FMError> {
444        let mut content = Self::from_json_str_with_id(
445            &payload.json,
446            payload.generation_id.map(GenerationId::from_bridge),
447        )?;
448        content.is_complete = is_complete;
449        Ok(content)
450    }
451
452    pub(crate) fn to_bridge_value(&self) -> Result<Value, FMError> {
453        serde_json::to_value(BridgeGeneratedContent {
454            json: self.json_string()?,
455            generation_id: self.generation_id.as_ref().map(GenerationId::to_bridge),
456        })
457        .map_err(|error| {
458            FMError::InvalidArgument(format!(
459                "generated content bridge payload is not JSON-serializable: {error}"
460            ))
461        })
462    }
463
464    /// Return the underlying JSON value.
465    #[must_use]
466    pub const fn raw_value(&self) -> &Value {
467        &self.value
468    }
469
470    /// Consume the content and return the underlying JSON value.
471    #[must_use]
472    pub fn into_raw_value(self) -> Value {
473        self.value
474    }
475
476    /// Return the typed content kind.
477    #[must_use]
478    pub fn kind(&self) -> GeneratedContentKind {
479        kind_from_value(&self.value)
480    }
481
482    /// Serialize the content back to a compact JSON string.
483    ///
484    /// # Errors
485    ///
486    /// Returns [`FMError::Unknown`] if serialization fails.
487    pub fn json_string(&self) -> Result<String, FMError> {
488        serde_json::to_string(&self.value).map_err(|error| FMError::Unknown {
489            code: crate::ffi::status::UNKNOWN,
490            message: format!("failed to serialize generated content: {error}"),
491        })
492    }
493
494    /// Serialize the content as pretty JSON.
495    ///
496    /// # Errors
497    ///
498    /// Returns [`FMError::Unknown`] if serialization fails.
499    pub fn json_string_pretty(&self) -> Result<String, FMError> {
500        serde_json::to_string_pretty(&self.value).map_err(|error| FMError::Unknown {
501            code: crate::ffi::status::UNKNOWN,
502            message: format!("failed to serialize generated content: {error}"),
503        })
504    }
505
506    /// Decode the content into a Rust value.
507    ///
508    /// # Errors
509    ///
510    /// Returns [`FMError::DecodingFailure`] if the JSON value does not match `T`.
511    pub fn value<T>(&self) -> Result<T, FMError>
512    where
513        T: DeserializeOwned,
514    {
515        serde_json::from_value(self.value.clone())
516            .map_err(|error| FMError::DecodingFailure(error.to_string()))
517    }
518
519    /// Decode a named property from an object content value.
520    ///
521    /// # Errors
522    ///
523    /// Returns [`FMError::DecodingFailure`] if the value is not an object, the
524    /// property does not exist, or the property cannot be decoded as `T`.
525    pub fn value_for_property<T>(&self, property: &str) -> Result<T, FMError>
526    where
527        T: DeserializeOwned,
528    {
529        let Value::Object(map) = &self.value else {
530            return Err(FMError::DecodingFailure(
531                "generated content is not an object".into(),
532            ));
533        };
534        let value = map.get(property).cloned().ok_or_else(|| {
535            FMError::DecodingFailure(format!(
536                "generated content is missing property `{property}`"
537            ))
538        })?;
539        serde_json::from_value(value).map_err(|error| FMError::DecodingFailure(error.to_string()))
540    }
541
542    /// Whether Apple's structured stream reported this content as complete.
543    #[must_use]
544    pub const fn is_complete(&self) -> bool {
545        self.is_complete
546    }
547
548    /// Apple's opaque generation identifier, if one was attached.
549    #[must_use]
550    pub fn generation_id(&self) -> Option<&str> {
551        self.generation_id
552            .as_ref()
553            .map(GenerationId::best_effort_string)
554    }
555
556    /// Borrow the typed generation identifier handle, if one was attached.
557    #[must_use]
558    pub fn generation_id_handle(&self) -> Option<&GenerationId> {
559        self.generation_id.as_ref()
560    }
561
562    /// Replace the attached generation identifier.
563    #[must_use]
564    pub fn with_generation_id(mut self, generation_id: impl Into<Option<GenerationId>>) -> Self {
565        self.generation_id = generation_id.into();
566        self
567    }
568}
569
570fn kind_from_value(value: &Value) -> GeneratedContentKind {
571    match value {
572        Value::Null => GeneratedContentKind::Null,
573        Value::Bool(value) => GeneratedContentKind::Bool(*value),
574        Value::Number(value) => GeneratedContentKind::Number(value.as_f64().unwrap_or_default()),
575        Value::String(value) => GeneratedContentKind::String(value.clone()),
576        Value::Array(values) => GeneratedContentKind::Array(
577            values
578                .iter()
579                .cloned()
580                .map(|value| {
581                    GeneratedContent::try_from(value)
582                        .expect("JSON arrays are valid generated content")
583                })
584                .collect(),
585        ),
586        Value::Object(map) => GeneratedContentKind::Structure {
587            properties: map
588                .iter()
589                .map(|(key, value)| {
590                    (
591                        key.clone(),
592                        GeneratedContent::try_from(value.clone())
593                            .expect("JSON objects are valid generated content"),
594                    )
595                })
596                .collect(),
597            ordered_keys: map.keys().cloned().collect(),
598        },
599    }
600}
601
602impl TryFrom<Value> for GeneratedContent {
603    type Error = FMError;
604
605    fn try_from(value: Value) -> Result<Self, Self::Error> {
606        Ok(Self {
607            value,
608            generation_id: None,
609            is_complete: true,
610        })
611    }
612}
613
614impl From<GeneratedContent> for Value {
615    fn from(value: GeneratedContent) -> Self {
616        value.value
617    }
618}
619
620macro_rules! impl_scalar_content {
621    ($($ty:ty),+ $(,)?) => {
622        $(
623            impl From<$ty> for GeneratedContent {
624                fn from(value: $ty) -> Self {
625                    Self {
626                        value: serde_json::to_value(value)
627                            .expect("scalar values must always be JSON-serializable"),
628                        generation_id: None,
629                        is_complete: true,
630                    }
631                }
632            }
633        )+
634    };
635}
636
637impl_scalar_content!(bool, f32, f64, i8, i16, i32, i64, u8, u16, u32, u64);
638
639impl From<String> for GeneratedContent {
640    fn from(value: String) -> Self {
641        Self {
642            value: Value::String(value),
643            generation_id: None,
644            is_complete: true,
645        }
646    }
647}
648
649impl From<&str> for GeneratedContent {
650    fn from(value: &str) -> Self {
651        Self::from(value.to_owned())
652    }
653}
654
655impl<T> From<Vec<T>> for GeneratedContent
656where
657    T: Into<GeneratedContent>,
658{
659    fn from(values: Vec<T>) -> Self {
660        Self {
661            value: Value::Array(values.into_iter().map(|value| value.into().value).collect()),
662            generation_id: None,
663            is_complete: true,
664        }
665    }
666}
667
668impl FromGeneratedContent for GeneratedContent {
669    fn from_generated_content(content: &GeneratedContent) -> Result<Self, FMError> {
670        Ok(content.clone())
671    }
672}
673
674impl ToGeneratedContent for GeneratedContent {
675    fn to_generated_content(&self) -> Result<GeneratedContent, FMError> {
676        Ok(self.clone())
677    }
678}
679
680impl FromGeneratedContent for Value {
681    fn from_generated_content(content: &GeneratedContent) -> Result<Self, FMError> {
682        Ok(content.raw_value().clone())
683    }
684}
685
686impl ToGeneratedContent for Value {
687    fn to_generated_content(&self) -> Result<GeneratedContent, FMError> {
688        GeneratedContent::from_value(self)
689    }
690}
691
692impl FromGeneratedContent for String {
693    fn from_generated_content(content: &GeneratedContent) -> Result<Self, FMError> {
694        content.value()
695    }
696}
697
698impl ToGeneratedContent for String {
699    fn to_generated_content(&self) -> Result<GeneratedContent, FMError> {
700        Ok(GeneratedContent::from(self.clone()))
701    }
702}
703
704impl ToGeneratedContent for str {
705    fn to_generated_content(&self) -> Result<GeneratedContent, FMError> {
706        Ok(GeneratedContent::from(self))
707    }
708}
709
710impl FromGeneratedContent for bool {
711    fn from_generated_content(content: &GeneratedContent) -> Result<Self, FMError> {
712        content.value()
713    }
714}
715
716impl ToGeneratedContent for bool {
717    fn to_generated_content(&self) -> Result<GeneratedContent, FMError> {
718        Ok(GeneratedContent::from(*self))
719    }
720}
721
722macro_rules! impl_numeric_conversion {
723    ($($ty:ty),+ $(,)?) => {
724        $(
725            impl FromGeneratedContent for $ty {
726                fn from_generated_content(content: &GeneratedContent) -> Result<Self, FMError> {
727                    content.value()
728                }
729            }
730
731            impl ToGeneratedContent for $ty {
732                fn to_generated_content(&self) -> Result<GeneratedContent, FMError> {
733                    Ok(GeneratedContent::from(*self))
734                }
735            }
736        )+
737    };
738}
739
740impl_numeric_conversion!(f32, f64, i8, i16, i32, i64, u8, u16, u32, u64);
741
742impl FromGeneratedContent for Decimal {
743    fn from_generated_content(content: &GeneratedContent) -> Result<Self, FMError> {
744        let json = CString::new(content.json_string()?).map_err(|error| {
745            FMError::InvalidArgument(format!(
746                "generated content JSON contains an interior NUL byte: {error}"
747            ))
748        })?;
749        let value = call_string_bridge(|output, error| unsafe {
750            ffi::fm_decimal_from_generated_content_json(json.as_ptr(), output, error)
751        })?;
752        Ok(Self::new(value))
753    }
754}
755
756impl ToGeneratedContent for Decimal {
757    fn to_generated_content(&self) -> Result<GeneratedContent, FMError> {
758        let decimal = CString::new(self.as_str()).map_err(|error| {
759            FMError::InvalidArgument(format!(
760                "decimal string contains an interior NUL byte: {error}"
761            ))
762        })?;
763        let json = call_string_bridge(|output, error| unsafe {
764            ffi::fm_decimal_to_generated_content_json(decimal.as_ptr(), output, error)
765        })?;
766        GeneratedContent::from_json_str(&json)
767    }
768}
769
770impl<T> FromGeneratedContent for Vec<T>
771where
772    T: FromGeneratedContent,
773{
774    fn from_generated_content(content: &GeneratedContent) -> Result<Self, FMError> {
775        let values: Vec<Value> = content.value()?;
776        values
777            .iter()
778            .map(|value| {
779                let nested = GeneratedContent::try_from(value.clone())?;
780                T::from_generated_content(&nested)
781            })
782            .collect()
783    }
784}
785
786impl<T> ToGeneratedContent for Vec<T>
787where
788    T: ToGeneratedContent,
789{
790    fn to_generated_content(&self) -> Result<GeneratedContent, FMError> {
791        let values = self
792            .iter()
793            .map(ToGeneratedContent::to_generated_content)
794            .collect::<Result<Vec<_>, _>>()?;
795        Ok(GeneratedContent {
796            value: Value::Array(
797                values
798                    .into_iter()
799                    .map(GeneratedContent::into_raw_value)
800                    .collect(),
801            ),
802            generation_id: None,
803            is_complete: true,
804        })
805    }
806}
807
808impl<T> FromGeneratedContent for Option<T>
809where
810    T: FromGeneratedContent,
811{
812    fn from_generated_content(content: &GeneratedContent) -> Result<Self, FMError> {
813        if content.raw_value().is_null() {
814            return Ok(None);
815        }
816        T::from_generated_content(content).map(Some)
817    }
818}
819
820impl<T> ToGeneratedContent for Option<T>
821where
822    T: ToGeneratedContent,
823{
824    fn to_generated_content(&self) -> Result<GeneratedContent, FMError> {
825        match self {
826            Some(value) => value.to_generated_content(),
827            None => GeneratedContent::from_value(Option::<Value>::None),
828        }
829    }
830}
831
832impl Generable for GeneratedContent {
833    fn generation_schema() -> Result<GenerationSchema, FMError> {
834        Ok(GenerationSchema::generated_content())
835    }
836}
837
838impl Generable for String {
839    fn generation_schema() -> Result<GenerationSchema, FMError> {
840        Ok(GenerationSchema::string())
841    }
842}
843
844impl Generable for bool {
845    fn generation_schema() -> Result<GenerationSchema, FMError> {
846        Ok(GenerationSchema::boolean())
847    }
848}
849
850impl Generable for Decimal {
851    fn generation_schema() -> Result<GenerationSchema, FMError> {
852        GenerationSchema::from_dynamic(DynamicGenerationSchema::decimal(), [])
853    }
854}
855
856macro_rules! impl_integer_generable {
857    ($($ty:ty),+ $(,)?) => {
858        $(
859            impl Generable for $ty {
860                fn generation_schema() -> Result<GenerationSchema, FMError> {
861                    Ok(GenerationSchema::integer())
862                }
863            }
864        )+
865    };
866}
867
868macro_rules! impl_number_generable {
869    ($($ty:ty),+ $(,)?) => {
870        $(
871            impl Generable for $ty {
872                fn generation_schema() -> Result<GenerationSchema, FMError> {
873                    Ok(GenerationSchema::number())
874                }
875            }
876        )+
877    };
878}
879
880impl_integer_generable!(i8, i16, i32, i64, u8, u16, u32, u64);
881impl_number_generable!(f32, f64);
882
883impl<T> Generable for Vec<T>
884where
885    T: Generable,
886{
887    fn generation_schema() -> Result<GenerationSchema, FMError> {
888        let item_schema: Value = serde_json::from_str(T::generation_schema()?.json_schema())
889            .map_err(|error| {
890                FMError::InvalidArgument(format!("element schema is not valid JSON: {error}"))
891            })?;
892        Ok(GenerationSchema::from_json_schema_unchecked(
893            serde_json::json!({ "type": "array", "items": item_schema }).to_string(),
894        ))
895    }
896}
897
898impl<T> Generable for Option<T>
899where
900    T: Generable,
901{
902    fn generation_schema() -> Result<GenerationSchema, FMError> {
903        T::generation_schema()
904    }
905}