1use indexmap::IndexMap;
4use openapiv3::AnySchema;
5use schemars::schema::SingleOrVec;
6use serde_json::Value;
7
8pub trait ToSchema {
9 fn to_schema(&self) -> schemars::schema::Schema;
10}
11
12trait Convert<T> {
13 fn convert(&self) -> T;
14}
15
16impl ToSchema for openapiv3::Schema {
17 fn to_schema(&self) -> schemars::schema::Schema {
18 self.convert()
19 }
20}
21
22impl ToSchema for openapiv3::ReferenceOr<openapiv3::Schema> {
23 fn to_schema(&self) -> schemars::schema::Schema {
24 self.convert()
25 }
26}
27
28impl<I, T> Convert<Vec<T>> for Vec<I>
29where
30 I: Convert<T>,
31{
32 fn convert(&self) -> Vec<T> {
33 self.iter().map(Convert::convert).collect()
34 }
35}
36
37impl<I, T> Convert<Option<Vec<T>>> for Vec<I>
38where
39 I: Convert<T>,
40{
41 fn convert(&self) -> Option<Vec<T>> {
42 if self.is_empty() {
43 None
44 } else {
45 Some(self.iter().map(Convert::convert).collect())
46 }
47 }
48}
49
50impl Convert<schemars::schema::Schema> for openapiv3::ReferenceOr<openapiv3::Schema> {
51 fn convert(&self) -> schemars::schema::Schema {
52 match self {
53 openapiv3::ReferenceOr::Reference { reference } => {
54 schemars::schema::SchemaObject::new_ref(reference.clone()).into()
55 }
56 openapiv3::ReferenceOr::Item(schema) => schema.convert(),
57 }
58 }
59}
60
61impl<T, TT> Convert<TT> for openapiv3::ReferenceOr<Box<T>>
62where
63 openapiv3::ReferenceOr<T>: Convert<TT>,
64 T: Clone,
65{
66 fn convert(&self) -> TT {
67 self.clone().unbox().convert()
68 }
69}
70
71impl Convert<schemars::schema::Schema> for openapiv3::Schema {
72 fn convert(&self) -> schemars::schema::Schema {
73 let openapiv3::SchemaData {
95 nullable,
96 discriminator: _, external_docs: _, title,
100 description,
101 default,
102 deprecated,
103 read_only,
104 write_only,
105 example,
106 extensions,
107 } = self.schema_data.clone();
108
109 let metadata = schemars::schema::Metadata {
110 id: None,
111 title,
112 description,
113 default,
114 deprecated,
115 read_only,
116 write_only,
117 examples: example.into_iter().collect::<Vec<_>>(),
118 };
119
120 let metadata = Some(Box::new(metadata)).reduce();
121 let extensions = extensions.into_iter().collect();
122
123 match &self.schema_kind {
124 openapiv3::SchemaKind::Type(openapiv3::Type::String(openapiv3::StringType {
125 format,
126 pattern,
127 enumeration,
128 min_length,
129 max_length,
130 })) => schemars::schema::SchemaObject {
131 metadata,
132 instance_type: instance_type(schemars::schema::InstanceType::String, nullable),
133 format: format.convert(),
134 enum_values: enumeration.convert(),
135 string: Some(Box::new(schemars::schema::StringValidation {
136 max_length: max_length.convert(),
137 min_length: min_length.convert(),
138 pattern: pattern.clone(),
139 }))
140 .reduce(),
141 extensions,
142 ..Default::default()
143 },
144 openapiv3::SchemaKind::Type(openapiv3::Type::Number(openapiv3::NumberType {
145 format,
146 multiple_of,
147 exclusive_minimum,
148 exclusive_maximum,
149 minimum,
150 maximum,
151 enumeration,
152 })) => {
153 let (maximum, exclusive_maximum) = match (maximum, exclusive_maximum) {
154 (v, true) => (None, *v),
155 (v, false) => (*v, None),
156 };
157 let (minimum, exclusive_minimum) = match (minimum, exclusive_minimum) {
158 (v, true) => (None, *v),
159 (v, false) => (*v, None),
160 };
161 schemars::schema::SchemaObject {
162 metadata,
163 instance_type: instance_type(schemars::schema::InstanceType::Number, nullable),
164 format: format.convert(),
165 enum_values: enumeration.convert(),
166 number: Some(Box::new(schemars::schema::NumberValidation {
167 multiple_of: *multiple_of,
168 maximum,
169 exclusive_maximum,
170 minimum,
171 exclusive_minimum,
172 }))
173 .reduce(),
174 extensions,
175 ..Default::default()
176 }
177 }
178
179 openapiv3::SchemaKind::Type(openapiv3::Type::Integer(openapiv3::IntegerType {
180 format,
181 multiple_of,
182 exclusive_minimum,
183 exclusive_maximum,
184 minimum,
185 maximum,
186 enumeration,
187 })) => {
188 let (maximum, exclusive_maximum) = match (maximum, exclusive_maximum) {
189 (Some(v), true) => (None, Some(*v as f64)),
190 (Some(v), false) => (Some(*v as f64), None),
191 (None, _) => (None, None),
192 };
193 let (minimum, exclusive_minimum) = match (minimum, exclusive_minimum) {
194 (Some(v), true) => (None, Some(*v as f64)),
195 (Some(v), false) => (Some(*v as f64), None),
196 (None, _) => (None, None),
197 };
198 schemars::schema::SchemaObject {
199 metadata,
200 instance_type: instance_type(schemars::schema::InstanceType::Integer, nullable),
201 format: format.convert(),
202 enum_values: enumeration.convert(),
203 number: Some(Box::new(schemars::schema::NumberValidation {
204 multiple_of: multiple_of.map(|v| v as f64).convert(),
205 maximum,
206 exclusive_maximum,
207 minimum,
208 exclusive_minimum,
209 }))
210 .reduce(),
211 extensions,
212 ..Default::default()
213 }
214 }
215
216 openapiv3::SchemaKind::Type(openapiv3::Type::Object(openapiv3::ObjectType {
217 properties,
218 required,
219 additional_properties,
220 min_properties,
221 max_properties,
222 })) => schemars::schema::SchemaObject {
223 metadata,
224 instance_type: instance_type(schemars::schema::InstanceType::Object, nullable),
225 object: Some(Box::new(schemars::schema::ObjectValidation {
226 max_properties: max_properties.convert(),
227 min_properties: min_properties.convert(),
228 required: required.convert(),
229 properties: properties.convert(),
230 pattern_properties: schemars::Map::default(),
231 additional_properties: additional_properties.convert(),
232 property_names: None,
233 }))
234 .reduce(),
235 extensions,
236 ..Default::default()
237 },
238
239 openapiv3::SchemaKind::Type(openapiv3::Type::Array(openapiv3::ArrayType {
240 items,
241 min_items,
242 max_items,
243 unique_items,
244 })) => schemars::schema::SchemaObject {
245 metadata,
246 instance_type: instance_type(schemars::schema::InstanceType::Array, nullable),
247 array: Some(Box::new(schemars::schema::ArrayValidation {
248 items: items.as_ref().map(|items| {
249 schemars::schema::SingleOrVec::Single(Box::new(items.convert()))
250 }),
251 additional_items: None,
252 max_items: max_items.convert(),
253 min_items: min_items.convert(),
254 unique_items: if *unique_items { Some(true) } else { None },
255 contains: None,
256 }))
257 .reduce(),
258 extensions,
259 ..Default::default()
260 },
261
262 openapiv3::SchemaKind::Type(openapiv3::Type::Boolean(openapiv3::BooleanType {
263 enumeration,
264 })) => schemars::schema::SchemaObject {
265 metadata,
266 instance_type: instance_type(schemars::schema::InstanceType::Boolean, nullable),
267 enum_values: enumeration.convert(),
268 extensions,
269 ..Default::default()
270 },
271
272 openapiv3::SchemaKind::OneOf { one_of } => oneof_nullable_wrapper(
273 schemars::schema::SchemaObject {
274 metadata,
275 subschemas: Some(Box::new(schemars::schema::SubschemaValidation {
276 one_of: Some(one_of.convert()),
277 ..Default::default()
278 })),
279 extensions,
280 ..Default::default()
281 },
282 nullable,
283 ),
284
285 openapiv3::SchemaKind::AllOf { all_of } => oneof_nullable_wrapper(
286 schemars::schema::SchemaObject {
287 metadata,
288 subschemas: Some(Box::new(schemars::schema::SubschemaValidation {
289 all_of: Some(all_of.convert()),
290 ..Default::default()
291 })),
292 extensions,
293 ..Default::default()
294 },
295 nullable,
296 ),
297
298 openapiv3::SchemaKind::AnyOf { any_of } => oneof_nullable_wrapper(
299 schemars::schema::SchemaObject {
300 metadata,
301 subschemas: Some(Box::new(schemars::schema::SubschemaValidation {
302 any_of: Some(any_of.convert()),
303 ..Default::default()
304 })),
305 extensions,
306 ..Default::default()
307 },
308 nullable,
309 ),
310
311 openapiv3::SchemaKind::Not { not } => oneof_nullable_wrapper(
312 schemars::schema::SchemaObject {
313 metadata,
314 subschemas: Some(Box::new(schemars::schema::SubschemaValidation {
315 not: Some(Box::new(not.convert())),
316 ..Default::default()
317 })),
318 extensions,
319 ..Default::default()
320 },
321 nullable,
322 ),
323
324 openapiv3::SchemaKind::Any(AnySchema {
327 typ: None,
328 pattern: None,
329 multiple_of: None,
330 exclusive_minimum: None,
331 exclusive_maximum: None,
332 minimum: None,
333 maximum: None,
334 properties,
335 required,
336 additional_properties: None,
337 min_properties: None,
338 max_properties: None,
339 items: None,
340 min_items: None,
341 max_items: None,
342 unique_items: None,
343 format: None,
344 enumeration,
345 min_length: None,
346 max_length: None,
347 one_of,
348 all_of,
349 any_of,
350 not: None,
351 }) if properties.is_empty()
352 && required.is_empty()
353 && enumeration.is_empty()
354 && one_of.is_empty()
355 && all_of.is_empty()
356 && any_of.is_empty() =>
357 {
358 schemars::schema::SchemaObject {
359 metadata,
360 extensions,
361 ..Default::default()
362 }
363 }
364
365 openapiv3::SchemaKind::Any(AnySchema {
368 typ: None,
369 pattern: None,
370 multiple_of: None,
371 exclusive_minimum: None,
372 exclusive_maximum: None,
373 minimum: None,
374 maximum: None,
375 properties,
376 required,
377 additional_properties: None,
378 min_properties: None,
379 max_properties: None,
380 items: None,
381 min_items: None,
382 max_items: None,
383 unique_items: None,
384 format: None,
385 enumeration,
386 min_length: None,
387 max_length: None,
388 one_of,
389 all_of,
390 any_of,
391 not: None,
392 }) if properties.is_empty()
393 && required.is_empty()
394 && enumeration.len() == 1
395 && enumeration[0] == serde_json::Value::Null
396 && one_of.is_empty()
397 && all_of.is_empty()
398 && any_of.is_empty() =>
399 {
400 schemars::schema::SchemaObject {
401 metadata,
402 instance_type: Some(schemars::schema::InstanceType::Null.into()),
403 extensions,
404 ..Default::default()
405 }
406 }
407
408 openapiv3::SchemaKind::Any(AnySchema {
409 typ,
410 pattern,
411 multiple_of,
412 exclusive_minimum,
413 exclusive_maximum,
414 minimum,
415 maximum,
416 properties,
417 required,
418 additional_properties,
419 min_properties,
420 max_properties,
421 items,
422 min_items,
423 max_items,
424 unique_items,
425 enumeration,
426 format,
427 min_length,
428 max_length,
429 one_of,
430 all_of,
431 any_of,
432 not,
433 }) => {
434 let mut so = schemars::schema::SchemaObject {
435 metadata,
436 extensions,
437 ..Default::default()
438 };
439
440 if let Some(format) = format {
442 so.format = Some(format.clone());
443 }
444 if !enumeration.is_empty() {
445 so.enum_values = Some(enumeration.clone());
446 }
447
448 if let Some(pattern) = pattern {
450 so.string().pattern = Some(pattern.clone());
451 }
452 if let Some(min_length) = min_length {
453 so.string().min_length = Some(*min_length as u32);
454 }
455 if let Some(max_length) = max_length {
456 so.string().max_length = Some(*max_length as u32);
457 }
458
459 if let Some(multiple_of) = multiple_of {
461 so.number().multiple_of = Some(*multiple_of);
462 }
463 match (minimum, exclusive_minimum) {
464 (None, Some(true)) => {
465 todo!("exclusive_minimum set without minimum");
466 }
467 (None, _) => (),
468 (Some(minimum), Some(true)) => {
469 so.number().exclusive_minimum = Some(*minimum);
470 }
471 (Some(minimum), _) => {
472 so.number().minimum = Some(*minimum);
473 }
474 }
475 match (maximum, exclusive_maximum) {
476 (None, Some(true)) => {
477 todo!("exclusive_maximum set without maximum");
478 }
479 (None, _) => (),
480 (Some(maximum), Some(true)) => {
481 so.number().exclusive_maximum = Some(*maximum);
482 }
483 (Some(maximum), _) => {
484 so.number().maximum = Some(*maximum);
485 }
486 }
487
488 if !properties.is_empty() {
490 so.object().properties = properties.convert();
491 }
492 if !required.is_empty() {
493 so.object().required = required.convert();
494 }
495 if additional_properties.is_some() {
496 so.object().additional_properties = additional_properties.convert();
497 }
498 if let Some(min_properties) = min_properties {
499 so.object().min_properties = Some(*min_properties as u32);
500 }
501 if let Some(max_properties) = max_properties {
502 so.object().max_properties = Some(*max_properties as u32);
503 }
504
505 if items.is_some() {
507 so.array().items = items.convert().clone().map(SingleOrVec::Single);
508 }
509 if let Some(min_items) = min_items {
510 so.array().min_items = Some(*min_items as u32);
511 }
512 if let Some(max_items) = max_items {
513 so.array().max_items = Some(*max_items as u32);
514 }
515 if let Some(unique_items) = unique_items {
516 so.array().unique_items = Some(*unique_items);
517 }
518
519 if !one_of.is_empty() {
521 so.subschemas().one_of = one_of.convert();
522 }
523 if !all_of.is_empty() {
524 so.subschemas().all_of = all_of.convert();
525 }
526 if !any_of.is_empty() {
527 so.subschemas().any_of = any_of.convert();
528 }
529 if not.is_some() {
530 so.subschemas().not = not.convert();
531 }
532
533 match (typ.as_deref(), nullable) {
535 (Some("boolean"), _) => {
536 so.instance_type =
537 instance_type(schemars::schema::InstanceType::Boolean, nullable);
538 }
539 (Some("object"), _) => {
540 so.instance_type =
541 instance_type(schemars::schema::InstanceType::Object, nullable);
542 }
543 (Some("array"), _) => {
544 so.instance_type =
545 instance_type(schemars::schema::InstanceType::Array, nullable);
546 }
547 (Some("number"), _) => {
548 so.instance_type =
549 instance_type(schemars::schema::InstanceType::Number, nullable);
550 }
551 (Some("string"), _) => {
552 so.instance_type =
553 instance_type(schemars::schema::InstanceType::String, nullable);
554 }
555 (Some("integer"), _) => {
556 so.instance_type =
557 instance_type(schemars::schema::InstanceType::Integer, nullable);
558 }
559
560 (Some(typ), _) => todo!("invalid type: {}", typ),
561
562 (None, false) => (),
564
565 (None, true) => {
569 let instance_types = [
570 so.object
571 .is_some()
572 .then_some(schemars::schema::InstanceType::Object),
573 so.array
574 .is_some()
575 .then_some(schemars::schema::InstanceType::Array),
576 so.number
577 .is_some()
578 .then_some(schemars::schema::InstanceType::Array),
579 so.string
580 .is_some()
581 .then_some(schemars::schema::InstanceType::Array),
582 nullable.then_some(schemars::schema::InstanceType::Null),
583 ]
584 .into_iter()
585 .flatten()
586 .collect::<Vec<_>>();
587
588 so.instance_type = match (instance_types.first(), instance_types.len()) {
592 (Some(typ), 1) => Some(SingleOrVec::Single(Box::new(*typ))),
593 (Some(_), _) => Some(SingleOrVec::Vec(instance_types)),
594 (None, _) => None,
595 };
596 }
597 };
598
599 match &so.instance_type {
604 Some(SingleOrVec::Single(it))
605 if **it == schemars::schema::InstanceType::Null
606 && so.subschemas.is_some() =>
607 {
608 oneof_nullable_wrapper(
609 schemars::schema::SchemaObject {
610 instance_type: None,
611 ..so
612 },
613 nullable,
614 )
615 }
616 _ => so,
617 }
618 }
619 }
620 .into()
621 }
622}
623
624impl<T> Convert<Option<String>> for openapiv3::VariantOrUnknownOrEmpty<T>
625where
626 T: Convert<String>,
627{
628 fn convert(&self) -> Option<String> {
629 match self {
630 openapiv3::VariantOrUnknownOrEmpty::Item(i) => Some(i.convert()),
631 openapiv3::VariantOrUnknownOrEmpty::Unknown(s) => Some(s.clone()),
632 openapiv3::VariantOrUnknownOrEmpty::Empty => None,
633 }
634 }
635}
636
637impl Convert<String> for openapiv3::StringFormat {
638 fn convert(&self) -> String {
639 match self {
640 openapiv3::StringFormat::Date => "date",
641 openapiv3::StringFormat::DateTime => "date-time",
642 openapiv3::StringFormat::Password => "password",
643 openapiv3::StringFormat::Byte => "byte",
644 openapiv3::StringFormat::Binary => "binary",
645 }
646 .to_string()
647 }
648}
649
650impl Convert<String> for openapiv3::NumberFormat {
651 fn convert(&self) -> String {
652 match self {
653 openapiv3::NumberFormat::Float => "float",
654 openapiv3::NumberFormat::Double => "double",
655 }
656 .to_string()
657 }
658}
659
660impl Convert<String> for openapiv3::IntegerFormat {
661 fn convert(&self) -> String {
662 match self {
663 openapiv3::IntegerFormat::Int32 => "int32",
664 openapiv3::IntegerFormat::Int64 => "int64",
665 }
666 .to_string()
667 }
668}
669
670impl Convert<Value> for Option<String> {
671 fn convert(&self) -> Value {
672 match self {
673 Some(value) => Value::String(value.clone()),
674 None => Value::Null,
675 }
676 }
677}
678
679impl Convert<Value> for Option<f64> {
680 fn convert(&self) -> Value {
681 match self {
682 Some(value) => Value::Number(serde_json::Number::from_f64(*value).unwrap()),
683 None => Value::Null,
684 }
685 }
686}
687impl Convert<Value> for Option<i64> {
688 fn convert(&self) -> Value {
689 match self {
690 Some(value) => Value::Number(serde_json::Number::from(*value)),
691 None => Value::Null,
692 }
693 }
694}
695impl Convert<Value> for Option<bool> {
696 fn convert(&self) -> Value {
697 match self {
698 Some(value) => Value::Bool(*value),
699 None => Value::Null,
700 }
701 }
702}
703
704fn instance_type(
705 instance_type: schemars::schema::InstanceType,
706 nullable: bool,
707) -> Option<schemars::schema::SingleOrVec<schemars::schema::InstanceType>> {
708 if nullable {
709 Some(vec![instance_type, schemars::schema::InstanceType::Null].into())
710 } else {
711 Some(instance_type.into())
712 }
713}
714
715fn oneof_nullable_wrapper(
716 mut schema: schemars::schema::SchemaObject,
717 nullable: bool,
718) -> schemars::schema::SchemaObject {
719 if nullable {
720 let metadata = schema.metadata;
721 let extensions = schema.extensions;
722
723 schema.metadata = None;
724 schema.extensions = Default::default();
725
726 schemars::schema::SchemaObject {
727 metadata,
728 extensions,
729 subschemas: Some(Box::new(schemars::schema::SubschemaValidation {
730 one_of: Some(vec![
731 schemars::schema::SchemaObject {
732 instance_type: Some(schemars::schema::InstanceType::Null.into()),
733 ..Default::default()
734 }
735 .into(),
736 schema.into(),
737 ]),
738 ..Default::default()
739 })),
740 ..Default::default()
741 }
742 } else {
743 schema
744 }
745}
746
747impl Convert<Option<u32>> for Option<usize> {
748 fn convert(&self) -> Option<u32> {
749 (*self).map(|m| m as u32)
750 }
751}
752
753impl Convert<Option<f64>> for Option<f64> {
754 fn convert(&self) -> Option<f64> {
755 *self
756 }
757}
758
759impl Convert<schemars::Set<String>> for Vec<String> {
760 fn convert(&self) -> schemars::Set<String> {
761 self.iter().cloned().collect()
762 }
763}
764
765impl Convert<schemars::Map<String, schemars::schema::Schema>>
766 for IndexMap<String, openapiv3::ReferenceOr<Box<openapiv3::Schema>>>
767{
768 fn convert(&self) -> schemars::Map<String, schemars::schema::Schema> {
769 self.iter().map(|(k, v)| (k.clone(), v.convert())).collect()
770 }
771}
772
773impl<T, TT> Convert<TT> for Box<T>
774where
775 T: Convert<TT>,
776{
777 fn convert(&self) -> TT {
778 self.as_ref().convert()
779 }
780}
781
782impl<T, TT> Convert<Option<Box<TT>>> for Option<T>
783where
784 T: Convert<TT>,
785{
786 fn convert(&self) -> Option<Box<TT>> {
787 self.as_ref().map(|t| Box::new(t.convert()))
788 }
789}
790
791impl Convert<schemars::schema::Schema> for openapiv3::AdditionalProperties {
792 fn convert(&self) -> schemars::schema::Schema {
793 match self {
794 openapiv3::AdditionalProperties::Any(b) => schemars::schema::Schema::Bool(*b),
795 openapiv3::AdditionalProperties::Schema(schema) => schema.convert(),
796 }
797 }
798}
799
800trait OptionReduce {
801 fn reduce(self) -> Self;
802}
803
804impl<T> OptionReduce for Option<T>
806where
807 T: Default + PartialEq + std::fmt::Debug,
808{
809 fn reduce(self) -> Self {
810 match &self {
811 Some(s) if s != &T::default() => self,
812 _ => None,
813 }
814 }
815}
816
817#[cfg(test)]
818mod tests {
819 use serde_json::json;
820
821 use crate::to_schema::Convert;
822
823 #[test]
824 fn test_null() {
825 let schema_value = json!({ "enum": [null] });
826 let oa_schema = serde_json::from_value::<openapiv3::Schema>(schema_value).unwrap();
827
828 let schema = oa_schema.convert();
829 assert_eq!(
830 schema.into_object().instance_type,
831 Some(schemars::schema::SingleOrVec::Single(Box::new(
832 schemars::schema::InstanceType::Null
833 )))
834 );
835 }
836
837 #[test]
838 fn test_weird_integer() {
839 let schema_value = json!({
840 "type": "integer",
841 "minimum": 0.0,
842 });
843 let oa_schema = serde_json::from_value::<openapiv3::Schema>(schema_value.clone()).unwrap();
844 let js_schema = serde_json::from_value::<schemars::schema::Schema>(schema_value).unwrap();
845
846 let conv_schema = oa_schema.convert();
847 assert_eq!(conv_schema, js_schema);
848 }
849
850 #[test]
851 fn test_object_no_type() {
852 let schema_value = json!({
853 "properties": {
854 "foo": {}
855 }
856 });
857 let oa_schema = serde_json::from_value::<openapiv3::Schema>(schema_value.clone()).unwrap();
858 let js_schema = serde_json::from_value::<schemars::schema::Schema>(schema_value).unwrap();
859
860 let conv_schema = oa_schema.convert();
861 assert_eq!(conv_schema, js_schema);
862 }
863
864 #[test]
865 fn test_array_no_type() {
866 let schema_value = json!({
867 "items": {}
868 });
869 let oa_schema = serde_json::from_value::<openapiv3::Schema>(schema_value.clone()).unwrap();
870 let js_schema = serde_json::from_value::<schemars::schema::Schema>(schema_value).unwrap();
871
872 let conv_schema = oa_schema.convert();
873 assert_eq!(conv_schema, js_schema);
874 }
875
876 #[test]
877 fn test_number_no_type() {
878 let schema_value = json!({
879 "minimum": 100.0
880 });
881 let oa_schema = serde_json::from_value::<openapiv3::Schema>(schema_value.clone()).unwrap();
882 let js_schema = serde_json::from_value::<schemars::schema::Schema>(schema_value).unwrap();
883
884 let conv_schema = oa_schema.convert();
885 assert_eq!(conv_schema, js_schema);
886 }
887
888 #[test]
889 fn test_solo_enum() {
890 let schema_value = json!({
891 "enum": ["one"]
892 });
893 let oa_schema = serde_json::from_value::<openapiv3::Schema>(schema_value.clone()).unwrap();
894 let js_schema = serde_json::from_value::<schemars::schema::Schema>(schema_value).unwrap();
895
896 let conv_schema = oa_schema.convert();
897 assert_eq!(conv_schema, js_schema);
898 }
899}