google_ai_rs/
schema.rs

1use google_ai_schema_derive::AsSchema;
2use std::{
3    borrow::Cow,
4    cell::{Cell, RefCell, RefMut},
5    collections::{BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque},
6    ffi::{CStr, CString},
7    marker::PhantomData,
8    num::{
9        NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128,
10        NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize,
11    },
12    path::{Path, PathBuf},
13    rc::{Rc, Weak},
14    sync::{
15        atomic::{
16            AtomicBool, AtomicI16, AtomicI32, AtomicI64, AtomicI8, AtomicIsize, AtomicU16,
17            AtomicU32, AtomicU64, AtomicU8, AtomicUsize,
18        },
19        Arc, Mutex, RwLock, Weak as ArcWeak,
20    },
21};
22use tokio::sync::{Mutex as TMutex, RwLock as TRwLock};
23
24use crate::proto::{Schema, Type};
25
26// SchemaType contains the list of OpenAPI data types as defined by
27// https://spec.openapis.org/oas/v3.0.3#data-types
28pub type SchemaType = Type;
29
30/// A list of supported OpenAPI data formats for primitive types.
31///
32/// This enum provides a type-safe way to specify the `format` field of a `Schema`,
33/// which is used to provide more detail about primitive data types.
34#[derive(Clone, Copy, Debug)]
35pub enum SchemaFormat {
36    /// 32-bit floating-point number.
37    Float,
38    /// 64-bit floating-point number.
39    Double,
40    /// 32-bit integer.
41    Int32,
42    /// 64-bit integer.
43    Int64,
44    /// A string that can only be one of a predefined set of values.
45    Enum,
46    None,
47}
48
49impl SchemaFormat {
50    /// Returns the string representation of the format.
51    fn as_str(self) -> &'static str {
52        match self {
53            Self::Float => "float",
54            Self::Double => "double",
55            Self::Int32 => "int32",
56            Self::Int64 => "int64",
57            Self::Enum => "enum",
58            Self::None => "",
59        }
60    }
61}
62
63impl Schema {
64    /// Constructs a new schema for the specified primitive type.
65    pub fn new(typ: SchemaType) -> Self {
66        Schema {
67            r#type: typ as i32,
68            ..Default::default()
69        }
70    }
71
72    /// Creates a new schema for an object type.
73    pub fn new_object() -> Self {
74        Self::new(Type::Object)
75    }
76
77    /// Creates a new schema for an array type.
78    pub fn new_array() -> Self {
79        Self::new(Type::Array)
80    }
81
82    /// Creates a new schema for a number type.
83    pub fn new_number() -> Self {
84        Self::new(Type::Number)
85    }
86
87    /// Creates a new schema for an integer type.
88    pub fn new_integer() -> Self {
89        Self::new(Type::Integer)
90    }
91
92    /// Creates a new schema for a string type.
93    pub fn new_string() -> Self {
94        Self::new(Type::String)
95    }
96
97    /// Sets the format of the schema.
98    ///
99    /// This is used for primitive types like `int32`, `int64`, `float`, `double`, or `enum`.
100    pub fn format(mut self, format: SchemaFormat) -> Self {
101        self.format = format.as_str().to_owned();
102        self
103    }
104
105    /// Sets the description of the schema.
106    ///
107    /// The description can be formatted as Markdown.
108    pub fn description(mut self, description: impl Into<String>) -> Self {
109        self.description = description.into();
110        self
111    }
112
113    /// Sets whether the schema value may be null.
114    pub fn nullable(mut self, nullable: bool) -> Self {
115        self.nullable = nullable;
116        self
117    }
118
119    /// Sets the possible enum values for a `String` type.
120    ///
121    /// This method automatically sets the schema's `type` to `String` and its `format` to `Enum`.
122    /// The values provided will be added to the `enum` field.
123    ///
124    /// # Example
125    /// ```rust
126    /// # use google_ai_rs::Schema;
127    /// let enum_schema = Schema::new_string()
128    ///     .into_enum(["EAST", "NORTH", "SOUTH", "WEST"]);
129    /// ```
130    pub fn into_enum<I, S>(self, r#enum: I) -> Self
131    where
132        I: IntoIterator<Item = S>,
133        S: Into<String>,
134    {
135        // We ensure the schema is a String type before applying enum properties.
136        if self.is_string() {
137            let mut self_with_format = self.format(SchemaFormat::Enum);
138            self_with_format.r#enum = r#enum.into_iter().map(Into::into).collect();
139            self_with_format
140        } else {
141            self
142        }
143    }
144
145    /// Sets the schema for the elements of an `Array` type.
146    ///
147    /// This method is only effective when the schema's type is `Array`. It specifies the
148    /// structure of the items contained within the array.
149    ///
150    /// # Example
151    /// For a `Vec<String>`, you would define the schema like this:
152    /// ```rust
153    /// # use google_ai_rs::Schema;
154    /// let string_array_schema = Schema::new_array()
155    ///     .items(Schema::new_string());
156    /// ```
157    pub fn items(mut self, items: Schema) -> Self {
158        if self.is_array() {
159            self.items = Some(Box::new(items));
160        }
161        self
162    }
163
164    /// Sets the maximum number of elements for an `Array` schema.
165    ///
166    /// This method is only effective when the schema's type is `Array`.
167    pub fn max_items(mut self, max_items: i64) -> Self {
168        if self.is_array() {
169            self.max_items = max_items;
170        }
171        self
172    }
173
174    /// Sets the minimum number of elements for an `Array` schema.
175    ///
176    /// This method is only effective when the schema's type is `Array`.
177    pub fn min_items(mut self, min_items: i64) -> Self {
178        if self.is_array() {
179            self.min_items = min_items;
180        }
181        self
182    }
183
184    /// Adds a single property to an `Object` schema.
185    ///
186    /// This method is a convenience for adding a single key-value pair to the properties map.
187    /// It's only effective when the schema's type is `Object`.
188    ///
189    /// # Arguments
190    /// * `name` - The name of the property.
191    /// * `schema` - The schema definition for the property.
192    pub fn property(mut self, name: impl Into<String>, schema: Schema) -> Self {
193        if self.is_object() {
194            self.properties.insert(name.into(), schema);
195        }
196        self
197    }
198
199    /// Sets the properties for an `Object` schema.
200    ///
201    /// This method is only effective when the schema's type is `Object`.
202    ///
203    /// # Arguments
204    /// * `properties` - An iterator of key-value pairs where the key is the property
205    ///   name and the value is the property's `Schema`.
206    pub fn properties<I, S>(mut self, properties: I) -> Self
207    where
208        I: IntoIterator<Item = (S, Schema)>,
209        S: Into<String>,
210    {
211        if self.is_object() {
212            self.properties = properties.into_iter().map(|(k, v)| (k.into(), v)).collect();
213        }
214        self
215    }
216
217    /// Adds a required field to an `Object` schema.
218    ///
219    /// This method is only effective when the schema's type is `Object`.
220    ///
221    /// # Arguments
222    /// * `name` - The name of the property that is now required.
223    pub fn required_field(mut self, name: impl Into<String>) -> Self {
224        if self.is_object() {
225            self.required.push(name.into());
226        }
227        self
228    }
229
230    /// Sets the list of all required properties for an `Object` schema.
231    ///
232    /// This method is only effective when the schema's type is `Object`.
233    ///
234    /// # Arguments
235    /// * `required` - An iterator of property names that must be present.
236    pub fn required<I, S>(mut self, required: I) -> Self
237    where
238        I: IntoIterator<Item = S>,
239        S: Into<String>,
240    {
241        if self.is_object() {
242            self.required = required.into_iter().map(Into::into).collect();
243        }
244        self
245    }
246
247    fn is_object(&self) -> bool {
248        SchemaType::Object as i32 == self.r#type
249    }
250
251    fn is_array(&self) -> bool {
252        SchemaType::Array as i32 == self.r#type
253    }
254
255    fn is_string(&self) -> bool {
256        SchemaType::Object as i32 == self.r#type
257    }
258}
259
260/// Trait for Rust types that can generate a `Schema` (a subset of OpenAPI schemas) automatically.
261///
262/// Implement this trait or derive `AsSchema` to enable schema generation for your types.
263/// The derive macro supports extensive customization through attributes and integrates with Serde.
264///
265/// # Description Attributes
266/// Descriptions can now span multiple lines. You can use multiple `#[schema(description = "...")]`
267/// attributes on a field or a struct. Each attribute's content will be concatenated.
268///
269/// To add a new line, use an empty `#[schema(description = "")]` attribute, similar to how
270/// the standard `///` documentation comments work.
271///
272/// # Examples
273///
274/// ```rust
275/// use google_ai_rs::AsSchema;
276///
277/// #[derive(AsSchema)]
278/// #[schema(rename_all = "camelCase")]
279/// struct AiReport {
280///     // A single-line description
281///     #[schema(description = "This should include user's name and date of birth", required)]
282///     data: String,
283///
284///     // A multi-line description
285///     #[schema(description = "This field contains important metadata.")]
286///     #[schema(description = "")] // New line
287///     #[schema(description = "For example, the creation timestamp and author.")]
288///     metadata: String,
289/// }
290/// ```
291///
292/// # Customizing Foreign Types
293///
294/// For types from other crates where you can't add the `derive`, you can either manually
295/// specify its schema or reference a function that generates it.
296///
297/// **1. Using `r#type` and `format`:**
298///
299/// ```rust
300/// use google_ai_rs::AsSchema;
301/// # mod some_crate { pub struct TheirType; }
302///
303/// #[derive(AsSchema)]
304/// struct AiReport {
305///     #[schema(r#type = "Number", format = "double")]
306///     foreign: some_crate::TheirType
307/// }
308/// ```
309///
310/// **2. Using `as_schema` with a function:**
311///
312/// This is useful for more complex foreign types.
313///
314/// ```rust
315/// use google_ai_rs::{AsSchema, Schema, schema::SchemaFormat};
316/// # mod some_crate { pub struct TheirType; }
317///
318/// #[derive(AsSchema)]
319/// struct AiReport {
320///     #[schema(as_schema = "foreign_type_schema")]
321///     foreign: some_crate::TheirType
322/// }
323///
324/// fn foreign_type_schema() -> Schema {
325///     Schema::new_object()
326///         .description("A custom schema for a foreign type.")
327///         .property("id", Schema::new_number().format(SchemaFormat::Int64))
328/// }
329/// ```
330///
331/// # Serde Compatibility
332///
333/// The `AsSchema` derive macro automatically integrates with `serde` attributes for convenience.
334///
335/// - `#[serde(rename)]`/`#[serde(rename_all)]` are respected for naming fields in the schema.
336/// - `#[serde(skip)]` fields are automatically excluded from the generated schema.
337/// - You can disable Serde integration for a specific field with `#[schema(ignore_serde)]` or for the whole
338///   type with `#[schema(serde_ignore)]` on the struct.
339///
340/// # Examples with Serde
341///
342/// ```rust
343/// use google_ai_rs::AsSchema;
344///
345/// #[derive(AsSchema, serde::Deserialize)]
346/// struct AiReport {
347///     #[schema(description = "Important field", required)]
348///     #[serde(rename = "json_field")] // Applies to the schema too
349///     field: String,
350///
351///     #[serde(skip)] // Excluded from schema
352///     internal: String,
353///
354///     #[schema(skip)] // Overrides Serde's behavior and excludes from schema
355///     #[serde(rename = "count")] // This rename is ignored by the schema
356///     item_count: i32,
357/// }
358/// ```
359#[cfg_attr(
360    not(no_diagnostic_namespace),
361    diagnostic::on_unimplemented(
362        note = "for local types consider adding `#[derive(google_ai_rs::AsSchema)]` to your `{Self}` type",
363        note = "for types from other crates consider the as_schema attribute or check if you can represent with the r#type and format attributes",
364        note = "consider google_ai_rs::Map for maps, and google_ai_rs::Tuple or derive AsSchemaWithSerde for tuples"
365    )
366)]
367pub trait AsSchema {
368    /// Generates the OpenAPI schema for this type
369    fn as_schema() -> Schema;
370}
371
372impl<T: AsSchema + ?Sized> AsSchema for &T {
373    fn as_schema() -> Schema {
374        T::as_schema()
375    }
376}
377
378impl<T: AsSchema + ?Sized> AsSchema for &mut T {
379    fn as_schema() -> Schema {
380        T::as_schema()
381    }
382}
383
384impl<T: AsSchema + ?Sized> AsSchema for *const T {
385    fn as_schema() -> Schema {
386        T::as_schema()
387    }
388}
389
390impl<T: AsSchema + ?Sized> AsSchema for *mut T {
391    fn as_schema() -> Schema {
392        T::as_schema()
393    }
394}
395
396macro_rules! wrapper_generic {
397    (
398        $($ty:ident <$($life:lifetime, )* T $(: $b0:ident $(+ $b:ident)*)* $(, $g:ident : $gb:ident)*>)*
399    ) => {
400    	$(
401	        impl<$($life,)* T $(, $g)*> AsSchema for $ty<$($life,)* T $(, $g)*>
402	        where
403	        	T: AsSchema $(+ $b0 $(+ $b)*)* + ?Sized,
404                $($g: $gb,)*
405	        {
406	            fn as_schema() -> Schema {
407	                T::as_schema()
408	            }
409	        }
410        )*
411    };
412}
413
414wrapper_generic! {
415    Box<T>
416    Arc<T>
417    Rc<T>
418    Mutex<T>
419    RwLock<T>
420    TMutex<T>
421    TRwLock<T>
422    Weak<T>
423    ArcWeak<T>
424    Cell<T>
425    RefCell<T>
426    PhantomData<T>
427    RefMut<'a, T>
428}
429
430impl<'a, T: AsSchema + ToOwned + ?Sized + 'a> AsSchema for Cow<'a, T> {
431    fn as_schema() -> Schema {
432        T::as_schema()
433    }
434}
435
436macro_rules! number {
437    ($($n:ident, $ty:ident, $format:ident)*) => {
438        $(impl AsSchema for $n {
439            fn as_schema() -> Schema {
440                Schema {
441		            r#type: SchemaType::$ty as i32,
442		            format: SchemaFormat::$format.as_str().into(),
443		            ..Default::default()
444		        }
445            }
446        })*
447    };
448}
449
450number! {
451    usize, Number, None
452    u8, Number, None
453    u16, Number, None
454    u32, Number, None
455    u64, Number, None
456    u128, Number, None
457    AtomicUsize, Number, None
458    AtomicU8, Number, None
459    AtomicU16, Number, None
460    AtomicU32, Number, None
461    AtomicU64, Number, None
462    NonZeroUsize, Number, None
463    NonZeroU8, Number, None
464    NonZeroU16, Number, None
465    NonZeroU32, Number, None
466    NonZeroU64, Number, None
467    NonZeroU128, Number, None
468}
469
470number! {
471    isize, Integer, None
472    i8, Integer, None
473    i16, Integer, None
474    i32, Integer, Int32
475    i64, Integer, Int64
476    i128, Integer, None
477    AtomicIsize, Integer, None
478    AtomicI8, Integer, None
479    AtomicI16, Integer, None
480    AtomicI32, Integer, Int32
481    AtomicI64, Integer, Int64
482    NonZeroIsize, Integer, None
483    NonZeroI8, Integer, None
484    NonZeroI16, Integer, None
485    NonZeroI32, Integer, Int32
486    NonZeroI64, Integer, Int64
487    NonZeroI128, Integer, None
488}
489
490number! {
491    f32, Number, Float
492    f64, Number, Double
493}
494
495macro_rules! string {
496    ($($n:ident)*) => {
497    	$(
498        impl AsSchema for $n {
499            fn as_schema() -> Schema {
500                Schema {
501                    r#type: SchemaType::String as i32,
502                    ..Default::default()
503                }
504            }
505        })*
506    };
507}
508
509string! {
510    str
511    String
512    Path
513    PathBuf
514    char //
515}
516
517impl AsSchema for bool {
518    fn as_schema() -> Schema {
519        Schema {
520            r#type: SchemaType::Boolean as i32,
521            ..Default::default()
522        }
523    }
524}
525
526impl AsSchema for AtomicBool {
527    fn as_schema() -> Schema {
528        bool::as_schema()
529    }
530}
531
532macro_rules! list_generic {
533    (
534        $($ty:ident <T $(: $b0:ident $(+ $b:ident)*)* $(, $g:ident : $gb:ident)*>)*
535    ) => {
536    	$(
537	        impl<T $(, $g)*> AsSchema for $ty<T $(, $g)*>
538	        where
539	        	T: AsSchema $(+ $b0 $(+ $b)*)*,
540	        	$($g: $gb,)*
541	        {
542	            fn as_schema() -> Schema {
543	                Schema {
544			            r#type: SchemaType::Array as i32,
545			            items: Some(Box::new(T::as_schema())),
546                        nullable: true,
547			            ..Default::default()
548			        }
549	            }
550	        }
551        )*
552    };
553}
554
555list_generic! {
556    LinkedList<T>
557    Vec<T>
558    VecDeque<T>
559    HashSet<T>
560    BTreeSet<T>
561    BinaryHeap<T>
562}
563
564impl AsSchema for CStr {
565    fn as_schema() -> Schema {
566        Vec::<u8>::as_schema()
567    }
568}
569
570impl AsSchema for CString {
571    fn as_schema() -> Schema {
572        Vec::<u8>::as_schema()
573    }
574}
575
576impl<T: AsSchema, const N: usize> AsSchema for [T; N] {
577    fn as_schema() -> Schema {
578        Schema {
579            r#type: SchemaType::Array as i32,
580            nullable: true,
581            items: Some(Box::new(T::as_schema())),
582            max_items: N as i64,
583            min_items: N as i64,
584            ..Default::default()
585        }
586    }
587}
588
589impl AsSchema for () {
590    fn as_schema() -> Schema {
591        Schema {
592            r#type: SchemaType::Array as i32,
593            nullable: true,
594            ..Default::default()
595        }
596    }
597}
598
599impl<T: AsSchema> AsSchema for Option<T> {
600    fn as_schema() -> Schema {
601        let mut schema = T::as_schema();
602        schema.nullable = true;
603        schema
604    }
605}
606
607use std::fmt::Debug;
608use std::ops::{Deref, DerefMut};
609
610macro_rules! custom_wrapper_utils {
611    ($($name:ident)*) => {
612        $(impl<T> Debug for $name<T>
613        where
614            T: Debug,
615        {
616            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
617                T::fmt(&self.inner, f)
618            }
619        }
620
621        impl<T> Deref for $name<T> {
622            type Target = T;
623
624            fn deref(&self) -> &Self::Target {
625                &self.inner
626            }
627        }
628
629        impl<T> DerefMut for $name<T> {
630            fn deref_mut(&mut self) -> &mut Self::Target {
631                &mut self.inner
632            }
633        }
634
635        impl<T> From<T> for $name<T> {
636            fn from(value: T) -> Self {
637                Self::new(value)
638            }
639        }
640
641        impl<T> IntoIterator for $name<T>
642        where
643            T: IntoIterator
644        {
645            type Item = T::Item;
646            type IntoIter = T::IntoIter;
647
648            fn into_iter(self) -> Self::IntoIter {
649                self.inner.into_iter()
650            }
651        }
652
653        impl<'a, T> IntoIterator for &'a $name<T>
654        where
655            &'a T: IntoIterator
656        {
657            type Item = <&'a T as IntoIterator>::Item;
658            type IntoIter = <&'a T as IntoIterator>::IntoIter;
659
660            fn into_iter(self) -> Self::IntoIter {
661                self.inner.into_iter()
662            }
663        }
664
665
666        impl<'a, T> IntoIterator for &'a mut $name<T>
667        where
668            &'a mut T: IntoIterator
669        {
670            type Item = <&'a mut T as IntoIterator>::Item;
671            type IntoIter = <&'a mut T as IntoIterator>::IntoIter;
672
673            fn into_iter(self) -> Self::IntoIter {
674                self.inner.into_iter()
675            }
676        }
677
678        impl<T> $name<T> {
679            pub fn new(inner: T) -> Self {
680                Self { inner }
681            }
682
683            pub fn into_inner(self) -> T {
684                self.inner
685            }
686        })*
687    };
688}
689
690custom_wrapper_utils! {
691    Tuple
692    Map
693}
694
695/// A wrapper type to represent maps in Google Schema-friendly format.
696///
697/// Google Schema doesn't natively support maps, so this represents them as arrays
698/// of key-value pairs using an `Entry` struct. Provides schema generation through
699/// `AsSchema` and optional serde deserialization.
700///
701/// # Examples
702///
703/// Basic usage with HashMap:
704/// ```
705/// use std::collections::HashMap;
706/// use google_ai_rs::{Map, Schema, AsSchema};
707///
708/// type MyMap = Map<HashMap<String, i32>>;
709///
710/// # use google_ai_rs::SchemaType;
711/// let schema = Schema {
712///     r#type: SchemaType::Array as i32,
713///     items: Some(
714///         Schema {
715///             r#type: SchemaType::Object as i32,
716///             properties: [
717///                 ("key".to_string(), String::as_schema()),
718///                 ("value".to_string(), i32::as_schema()),
719///             ]
720///             .into(),
721///             required: ["key".to_string(), "value".to_string()].into(),
722///             ..Default::default()
723///         }
724///         .into(),
725///     ),
726///     nullable: true,
727///     ..Default::default()
728/// };
729///
730/// assert_eq!(schema, MyMap::as_schema())
731/// ```
732///
733/// Custom field identifiers and description:
734/// ```
735/// use google_ai_rs::{MapTrait, Map};
736///
737/// struct CustomMap;
738/// impl MapTrait for CustomMap {
739///     type Key = String;
740///     type Value = i32;
741///     const KEY_IDENT: &str = "id";
742///     const VALUE_IDENT: &str = "count";
743///     const DESCRIPTION: Option<&str> = Some("Custom mapped values");
744/// }
745///
746/// type SpecialMap = Map<CustomMap>;
747/// // Schema will have "id" and "count" fields with description
748///
749/// ```
750/// **Deserialization Note:**  
751/// Requires `serde` feature. Works best when `T` uses `MapAccess::next_entry` variants.
752#[derive(Default)]
753pub struct Map<T: ?Sized> {
754    inner: T,
755}
756
757impl<T> AsSchema for Map<T>
758where
759    T: MapTrait,
760    T::Key: AsSchema,
761    T::Value: AsSchema,
762{
763    fn as_schema() -> Schema {
764        let mut schema = Vec::<Entry<T>>::as_schema();
765        if let Some(description) = T::DESCRIPTION {
766            schema.description = description.to_owned()
767        }
768        schema
769    }
770}
771
772/// Trait defining contract for types that can be represented as maps
773///
774/// # Examples
775///
776/// Implementing for a custom collection:
777/// ```
778/// use google_ai_rs::MapTrait;
779///
780/// struct PairList<K, V>(Vec<(K, V)>);
781///
782/// impl<K, V> MapTrait for PairList<K, V> {
783///     type Key = K;
784///     type Value = V;
785///     const KEY_IDENT: &str = "k";
786///     const VALUE_IDENT: &str = "v";
787/// }
788/// ```
789pub trait MapTrait {
790    type Key;
791    type Value;
792    const KEY_IDENT: &str = "key";
793    const VALUE_IDENT: &str = "value";
794    const DESCRIPTION: Option<&str> = None;
795}
796
797impl<K, V> MapTrait for HashMap<K, V> {
798    type Key = K;
799
800    type Value = V;
801}
802
803/// Internal representation of a map entry for schema generation
804///
805/// Automatically renames fields based on the MapTrait implementation.
806///
807/// # Example
808///
809/// With custom field identifiers:
810/// ```
811/// # use google_ai_rs::MapTrait;
812/// struct Custom;
813/// impl MapTrait for Custom {
814///     type Key = String;
815///     type Value = i32;
816///     const KEY_IDENT: &str = "name";
817///     const VALUE_IDENT: &str = "score";
818/// }
819///
820/// // Entry<Custom> would generate schema fields "name" and "score"
821/// ```
822#[allow(dead_code)]
823#[derive(AsSchema)]
824#[schema(crate_path = "crate", rename_all_with = "Self::rename_idents")]
825struct Entry<T>
826where
827    T: MapTrait,
828{
829    pub(super) key: T::Key,
830    pub(super) value: T::Value,
831}
832
833impl<T> Entry<T>
834where
835    T: MapTrait,
836{
837    fn rename_idents(f: &str) -> String {
838        match f {
839            "key" => T::KEY_IDENT.to_owned(),
840            "value" => T::VALUE_IDENT.to_owned(),
841            _ => panic!("{f}"),
842        }
843    }
844}
845
846/// Wrapper type for tuple representation in Google Schema
847///
848/// Represents tuples as objects with positional field names ("0", "1", etc).
849/// Supports tuples up to 16 elements.
850///
851/// # Example
852///
853/// ```
854/// use google_ai_rs::{Tuple, Schema, AsSchema};
855///
856/// type StringIntPair = Tuple<(String, i32)>;
857///
858/// # use google_ai_rs::SchemaType;
859/// let schema = Schema {
860///     r#type: SchemaType::Object as i32,
861///     properties: [
862///         ("0".to_string(), String::as_schema()),
863///         ("1".to_string(), i32::as_schema()),
864///     ]
865///     .into(),
866///     required: ["0".to_string(), "1".to_string()].into(),
867///     ..Default::default()
868/// };
869///
870/// assert_eq!(schema, StringIntPair::as_schema())
871/// ```
872///
873/// For tuple structs, prefer `AsSchemaWithSerde` derive:
874/// ```
875/// # use google_ai_schema_derive::AsSchemaWithSerde;
876/// # use google_ai_rs::AsSchema;
877/// #[derive(AsSchemaWithSerde)]
878/// struct Point(f32, f32);
879///
880/// ```
881/// **Deserialization Note:**  
882/// Requires `serde` feature
883#[derive(Default)]
884pub struct Tuple<T: ?Sized> {
885    inner: T,
886}
887
888// FIXME: Reduce the indirections here
889macro_rules! tuple {
890    (
891        $(($($T:ident)*))*
892    ) => {
893        $(impl<$($T, )*> AsSchema for Tuple<($($T, )*)>
894        where
895            $($T: AsSchema),*
896        {
897            fn as_schema() -> Schema {
898                #[derive(google_ai_schema_derive::AsSchemaWithSerde)]
899                #[schema(crate_path = "crate")]
900                struct InnerTupleHelper<$($T, )*>($($T, )*);
901
902                #[cfg(feature = "serde")]
903                #[allow(non_local_definitions)]
904                impl<'de, $($T, )*> serde::Deserialize<'de> for Tuple<($($T, )*)>
905                where
906                    $($T: serde::Deserialize<'de> + Sized),*
907                {
908                    #[allow(non_snake_case)]
909                    #[inline]
910                    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
911                    where
912                        D: serde::Deserializer<'de>,
913                    {
914                        // transmuting would have been better
915                        let inner = InnerTupleHelper::<$($T, )*>::deserialize(deserializer)?;
916                        let InnerTupleHelper($($T, )*) = inner;
917                        let inner = ($($T, )*);
918                        Ok(Self{inner})
919                    }
920                }
921
922                InnerTupleHelper::<$($T, )*>::as_schema()
923            }
924        })*
925    };
926}
927
928tuple! {
929    ()
930    (T0)
931    (T0 T1)
932    (T0 T1 T2)
933    (T0 T1 T2 T3)
934    (T0 T1 T2 T3 T4)
935    (T0 T1 T2 T3 T4 T5)
936    (T0 T1 T2 T3 T4 T5 T6)
937    (T0 T1 T2 T3 T4 T5 T6 T7)
938    (T0 T1 T2 T3 T4 T5 T6 T7 T8)
939    (T0 T1 T2 T3 T4 T5 T6 T7 T8 T9)
940    (T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10)
941    (T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11)
942    (T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12)
943    (T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13)
944    (T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14)
945    (T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15)
946}
947
948#[cfg(feature = "serde")]
949mod serde_support {
950    use std::marker::PhantomData;
951
952    use common::{EPhantomData, MapAccessSeqAccess};
953    use serde::{de::Visitor, forward_to_deserialize_any, Deserialize, Deserializer};
954
955    use super::{Entry, Map, MapTrait};
956
957    impl<'de, T> Deserialize<'de> for Entry<T>
958    where
959        T: MapTrait,
960        T::Key: Deserialize<'de>,
961        T::Value: Deserialize<'de>,
962    {
963        #[inline]
964        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
965        where
966            D: Deserializer<'de>,
967        {
968            deserializer.deserialize_struct(
969                "Entry",
970                &[T::KEY_IDENT, T::VALUE_IDENT],
971                EPhantomData(PhantomData::<Self>),
972            )
973        }
974    }
975
976    impl<'de, T> Deserialize<'de> for Map<T>
977    where
978        T: MapTrait + Deserialize<'de>,
979    {
980        #[inline]
981        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
982        where
983            D: Deserializer<'de>,
984        {
985            struct MapToSeq<D, T> {
986                inner: D,
987                _marker: PhantomData<T>,
988            }
989
990            impl<'de, D, T> Deserializer<'de> for MapToSeq<D, T>
991            where
992                D: Deserializer<'de>,
993                T: MapTrait,
994            {
995                type Error = D::Error;
996
997                fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
998                where
999                    V: serde::de::Visitor<'de>,
1000                {
1001                    struct SeqMapV<V, T> {
1002                        inner: V,
1003                        _map: PhantomData<T>,
1004                    }
1005
1006                    impl<'de, V, T> Visitor<'de> for SeqMapV<V, T>
1007                    where
1008                        V: Visitor<'de>,
1009                        T: MapTrait,
1010                    {
1011                        type Value = V::Value;
1012
1013                        fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1014                            write!(
1015                                f,
1016                                "an array of object with two fields representing the key and value of a map"
1017                            )
1018                        }
1019
1020                        fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
1021                        where
1022                            A: serde::de::SeqAccess<'de>,
1023                        {
1024                            self.inner.visit_map(MapAccessSeqAccess {
1025                                _entry: PhantomData::<Entry<T>>,
1026                                seq,
1027                            })
1028                        }
1029                    }
1030
1031                    self.inner.deserialize_seq(SeqMapV {
1032                        inner: visitor,
1033                        _map: self._marker,
1034                    })
1035                }
1036
1037                forward_to_deserialize_any! {
1038                    bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
1039                    bytes byte_buf option unit unit_struct newtype_struct seq tuple
1040                    tuple_struct map struct enum identifier ignored_any
1041                }
1042
1043                fn is_human_readable(&self) -> bool {
1044                    self.inner.is_human_readable()
1045                }
1046            }
1047
1048            T::deserialize(MapToSeq {
1049                inner: deserializer,
1050                _marker: PhantomData::<T>,
1051            })
1052            .map(Into::into)
1053        }
1054    }
1055
1056    #[cfg(test)]
1057    mod test {
1058        use std::collections::HashMap;
1059
1060        use crate::AsSchema;
1061
1062        use super::*;
1063
1064        #[test]
1065        fn map() {
1066            #[derive(Deserialize, AsSchema, Hash, Eq, PartialEq, Debug)]
1067            #[schema(crate_path = "crate")]
1068            struct Question {
1069                intensity: i64,
1070                raw: String,
1071            }
1072
1073            #[derive(Deserialize, AsSchema, Debug, PartialEq, Eq)]
1074            #[schema(crate_path = "crate")]
1075            struct Answer {
1076                uniqueness: i64,
1077                raw: String,
1078            }
1079
1080            #[derive(PartialEq, Eq, Deserialize, Debug)]
1081            #[serde(transparent)]
1082            struct Snippet(HashMap<Question, Answer>);
1083
1084            impl MapTrait for Snippet {
1085                type Key = Question;
1086                type Value = Answer;
1087
1088                const KEY_IDENT: &str = "question";
1089                const VALUE_IDENT: &str = "answer";
1090            }
1091
1092            assert_eq!(
1093                Map::<Snippet>::as_schema(),
1094                Vec::<Entry<Snippet>>::as_schema()
1095            );
1096
1097            let response = r#"[{"question": {
1098                "intensity": 50,
1099                "raw": "What is the blah blah blah?"
1100            }, "answer": {
1101                "uniqueness": 3,
1102                "raw": "Hmmmm hmm."
1103            }}]"#;
1104
1105            let m: Map<Snippet> = serde_json::from_str(response).unwrap();
1106
1107            assert_eq!(
1108                m.into_inner(),
1109                Snippet(
1110                    [(
1111                        Question {
1112                            intensity: 50,
1113                            raw: "What is the blah blah blah?".into(),
1114                        },
1115                        Answer {
1116                            uniqueness: 3,
1117                            raw: "Hmmmm hmm.".into(),
1118                        }
1119                    )]
1120                    .into()
1121                )
1122            )
1123        }
1124    }
1125
1126    mod common {
1127        use std::marker::PhantomData;
1128
1129        use serde::{
1130            de::{DeserializeSeed, MapAccess, SeqAccess, Visitor},
1131            Deserialize, Deserializer,
1132        };
1133
1134        use crate::schema::{Entry, MapTrait};
1135
1136        pub(super) struct MapAccessSeqAccess<E, S> {
1137            pub(super) _entry: PhantomData<E>,
1138            pub(super) seq: S,
1139        }
1140
1141        impl<'de, E, S> MapAccess<'de> for MapAccessSeqAccess<E, S>
1142        where
1143            E: UnorderedEntry,
1144            S: SeqAccess<'de>,
1145        {
1146            type Error = S::Error;
1147
1148            fn next_entry_seed<K, V>(
1149                &mut self,
1150                kseed: K,
1151                vseed: V,
1152            ) -> Result<Option<(K::Value, V::Value)>, Self::Error>
1153            where
1154                K: DeserializeSeed<'de>,
1155                V: DeserializeSeed<'de>,
1156            {
1157                self.seq.next_element_seed(UnorderedEntrySeed {
1158                    key_seed: kseed,
1159                    value_seed: vseed,
1160                    _entry: self._entry,
1161                })
1162            }
1163
1164            // The methods below are based on luck. If the key comes before the value, we won't
1165            // be able to do anything since we don't have the value seed. Besides, we're unable
1166            // hold on to the mapaccess. So, we just error.
1167            fn next_key_seed<K>(&mut self, _seed: K) -> Result<Option<K::Value>, Self::Error>
1168            where
1169                K: DeserializeSeed<'de>,
1170            {
1171                Err(<Self::Error as serde::de::Error>::custom(
1172                    "Cannot call next_key_seed on MapAccessSeqAccess. \
1173                     Use next_entry_seed to process key-value pairs atomically",
1174                ))
1175            }
1176
1177            fn next_value_seed<V>(&mut self, _seed: V) -> Result<V::Value, Self::Error>
1178            where
1179                V: DeserializeSeed<'de>,
1180            {
1181                Err(<Self::Error as serde::de::Error>::custom(
1182                    "Cannot call next_value_seed on MapAccessSeqAccess. \
1183                     Use next_entry_seed to process key-value pairs atomically",
1184                ))
1185            }
1186
1187            fn size_hint(&self) -> Option<usize> {
1188                self.seq.size_hint()
1189            }
1190        }
1191
1192        // An entry is seen as a map
1193        pub(super) struct UnorderedEntrySeed<K, V, E> {
1194            pub(super) key_seed: K,
1195            pub(super) value_seed: V,
1196            pub(super) _entry: PhantomData<E>,
1197        }
1198
1199        impl<'de, K, V, E> DeserializeSeed<'de> for UnorderedEntrySeed<K, V, E>
1200        where
1201            K: DeserializeSeed<'de>,
1202            V: DeserializeSeed<'de>,
1203            E: UnorderedEntry,
1204        {
1205            type Value = (K::Value, V::Value);
1206
1207            fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
1208            where
1209                D: Deserializer<'de>,
1210            {
1211                deserializer.deserialize_struct(E::NAME, &[E::KEY_IDENT, E::VALUE_IDENT], self)
1212            }
1213        }
1214
1215        impl<'de, K, V, E> Visitor<'de> for UnorderedEntrySeed<K, V, E>
1216        where
1217            K: DeserializeSeed<'de>,
1218            V: DeserializeSeed<'de>,
1219            E: UnorderedEntry,
1220        {
1221            type Value = <Self as DeserializeSeed<'de>>::Value;
1222
1223            fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1224                write!(f, "a struct representing a map entry")
1225            }
1226
1227            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
1228            where
1229                A: MapAccess<'de>,
1230            {
1231                let (mut key, mut value) = (None, None);
1232                let (mut key_seed, mut value_seed) = (Some(self.key_seed), Some(self.value_seed));
1233                let (key_ident, value_ident) = (E::KEY_IDENT, E::VALUE_IDENT);
1234
1235                macro_rules! try_fix_once {
1236                    ($seed:tt, $target:tt, $field:expr) => {{
1237                        if $target.is_none() {
1238                            $target = Some(map.next_value_seed($seed.take().unwrap())?);
1239                        } else {
1240                            return Err(serde::de::Error::duplicate_field($field));
1241                        }
1242                    }};
1243                }
1244
1245                while let Some(field_ident) = map.next_key()? {
1246                    match field_ident {
1247                        key_field if key_field == key_ident => {
1248                            try_fix_once!(key_seed, key, E::KEY_IDENT);
1249                        }
1250                        value_field if value_field == value_ident => {
1251                            try_fix_once!(value_seed, value, E::VALUE_IDENT);
1252                        }
1253                        _ => {
1254                            return Err(<A::Error as serde::de::Error>::unknown_field(
1255                                field_ident,
1256                                &[E::KEY_IDENT, E::VALUE_IDENT],
1257                            ))
1258                        }
1259                    }
1260                }
1261
1262                match (key, value) {
1263                    (Some(k), Some(v)) => Ok((k, v)),
1264                    (None, _) => Err(<A::Error as serde::de::Error>::missing_field(E::KEY_IDENT)),
1265                    (_, None) => Err(<A::Error as serde::de::Error>::missing_field(
1266                        E::VALUE_IDENT,
1267                    )),
1268                }
1269            }
1270        }
1271
1272        pub(super) trait UnorderedEntry {
1273            const NAME: &str;
1274            const KEY_IDENT: &str;
1275            const VALUE_IDENT: &str;
1276        }
1277
1278        impl<T: MapTrait> UnorderedEntry for Entry<T> {
1279            const NAME: &str = "Entry";
1280            const KEY_IDENT: &str = T::KEY_IDENT;
1281            const VALUE_IDENT: &str = T::VALUE_IDENT;
1282        }
1283
1284        pub(super) struct EPhantomData<T>(pub PhantomData<T>);
1285
1286        impl<'de, T> Visitor<'de> for EPhantomData<Entry<T>>
1287        where
1288            T: MapTrait,
1289            T::Key: Deserialize<'de>,
1290            T::Value: Deserialize<'de>,
1291        {
1292            type Value = Entry<T>;
1293
1294            fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1295                write!(f, "a struct representing a map entry")
1296            }
1297
1298            fn visit_map<A>(self, map: A) -> Result<Self::Value, A::Error>
1299            where
1300                A: MapAccess<'de>,
1301            {
1302                let (key, value) = UnorderedEntrySeed {
1303                    key_seed: PhantomData,
1304                    value_seed: PhantomData,
1305                    _entry: PhantomData::<Entry<T>>,
1306                }
1307                .visit_map(map)?;
1308
1309                Ok(Entry { key, value })
1310            }
1311        }
1312    }
1313}
1314
1315#[cfg(test)]
1316#[allow(dead_code)]
1317mod derive_test {
1318    use std::marker::PhantomData;
1319
1320    use super::AsSchema;
1321
1322    use crate::{Schema, SchemaType};
1323
1324    #[test]
1325    fn rename_all_with() {
1326        #[derive(AsSchema)]
1327        #[schema(crate_path = "crate")]
1328        #[schema(rename_all_with = "sAwCaSe")]
1329        struct S {
1330            field: (),
1331            field1: (),
1332        }
1333
1334        #[allow(non_snake_case)]
1335        fn sAwCaSe(former_name: &str) -> String {
1336            former_name
1337                .char_indices()
1338                .map(|(i, c)| {
1339                    if i % 2 == 0 {
1340                        c.to_ascii_lowercase()
1341                    } else {
1342                        c.to_ascii_uppercase()
1343                    }
1344                })
1345                .collect()
1346        }
1347
1348        let expect = Schema {
1349            r#type: SchemaType::Object as i32,
1350            properties: [
1351                ("fIeLd".into(), <()>::as_schema()),
1352                ("fIeLd1".into(), <()>::as_schema()),
1353            ]
1354            .into(),
1355            required: vec!["fIeLd".into(), "fIeLd1".into()],
1356            ..Default::default()
1357        };
1358
1359        assert_eq!(S::as_schema(), expect)
1360    }
1361
1362    #[test]
1363    fn as_schema() {
1364        struct Wrapper<T>(T);
1365
1366        fn wrapper_as_schema<T: AsSchema>() -> Schema {
1367            T::as_schema()
1368        }
1369
1370        #[derive(AsSchema)]
1371        #[schema(crate_path = "crate")]
1372        struct S {
1373            #[schema(as_schema = "wrapper_as_schema::<String>")]
1374            field: Wrapper<String>,
1375        }
1376
1377        assert_eq!(
1378            S::as_schema(),
1379            Schema {
1380                r#type: SchemaType::Object.into(),
1381                properties: [("field".into(), String::as_schema())].into(),
1382                required: vec![("field".into())],
1383                ..Default::default()
1384            }
1385        )
1386    }
1387
1388    #[test]
1389    fn as_schema_generic() {
1390        struct Wrapper<T>(T);
1391
1392        fn wrapper_as_schema<T: AsSchema>() -> (Schema, PhantomData<Wrapper<T>>) {
1393            (T::as_schema(), PhantomData)
1394        }
1395
1396        #[derive(AsSchema)]
1397        #[schema(crate_path = "crate")]
1398        struct S {
1399            #[schema(as_schema_generic = "wrapper_as_schema")]
1400            field: Wrapper<String>,
1401        }
1402
1403        assert_eq!(
1404            S::as_schema(),
1405            Schema {
1406                r#type: SchemaType::Object.into(),
1407                properties: [("field".into(), String::as_schema())].into(),
1408                required: vec![("field".into())],
1409                ..Default::default()
1410            }
1411        )
1412    }
1413}