Skip to main content

kynos_openapi/model/schema/
mod.rs

1//! The Schema Object: JSON Schema 2020-12 plus the OAS base vocabulary.
2
3pub mod dialect;
4pub mod discriminator;
5pub mod object;
6pub mod types;
7pub mod xml;
8
9use serde::{Deserialize, Serialize};
10
11use crate::model::schema::{
12    object::SchemaObject,
13    types::{SchemaType, TypeSet},
14};
15
16/// A JSON Schema.
17///
18/// A boolean is a valid schema in JSON Schema 2020-12: `true` accepts every
19/// instance and `false` accepts none. That is why this is an enum rather than a
20/// struct.
21///
22/// [`Schema::Bool(true)`](Schema::Bool) is how a genuinely unconstrained
23/// payload is represented. Kynos never produces it by accident — a Rust type
24/// that cannot describe itself has no `Schema` implementation at all, and the
25/// permissive schema is reachable only by naming it in the handler signature.
26#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
27#[serde(untagged)]
28pub enum Schema {
29    /// The trivially true (`true`) or trivially false (`false`) schema.
30    Bool(bool),
31    /// A schema with keywords.
32    Object(Box<SchemaObject>),
33}
34
35impl Default for Schema {
36    fn default() -> Self {
37        Self::Object(Box::default())
38    }
39}
40
41impl Schema {
42    /// The schema that accepts any instance.
43    #[must_use]
44    pub fn any() -> Self {
45        Self::Bool(true)
46    }
47
48    /// The schema that accepts no instance.
49    #[must_use]
50    pub fn never() -> Self {
51        Self::Bool(false)
52    }
53
54    /// A schema constrained to a single primitive type.
55    #[must_use]
56    pub fn of_type(ty: SchemaType) -> Self {
57        Self::Object(Box::new(SchemaObject {
58            ty: Some(TypeSet::One(ty)),
59            ..SchemaObject::default()
60        }))
61    }
62
63    /// A schema that is `ty` or `null`.
64    ///
65    /// This is how nullability is expressed from OpenAPI 3.1 onward. The 3.0
66    /// `nullable: true` keyword does not exist and must never be emitted.
67    #[must_use]
68    pub fn nullable(ty: SchemaType) -> Self {
69        Self::Object(Box::new(SchemaObject {
70            ty: Some(TypeSet::Many(vec![ty, SchemaType::Null])),
71            ..SchemaObject::default()
72        }))
73    }
74
75    /// A `$ref` to another schema.
76    ///
77    /// Unlike a [`Ref`](crate::Ref), sibling keywords on a schema `$ref` are
78    /// applied rather than ignored.
79    #[must_use]
80    pub fn reference(uri: impl Into<String>) -> Self {
81        Self::Object(Box::new(SchemaObject {
82            reference: Some(uri.into()),
83            ..SchemaObject::default()
84        }))
85    }
86
87    /// A `$ref` to a named entry under `#/components/schemas`.
88    #[must_use]
89    pub fn component(name: &str) -> Self {
90        Self::reference(format!("#/components/schemas/{name}"))
91    }
92
93    /// Returns the keyword-carrying form, if this is not a boolean schema.
94    #[must_use]
95    pub fn as_object(&self) -> Option<&SchemaObject> {
96        match self {
97            Self::Object(object) => Some(object),
98            Self::Bool(_) => None,
99        }
100    }
101}
102
103#[cfg(test)]
104mod tests;