1use 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
16pub trait FromGeneratedContent: Sized {
18 fn from_generated_content(content: &GeneratedContent) -> Result<Self, FMError>;
20}
21
22pub trait ToGeneratedContent {
24 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#[derive(Debug, Clone, PartialEq, Eq, Hash)]
76pub struct GenerationId {
77 token: String,
78 description: String,
79}
80
81impl GenerationId {
82 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 #[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#[derive(Debug, Clone, PartialEq, Eq, Hash)]
125pub struct Decimal {
126 value: String,
127}
128
129impl Decimal {
130 #[must_use]
132 pub fn new(value: impl Into<String>) -> Self {
133 Self {
134 value: value.into(),
135 }
136 }
137
138 #[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#[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#[derive(Debug, Clone, PartialEq)]
224pub struct GeneratedContent {
225 value: Value,
226 generation_id: Option<GenerationId>,
227 is_complete: bool,
228}
229
230impl GeneratedContent {
231 pub fn from_json_str(json: &str) -> Result<Self, FMError> {
237 Self::from_json_str_with_id(json, None)
238 }
239
240 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 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 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 pub fn from_kind(kind: GeneratedContentKind) -> Result<Self, FMError> {
301 Self::from_kind_with_id(kind, None)
302 }
303
304 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 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 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 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 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 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 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 #[must_use]
466 pub const fn raw_value(&self) -> &Value {
467 &self.value
468 }
469
470 #[must_use]
472 pub fn into_raw_value(self) -> Value {
473 self.value
474 }
475
476 #[must_use]
478 pub fn kind(&self) -> GeneratedContentKind {
479 kind_from_value(&self.value)
480 }
481
482 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 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 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 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 #[must_use]
544 pub const fn is_complete(&self) -> bool {
545 self.is_complete
546 }
547
548 #[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 #[must_use]
558 pub fn generation_id_handle(&self) -> Option<&GenerationId> {
559 self.generation_id.as_ref()
560 }
561
562 #[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}