1use std::fmt;
4use std::sync::Arc;
5use std::sync::LazyLock;
6
7use ferrin_spec::error::TypeValidationError;
8use schemars::JsonSchema;
9use serde::de::DeserializeOwned;
10use serde_json::Value;
11use serde_json::json;
12
13use crate::dialect::SchemaDialect;
14use crate::transform::SchemaTransform;
15
16type LazyValue = LazyLock<Value, Box<dyn FnOnce() -> Value + Send>>;
17type Validate<T> = dyn Fn(Value) -> Result<T, TypeValidationError> + Send + Sync;
18
19pub struct Schema<T> {
25 json_schema: Arc<LazyValue>,
26 validate: Arc<Validate<T>>,
27}
28
29impl<T> Clone for Schema<T> {
30 fn clone(&self) -> Self {
31 Self {
32 json_schema: Arc::clone(&self.json_schema),
33 validate: Arc::clone(&self.validate),
34 }
35 }
36}
37
38impl<T> fmt::Debug for Schema<T> {
39 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40 f.debug_struct("Schema")
41 .field("json_schema", &LazyLock::get(&self.json_schema))
42 .finish_non_exhaustive()
43 }
44}
45
46impl<T: DeserializeOwned + JsonSchema + 'static> Schema<T> {
47 #[must_use]
50 pub fn derived() -> Self {
51 Self::derived_with(SchemaDialect::default())
52 }
53
54 #[must_use]
56 pub fn derived_with(dialect: SchemaDialect) -> Self {
57 Self::lazy(
58 move || {
59 let mut schema = dialect.generate::<T>();
60 crate::transform::add_additional_properties_false(&mut schema);
61 schema
62 },
63 deserialize_into::<T>,
64 )
65 }
66}
67
68impl<T: DeserializeOwned + 'static> Schema<T> {
69 #[must_use]
75 pub fn typed_from_json_schema(schema: Value) -> Self {
76 let dynamic = Schema::<Value>::from_json_schema(schema);
77 let dynamic_validate = Arc::clone(&dynamic.validate);
78 Self {
79 json_schema: dynamic.json_schema,
80 validate: Arc::new(move |value| {
81 let value = dynamic_validate(value)?;
82 deserialize_into::<T>(value)
83 }),
84 }
85 }
86}
87
88impl Schema<Value> {
89 #[must_use]
96 pub fn from_json_schema(schema: Value) -> Self {
97 let json_schema = Arc::new(LazyValue::new(Box::new(move || schema)));
98 let validate = dynamic_validator(Arc::clone(&json_schema));
99 Self {
100 json_schema,
101 validate,
102 }
103 }
104
105 #[must_use]
108 pub fn empty_object() -> Self {
109 Self::from_json_schema(json!({
110 "type": "object",
111 "properties": {},
112 "additionalProperties": false,
113 }))
114 }
115
116 #[must_use]
118 pub fn any() -> Self {
119 Self::from_json_schema(json!({}))
120 }
121}
122
123impl<T: 'static> Schema<T> {
124 pub fn lazy(
126 json_schema: impl FnOnce() -> Value + Send + 'static,
127 validate: impl Fn(Value) -> Result<T, TypeValidationError> + Send + Sync + 'static,
128 ) -> Self {
129 Self {
130 json_schema: Arc::new(LazyValue::new(Box::new(json_schema))),
131 validate: Arc::new(validate),
132 }
133 }
134
135 pub fn with_json_schema_and_validator(
137 json_schema: Value,
138 validate: impl Fn(Value) -> Result<T, TypeValidationError> + Send + Sync + 'static,
139 ) -> Self {
140 Self::lazy(move || json_schema, validate)
141 }
142
143 #[must_use]
145 pub fn with_validator(
146 self,
147 validate: impl Fn(Value) -> Result<T, TypeValidationError> + Send + Sync + 'static,
148 ) -> Self {
149 Self {
150 json_schema: self.json_schema,
151 validate: Arc::new(validate),
152 }
153 }
154
155 pub fn transformed(&self, transform: SchemaTransform) -> Result<Self, crate::SchemaError> {
163 let transformed = transform.applied(self.json_schema().clone())?;
164 Ok(Self {
165 json_schema: Arc::new(LazyValue::new(Box::new(move || transformed))),
166 validate: Arc::clone(&self.validate),
167 })
168 }
169
170 #[must_use]
173 pub fn erased(&self) -> Schema<Value> {
174 let validate = Arc::clone(&self.validate);
175 Schema {
176 json_schema: Arc::clone(&self.json_schema),
177 validate: Arc::new(move |value| validate(value.clone()).map(|_| value)),
178 }
179 }
180}
181
182impl<T> Schema<T> {
183 #[must_use]
185 pub fn json_schema(&self) -> &Value {
186 &self.json_schema
187 }
188
189 pub fn validate(&self, value: Value) -> Result<T, TypeValidationError> {
195 (self.validate)(value)
196 }
197}
198
199fn deserialize_into<T: DeserializeOwned>(value: Value) -> Result<T, TypeValidationError> {
200 match serde_json::from_value::<T>(value.clone()) {
201 Ok(typed) => Ok(typed),
202 Err(error) => Err(TypeValidationError::new(value, error)),
203 }
204}
205
206#[cfg(feature = "json-schema-validation")]
207fn dynamic_validator(json_schema: Arc<LazyValue>) -> Arc<Validate<Value>> {
208 use std::sync::OnceLock;
209
210 use crate::validation::ValidationIssues;
211 use crate::validation::Validator;
212
213 let compiled: OnceLock<Result<Validator, String>> = OnceLock::new();
214 Arc::new(move |value| {
215 let validator = compiled
216 .get_or_init(|| Validator::compile(&json_schema).map_err(|error| error.to_string()));
217 match validator {
218 Ok(validator) => validator
219 .validate(&value)
220 .map(|()| value.clone())
221 .map_err(|issues| TypeValidationError::new(value, issues)),
222 Err(message) => Err(TypeValidationError::new(
223 value,
224 ValidationIssues::message(message.clone()),
225 )),
226 }
227 })
228}
229
230#[cfg(not(feature = "json-schema-validation"))]
231fn dynamic_validator(_json_schema: Arc<LazyValue>) -> Arc<Validate<Value>> {
232 Arc::new(Ok)
233}