1use super::models::{
4 DataType, DataTypeFormat, DefaultOperationRaw, DefaultSchemaRaw, Either, Resolvable,
5 SecurityScheme,
6};
7
8use std::collections::{BTreeMap, BTreeSet};
9
10pub trait Schema: Sized {
17 fn description(&self) -> Option<&str>;
19
20 fn reference(&self) -> Option<&str>;
22
23 fn data_type(&self) -> Option<DataType>;
25
26 fn format(&self) -> Option<&DataTypeFormat>;
28
29 fn items(&self) -> Option<&Resolvable<Self>>;
31
32 fn items_mut(&mut self) -> Option<&mut Resolvable<Self>>;
34
35 fn additional_properties(&self) -> Option<&Either<bool, Resolvable<Self>>>;
37
38 fn additional_properties_mut(&mut self) -> Option<&mut Either<bool, Resolvable<Self>>>;
40
41 fn properties(&self) -> Option<&BTreeMap<String, Resolvable<Self>>>;
43
44 fn properties_mut(&mut self) -> Option<&mut BTreeMap<String, Resolvable<Self>>>;
46
47 fn required_properties(&self) -> Option<&BTreeSet<String>>;
49
50 fn enum_variants(&self) -> Option<&[serde_json::Value]>;
56
57 fn contains_any(&self) -> bool {
59 _schema_contains_any(self, vec![])
60 }
61
62 fn set_reference(&mut self, ref_: String);
66
67 fn set_cyclic(&mut self, cyclic: bool);
69
70 fn is_cyclic(&self) -> bool;
75
76 fn name(&self) -> Option<&str>;
81
82 fn set_name(&mut self, name: &str);
84}
85
86fn _schema_contains_any<'a, S: Schema>(schema: &'a S, mut nodes: Vec<&'a str>) -> bool {
87 if schema.data_type().is_none() {
88 return true;
89 }
90
91 if let Some(name) = schema.name() {
92 if nodes.iter().any(|&n| n == name) {
93 return false; } else {
95 nodes.push(name);
96 }
97 }
98
99 schema
100 .properties()
101 .map(|t| {
102 t.values()
103 .any(|s| _schema_contains_any(&*s.read().unwrap(), nodes.clone()))
104 })
105 .unwrap_or(false)
106 || schema
107 .items()
108 .map(|s| _schema_contains_any(&*s.read().unwrap(), nodes.clone()))
109 .unwrap_or(false)
110 || schema
111 .additional_properties()
112 .map(|e| match e {
113 Either::Left(extra_props_allowed) => *extra_props_allowed,
114 Either::Right(s) => _schema_contains_any(&*s.read().unwrap(), nodes),
115 })
116 .unwrap_or(false)
117}
118
119pub trait TypedData {
121 fn data_type() -> DataType {
123 DataType::Object
124 }
125
126 fn format() -> Option<DataTypeFormat> {
128 None
129 }
130
131 fn max() -> Option<f32> {
132 None
133 }
134
135 fn min() -> Option<f32> {
136 None
137 }
138}
139
140macro_rules! impl_type_simple {
141 ($ty:ty) => {
142 impl TypedData for $ty {}
143 };
144 ($ty:ty, $dt:expr) => {
145 impl TypedData for $ty {
146 fn data_type() -> DataType {
147 $dt
148 }
149 }
150 };
151 ($ty:ty, $dt:expr, $df:expr) => {
152 impl TypedData for $ty {
153 fn data_type() -> DataType {
154 $dt
155 }
156 fn format() -> Option<DataTypeFormat> {
157 Some($df)
158 }
159 }
160 };
161 ($ty:ty, $dt:expr, $df:expr, $min:expr, $max:expr) => {
162 impl TypedData for $ty {
163 fn data_type() -> DataType {
164 $dt
165 }
166 fn format() -> Option<DataTypeFormat> {
167 Some($df)
168 }
169 fn max() -> Option<f32> {
170 Some($max)
171 }
172 fn min() -> Option<f32> {
173 Some($min)
174 }
175 }
176 };
177}
178
179impl TypedData for &str {
180 fn data_type() -> DataType {
181 DataType::String
182 }
183}
184
185impl<T: TypedData> TypedData for &T {
186 fn data_type() -> DataType {
187 T::data_type()
188 }
189
190 fn format() -> Option<DataTypeFormat> {
191 T::format()
192 }
193}
194
195impl_type_simple!(char, DataType::String);
196impl_type_simple!(String, DataType::String);
197impl_type_simple!(PathBuf, DataType::String);
198#[cfg(feature = "camino")]
199impl_type_simple!(
200 camino::Utf8PathBuf,
201 DataType::String,
202 DataTypeFormat::Binary
203);
204impl_type_simple!(bool, DataType::Boolean);
205impl_type_simple!(f32, DataType::Number, DataTypeFormat::Float);
206impl_type_simple!(f64, DataType::Number, DataTypeFormat::Double);
207impl_type_simple!(
208 i8,
209 DataType::Integer,
210 DataTypeFormat::Int32,
211 i8::MIN as f32,
212 i8::MAX as f32
213);
214impl_type_simple!(
215 i16,
216 DataType::Integer,
217 DataTypeFormat::Int32,
218 i16::MIN as f32,
219 i16::MAX as f32
220);
221impl_type_simple!(i32, DataType::Integer, DataTypeFormat::Int32);
222impl_type_simple!(
223 u8,
224 DataType::Integer,
225 DataTypeFormat::Int32,
226 u8::MIN as f32,
227 u8::MAX as f32
228);
229impl_type_simple!(
230 u16,
231 DataType::Integer,
232 DataTypeFormat::Int32,
233 u16::MIN as f32,
234 u16::MAX as f32
235);
236impl_type_simple!(u32, DataType::Integer, DataTypeFormat::Int32);
237impl_type_simple!(i64, DataType::Integer, DataTypeFormat::Int64);
238impl_type_simple!(
239 i128,
240 DataType::Integer,
241 DataTypeFormat::Int64,
242 i128::MIN as f32,
243 i128::MAX as f32
244);
245impl_type_simple!(isize, DataType::Integer, DataTypeFormat::Int64);
246impl_type_simple!(u64, DataType::Integer, DataTypeFormat::Int64);
247impl_type_simple!(
248 u128,
249 DataType::Integer,
250 DataTypeFormat::Int64,
251 u128::MIN as f32,
252 u128::MAX as f32
253);
254impl_type_simple!(usize, DataType::Integer, DataTypeFormat::Int64);
255
256#[cfg(feature = "actix-multipart")]
257impl_type_simple!(
258 actix_multipart::Multipart,
259 DataType::File,
260 DataTypeFormat::Binary
261);
262#[cfg(feature = "actix-session")]
263impl_type_simple!(actix_session::Session);
264#[cfg(feature = "actix-identity")]
265impl_type_simple!(actix_identity::Identity);
266#[cfg(feature = "actix-files")]
267impl_type_simple!(
268 actix_files::NamedFile,
269 DataType::File,
270 DataTypeFormat::Binary
271);
272#[cfg(feature = "jiff01")]
273impl_type_simple!(jiff::Timestamp, DataType::String, DataTypeFormat::DateTime);
274#[cfg(feature = "jiff01")]
275impl_type_simple!(
276 jiff::Zoned,
277 DataType::String,
278 DataTypeFormat::Other );
280#[cfg(feature = "jiff01")]
281impl_type_simple!(
282 jiff::civil::DateTime,
283 DataType::String,
284 DataTypeFormat::Other );
286#[cfg(feature = "jiff01")]
287impl_type_simple!(jiff::civil::Date, DataType::String, DataTypeFormat::Date);
288#[cfg(feature = "jiff01")]
289impl_type_simple!(
290 jiff::civil::Time,
291 DataType::String,
292 DataTypeFormat::Other );
294#[cfg(feature = "jiff01")]
295impl_type_simple!(
296 jiff::Span,
297 DataType::String,
298 DataTypeFormat::Other );
300#[cfg(feature = "chrono")]
301impl_type_simple!(
302 chrono::NaiveDateTime,
303 DataType::String,
304 DataTypeFormat::DateTime
305);
306#[cfg(feature = "chrono")]
307impl_type_simple!(chrono::NaiveDate, DataType::String, DataTypeFormat::Date);
308#[cfg(feature = "chrono")]
309impl_type_simple!(chrono::NaiveTime, DataType::String);
310#[cfg(feature = "rust_decimal")]
311impl_type_simple!(
312 rust_decimal::Decimal,
313 DataType::Number,
314 DataTypeFormat::Float
315);
316
317#[cfg(feature = "url")]
318impl_type_simple!(url::Url, DataType::String, DataTypeFormat::Url);
319
320#[cfg(feature = "uuid0")]
321impl_type_simple!(uuid0_dep::Uuid, DataType::String, DataTypeFormat::Uuid);
322#[cfg(feature = "uuid1")]
323impl_type_simple!(uuid1_dep::Uuid, DataType::String, DataTypeFormat::Uuid);
324
325#[cfg(feature = "chrono")]
326impl<T: chrono::offset::TimeZone> TypedData for chrono::DateTime<T> {
327 fn data_type() -> DataType {
328 DataType::String
329 }
330 fn format() -> Option<DataTypeFormat> {
331 Some(DataTypeFormat::DateTime)
332 }
333}
334
335#[cfg(feature = "chrono")]
336#[allow(deprecated)]
337impl<T: chrono::offset::TimeZone> TypedData for chrono::Date<T> {
338 fn data_type() -> DataType {
339 DataType::String
340 }
341 fn format() -> Option<DataTypeFormat> {
342 Some(DataTypeFormat::Date)
343 }
344}
345
346impl_type_simple!(std::net::IpAddr, DataType::String, DataTypeFormat::Ip);
347impl_type_simple!(std::net::Ipv4Addr, DataType::String, DataTypeFormat::IpV4);
348impl_type_simple!(std::net::Ipv6Addr, DataType::String, DataTypeFormat::IpV6);
349
350pub trait Apiv2Schema {
362 fn name() -> Option<String> {
364 None
365 }
366
367 fn description() -> &'static str {
369 ""
370 }
371
372 fn required() -> bool {
374 true
375 }
376
377 fn raw_schema() -> DefaultSchemaRaw {
379 Default::default()
380 }
381
382 fn schema_with_ref() -> DefaultSchemaRaw {
396 let mut def = Self::raw_schema();
397 if let Some(n) = Self::name() {
398 def.reference =
399 Some(String::from("#/definitions/") + &n.replace('<', "%3C").replace('>', "%3E"));
400 } else if let Some(n) = def.name.as_ref() {
401 def.reference =
402 Some(String::from("#/definitions/") + &n.replace('<', "%3C").replace('>', "%3E"));
403 }
404 if !Self::description().is_empty() {
405 def.description = Some(Self::description().to_owned());
406 }
407
408 def
409 }
410
411 fn security_scheme() -> Option<SecurityScheme> {
413 None
414 }
415
416 fn header_parameter_schema() -> Vec<Parameter<DefaultSchemaRaw>> {
417 vec![]
418 }
419}
420
421impl Apiv2Schema for () {}
422impl Apiv2Schema for serde_json::Value {}
423impl Apiv2Schema for serde_yaml::Value {}
424
425impl<T: TypedData> Apiv2Schema for T {
426 fn raw_schema() -> DefaultSchemaRaw {
427 DefaultSchemaRaw {
428 data_type: Some(T::data_type()),
429 format: T::format(),
430 maximum: T::max(),
431 minimum: T::min(),
432 ..Default::default()
433 }
434 }
435}
436
437#[cfg(feature = "nightly")]
438impl<T> Apiv2Schema for Option<T> {
439 default fn name() -> Option<String> {
440 None
441 }
442
443 default fn required() -> bool {
444 false
445 }
446
447 default fn raw_schema() -> DefaultSchemaRaw {
448 Default::default()
449 }
450
451 default fn security_scheme() -> Option<SecurityScheme> {
452 None
453 }
454
455 default fn header_parameter_schema() -> Vec<Parameter<DefaultSchemaRaw>> {
456 vec![]
457 }
458}
459
460impl<T: Apiv2Schema> Apiv2Schema for Option<T> {
461 fn name() -> Option<String> {
462 T::name()
463 }
464
465 fn required() -> bool {
466 false
467 }
468
469 fn raw_schema() -> DefaultSchemaRaw {
470 T::raw_schema()
471 }
472
473 fn security_scheme() -> Option<SecurityScheme> {
474 T::security_scheme()
475 }
476
477 fn header_parameter_schema() -> Vec<Parameter<DefaultSchemaRaw>> {
478 T::header_parameter_schema()
479 }
480}
481
482#[cfg(feature = "nightly")]
483impl<T, E> Apiv2Schema for Result<T, E> {
484 default fn name() -> Option<String> {
485 None
486 }
487
488 default fn raw_schema() -> DefaultSchemaRaw {
489 Default::default()
490 }
491
492 default fn security_scheme() -> Option<SecurityScheme> {
493 Default::default()
494 }
495
496 default fn header_parameter_schema() -> Vec<Parameter<DefaultSchemaRaw>> {
497 Default::default()
498 }
499}
500
501impl<T: Apiv2Schema, E> Apiv2Schema for Result<T, E> {
502 fn name() -> Option<String> {
503 T::name()
504 }
505
506 fn raw_schema() -> DefaultSchemaRaw {
507 T::raw_schema()
508 }
509
510 fn security_scheme() -> Option<SecurityScheme> {
511 T::security_scheme()
512 }
513
514 fn header_parameter_schema() -> Vec<Parameter<DefaultSchemaRaw>> {
515 T::header_parameter_schema()
516 }
517}
518
519impl<T: Apiv2Schema + Clone> Apiv2Schema for std::borrow::Cow<'_, T> {
520 fn name() -> Option<String> {
521 T::name()
522 }
523
524 fn raw_schema() -> DefaultSchemaRaw {
525 T::raw_schema()
526 }
527
528 fn security_scheme() -> Option<SecurityScheme> {
529 T::security_scheme()
530 }
531
532 fn header_parameter_schema() -> Vec<Parameter<DefaultSchemaRaw>> {
533 T::header_parameter_schema()
534 }
535}
536
537impl<T: Apiv2Schema> Apiv2Schema for &[T] {
538 fn raw_schema() -> DefaultSchemaRaw {
539 Vec::<T>::raw_schema()
540 }
541}
542
543impl<T: Apiv2Schema, const N: usize> Apiv2Schema for [T; N] {
544 fn raw_schema() -> DefaultSchemaRaw {
545 DefaultSchemaRaw {
546 data_type: Some(DataType::Array),
547 items: Some(T::schema_with_ref().into()),
548 ..Default::default()
549 }
550 }
551}
552
553macro_rules! impl_schema_array {
554 ($ty:ty) => {
555 impl<T: Apiv2Schema> Apiv2Schema for $ty {
556 fn raw_schema() -> DefaultSchemaRaw {
557 DefaultSchemaRaw {
558 data_type: Some(DataType::Array),
559 items: Some(T::schema_with_ref().into()),
560 ..Default::default()
561 }
562 }
563 }
564 };
565}
566
567macro_rules! impl_schema_map {
568 ($ty:ty) => {
569 impl<K: ToString, V: Apiv2Schema> Apiv2Schema for $ty {
570 fn raw_schema() -> DefaultSchemaRaw {
571 DefaultSchemaRaw {
572 data_type: Some(DataType::Object),
573 extra_props: Some(Either::Right(V::schema_with_ref().into())),
574 ..Default::default()
575 }
576 }
577 }
578 };
579}
580
581use crate::v2::models::Parameter;
582use std::{collections::*, path::PathBuf};
583
584impl_schema_array!(Vec<T>);
585impl_schema_array!(HashSet<T>);
586impl_schema_array!(LinkedList<T>);
587impl_schema_array!(VecDeque<T>);
588impl_schema_array!(BTreeSet<T>);
589impl_schema_array!(BinaryHeap<T>);
590
591impl_schema_map!(HashMap<K, V>);
592impl_schema_map!(BTreeMap<K, V>);
593
594pub trait Apiv2Operation {
602 fn operation() -> DefaultOperationRaw;
604
605 fn security_definitions() -> BTreeMap<String, SecurityScheme>;
607
608 fn definitions() -> BTreeMap<String, DefaultSchemaRaw>;
610
611 fn is_visible() -> bool {
612 true
613 }
614}
615
616pub trait Apiv2Errors {
621 const ERROR_MAP: &'static [(u16, &'static str)] = &[];
622 fn update_error_definitions(_op: &mut DefaultOperationRaw) {}
623 fn update_definitions(_map: &mut BTreeMap<String, DefaultSchemaRaw>) {}
624}
625
626impl Apiv2Errors for () {}
627#[cfg(feature = "actix-base")]
628impl Apiv2Errors for actix_web::Error {}