paperclip_core/v3/models/
schema.rs

1use crate::v2::models::Either;
2
3use super::{invalid_referenceor, v2};
4use std::ops::Deref;
5
6impl From<v2::DefaultSchemaRaw> for openapiv3::ReferenceOr<Box<openapiv3::Schema>> {
7    fn from(v2: v2::DefaultSchemaRaw) -> Self {
8        let x: openapiv3::ReferenceOr<openapiv3::Schema> = v2.into();
9        match x {
10            openapiv3::ReferenceOr::Reference { reference } => {
11                openapiv3::ReferenceOr::Reference { reference }
12            }
13            openapiv3::ReferenceOr::Item(item) => openapiv3::ReferenceOr::Item(Box::new(item)),
14        }
15    }
16}
17
18impl From<v2::DefaultSchemaRaw> for openapiv3::ReferenceOr<openapiv3::Schema> {
19    fn from(v2: v2::DefaultSchemaRaw) -> Self {
20        match v2.reference.clone() {
21            Some(reference) => v2::Reference { reference }.into(),
22            None => {
23                let item = openapiv3::Schema {
24                    schema_data: openapiv3::SchemaData {
25                        nullable: false,
26                        read_only: false,
27                        write_only: false,
28                        deprecated: false,
29                        external_docs: None,
30                        example: v2.example,
31                        title: v2.title,
32                        description: v2.description,
33                        discriminator: None,
34                        default: None,
35                        extensions: Default::default(),
36                    },
37                    schema_kind: {
38                        if let Some(data_type) = v2.data_type {
39                            v2_data_type_to_v3(
40                                &data_type,
41                                &v2.format,
42                                &v2.enum_,
43                                &v2.items,
44                                &v2.properties,
45                                &v2.extra_props,
46                                &v2.required,
47                            )
48                        } else {
49                            openapiv3::SchemaKind::Type(openapiv3::Type::Object(
50                                openapiv3::ObjectType::default(),
51                            ))
52                        }
53                    },
54                };
55                openapiv3::ReferenceOr::Item(item)
56            }
57        }
58    }
59}
60
61// helper function to convert a v2 DataType to v3, with explicit types making it more
62// rust-analyzer friendly as the DefaultSchemaRaw is autogenerated by a macro
63fn v2_data_type_to_v3(
64    data_type: &v2::DataType,
65    format: &Option<v2::DataTypeFormat>,
66    enum_: &[serde_json::Value],
67    items: &Option<Box<v2::DefaultSchemaRaw>>,
68    properties: &std::collections::BTreeMap<String, Box<v2::DefaultSchemaRaw>>,
69    extra_properties: &Option<Either<bool, Box<v2::DefaultSchemaRaw>>>,
70    required: &std::collections::BTreeSet<String>,
71) -> openapiv3::SchemaKind {
72    match data_type {
73        v2::DataType::Integer => {
74            openapiv3::SchemaKind::Type(openapiv3::Type::Integer(openapiv3::IntegerType {
75                format: match format {
76                    None => openapiv3::VariantOrUnknownOrEmpty::Empty,
77                    Some(format) => match format {
78                        v2::DataTypeFormat::Int32 => openapiv3::VariantOrUnknownOrEmpty::Item(
79                            openapiv3::IntegerFormat::Int32,
80                        ),
81                        v2::DataTypeFormat::Int64 => openapiv3::VariantOrUnknownOrEmpty::Item(
82                            openapiv3::IntegerFormat::Int64,
83                        ),
84                        other => {
85                            debug_assert!(false, "Invalid data type format: {:?}", other);
86                            openapiv3::VariantOrUnknownOrEmpty::Empty
87                        }
88                    },
89                },
90                multiple_of: None,
91                exclusive_minimum: false,
92                exclusive_maximum: false,
93                minimum: None,
94                maximum: None,
95                enumeration: enum_
96                    .iter()
97                    .cloned()
98                    .map(|v| serde_json::from_value(v).unwrap_or_default())
99                    .collect(),
100            }))
101        }
102        v2::DataType::Number => {
103            openapiv3::SchemaKind::Type(openapiv3::Type::Number(openapiv3::NumberType {
104                format: match format {
105                    None => openapiv3::VariantOrUnknownOrEmpty::Empty,
106                    Some(format) => match format {
107                        v2::DataTypeFormat::Float => openapiv3::VariantOrUnknownOrEmpty::Item(
108                            openapiv3::NumberFormat::Float {},
109                        ),
110                        v2::DataTypeFormat::Double => openapiv3::VariantOrUnknownOrEmpty::Item(
111                            openapiv3::NumberFormat::Double {},
112                        ),
113                        other => {
114                            debug_assert!(false, "Invalid data type format: {:?}", other);
115                            openapiv3::VariantOrUnknownOrEmpty::Empty
116                        }
117                    },
118                },
119                multiple_of: None,
120                exclusive_minimum: false,
121                exclusive_maximum: false,
122                minimum: None,
123                maximum: None,
124                enumeration: enum_
125                    .iter()
126                    .cloned()
127                    .map(|v| serde_json::from_value(v).unwrap_or_default())
128                    .collect(),
129            }))
130        }
131        v2::DataType::String => {
132            openapiv3::SchemaKind::Type(openapiv3::Type::String(openapiv3::StringType {
133                format: match format {
134                    None => openapiv3::VariantOrUnknownOrEmpty::Empty,
135                    Some(format) => match format {
136                        v2::DataTypeFormat::Byte => {
137                            openapiv3::VariantOrUnknownOrEmpty::Item(openapiv3::StringFormat::Byte)
138                        }
139                        v2::DataTypeFormat::Binary => openapiv3::VariantOrUnknownOrEmpty::Item(
140                            openapiv3::StringFormat::Binary,
141                        ),
142                        v2::DataTypeFormat::Date => {
143                            openapiv3::VariantOrUnknownOrEmpty::Item(openapiv3::StringFormat::Date)
144                        }
145                        v2::DataTypeFormat::DateTime => openapiv3::VariantOrUnknownOrEmpty::Item(
146                            openapiv3::StringFormat::DateTime,
147                        ),
148                        v2::DataTypeFormat::Password => openapiv3::VariantOrUnknownOrEmpty::Item(
149                            openapiv3::StringFormat::Password,
150                        ),
151                        v2::DataTypeFormat::Other => {
152                            debug_assert!(false, "Invalid data type format: other");
153                            openapiv3::VariantOrUnknownOrEmpty::Unknown(
154                                v2::DataTypeFormat::Other.to_string(),
155                            )
156                        }
157                        others => openapiv3::VariantOrUnknownOrEmpty::Unknown(others.to_string()),
158                    },
159                },
160                pattern: None,
161                enumeration: enum_
162                    .iter()
163                    .cloned()
164                    .map(|v| serde_json::from_value(v).unwrap_or_default())
165                    .collect(),
166                min_length: None,
167                max_length: None,
168            }))
169        }
170        v2::DataType::Boolean => {
171            openapiv3::SchemaKind::Type(openapiv3::Type::Boolean(Default::default()))
172        }
173        v2::DataType::Array => {
174            openapiv3::SchemaKind::Type(openapiv3::Type::Array(openapiv3::ArrayType {
175                items: items.as_ref().map(|items| items.deref().clone().into()),
176                min_items: None,
177                max_items: None,
178                unique_items: false,
179            }))
180        }
181        v2::DataType::Object => {
182            openapiv3::SchemaKind::Type(openapiv3::Type::Object(openapiv3::ObjectType {
183                properties: {
184                    properties.iter().fold(Default::default(), |mut i, b| {
185                        i.insert(b.0.to_string(), b.1.deref().clone().into());
186                        i
187                    })
188                },
189                required: required.iter().cloned().collect::<Vec<_>>(),
190                additional_properties: extra_properties.as_ref().map(|e| match e {
191                    Either::Right(box_schema) => openapiv3::AdditionalProperties::Schema(Box::new(
192                        box_schema.deref().clone().into(),
193                    )),
194                    Either::Left(v) => openapiv3::AdditionalProperties::Any(*v),
195                }),
196                min_properties: None,
197                max_properties: None,
198            }))
199        }
200        v2::DataType::File => {
201            openapiv3::SchemaKind::Type(openapiv3::Type::String(openapiv3::StringType {
202                format: openapiv3::VariantOrUnknownOrEmpty::Item(openapiv3::StringFormat::Binary),
203                ..Default::default()
204            }))
205        }
206    }
207}
208
209impl From<v2::Items> for openapiv3::ReferenceOr<Box<openapiv3::Schema>> {
210    fn from(v2: v2::Items) -> Self {
211        let kind = match v2.data_type {
212            None => {
213                return invalid_referenceor("Invalid Item, should have a data type".into());
214            }
215            Some(data_type) => match data_type {
216                v2::DataType::Integer => {
217                    openapiv3::SchemaKind::Type(openapiv3::Type::Integer(openapiv3::IntegerType {
218                        format: match &v2.format {
219                            None => openapiv3::VariantOrUnknownOrEmpty::Empty,
220                            Some(format) => match format {
221                                v2::DataTypeFormat::Int32 => {
222                                    openapiv3::VariantOrUnknownOrEmpty::Item(
223                                        openapiv3::IntegerFormat::Int32,
224                                    )
225                                }
226                                v2::DataTypeFormat::Int64 => {
227                                    openapiv3::VariantOrUnknownOrEmpty::Item(
228                                        openapiv3::IntegerFormat::Int64,
229                                    )
230                                }
231                                other => {
232                                    return invalid_referenceor(format!(
233                                        "Invalid data type format: {:?}",
234                                        other
235                                    ));
236                                }
237                            },
238                        },
239                        multiple_of: v2.multiple_of.map(|v| v as i64),
240                        exclusive_minimum: v2.exclusive_minimum.unwrap_or_default(),
241                        exclusive_maximum: v2.exclusive_maximum.unwrap_or_default(),
242                        minimum: v2.minimum.map(|v| v as i64),
243                        maximum: v2.maximum.map(|v| v as i64),
244                        enumeration: v2
245                            .enum_
246                            .iter()
247                            .cloned()
248                            .map(|v| serde_json::from_value(v).unwrap_or_default())
249                            .collect(),
250                    }))
251                }
252                v2::DataType::Number => {
253                    openapiv3::SchemaKind::Type(openapiv3::Type::Number(openapiv3::NumberType {
254                        format: match &v2.format {
255                            None => openapiv3::VariantOrUnknownOrEmpty::Empty,
256                            Some(format) => match format {
257                                v2::DataTypeFormat::Float => {
258                                    openapiv3::VariantOrUnknownOrEmpty::Item(
259                                        openapiv3::NumberFormat::Float {},
260                                    )
261                                }
262                                v2::DataTypeFormat::Double => {
263                                    openapiv3::VariantOrUnknownOrEmpty::Item(
264                                        openapiv3::NumberFormat::Double {},
265                                    )
266                                }
267                                other => {
268                                    return invalid_referenceor(format!(
269                                        "Invalid data type format: {:?}",
270                                        other
271                                    ));
272                                }
273                            },
274                        },
275                        multiple_of: v2.multiple_of.map(From::from),
276                        exclusive_minimum: v2.exclusive_minimum.unwrap_or_default(),
277                        exclusive_maximum: v2.exclusive_maximum.unwrap_or_default(),
278                        minimum: v2.minimum.map(From::from),
279                        maximum: v2.maximum.map(From::from),
280                        enumeration: v2
281                            .enum_
282                            .iter()
283                            .cloned()
284                            .map(|v| serde_json::from_value(v).unwrap_or_default())
285                            .collect(),
286                    }))
287                }
288                v2::DataType::String => {
289                    openapiv3::SchemaKind::Type(openapiv3::Type::String(openapiv3::StringType {
290                        format: match &v2.format {
291                            None => openapiv3::VariantOrUnknownOrEmpty::Empty,
292                            Some(format) => match format {
293                                v2::DataTypeFormat::Byte => {
294                                    openapiv3::VariantOrUnknownOrEmpty::Item(
295                                        openapiv3::StringFormat::Byte,
296                                    )
297                                }
298                                v2::DataTypeFormat::Binary => {
299                                    openapiv3::VariantOrUnknownOrEmpty::Item(
300                                        openapiv3::StringFormat::Binary,
301                                    )
302                                }
303                                v2::DataTypeFormat::Date => {
304                                    openapiv3::VariantOrUnknownOrEmpty::Item(
305                                        openapiv3::StringFormat::Date,
306                                    )
307                                }
308                                v2::DataTypeFormat::DateTime => {
309                                    openapiv3::VariantOrUnknownOrEmpty::Item(
310                                        openapiv3::StringFormat::DateTime,
311                                    )
312                                }
313                                v2::DataTypeFormat::Password => {
314                                    openapiv3::VariantOrUnknownOrEmpty::Item(
315                                        openapiv3::StringFormat::Password,
316                                    )
317                                }
318                                other => {
319                                    return invalid_referenceor(format!(
320                                        "Invalid data type format: {:?}",
321                                        other
322                                    ));
323                                }
324                            },
325                        },
326                        pattern: v2.pattern.clone(),
327                        enumeration: v2
328                            .enum_
329                            .iter()
330                            .cloned()
331                            .map(|v| serde_json::from_value(v).unwrap_or_default())
332                            .collect(),
333                        min_length: v2.min_length.map(|v| v as usize),
334                        max_length: v2.max_length.map(|v| v as usize),
335                    }))
336                }
337                v2::DataType::Boolean => {
338                    openapiv3::SchemaKind::Type(openapiv3::Type::Boolean(Default::default()))
339                }
340                v2::DataType::Array => {
341                    openapiv3::SchemaKind::Type(openapiv3::Type::Array(openapiv3::ArrayType {
342                        items: v2.items.map(|items| items.deref().clone().into()),
343                        min_items: v2.min_items.map(|v| v as usize),
344                        max_items: v2.max_items.map(|v| v as usize),
345                        unique_items: v2.unique_items.unwrap_or_default(),
346                    }))
347                }
348                invalid => {
349                    return invalid_referenceor(format!("Invalid Item data_type: {:?}", invalid))
350                }
351            },
352        };
353
354        openapiv3::ReferenceOr::Item(Box::new(openapiv3::Schema {
355            schema_data: Default::default(),
356            schema_kind: kind,
357        }))
358    }
359}