Skip to content

Commit efddb7d

Browse files
authored
Add an impl of From<schema::Schema> for serde::Schema (#8)
1 parent eab585e commit efddb7d

File tree

3 files changed

+236
-11
lines changed

3 files changed

+236
-11
lines changed

src/form.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::collections::HashMap;
33
use std::collections::HashSet;
44
use std::str::FromStr;
55

6-
#[derive(Debug, PartialEq)]
6+
#[derive(Clone, Debug, PartialEq)]
77
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
88
pub enum Form {
99
Empty,
@@ -22,21 +22,21 @@ impl Default for Form {
2222
}
2323
}
2424

25-
#[derive(Debug, PartialEq)]
25+
#[derive(Clone, Debug, PartialEq)]
2626
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
2727
pub struct Ref {
2828
pub nullable: bool,
2929
pub definition: String,
3030
}
3131

32-
#[derive(Debug, PartialEq)]
32+
#[derive(Clone, Debug, PartialEq)]
3333
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
3434
pub struct Type {
3535
pub nullable: bool,
3636
pub type_value: TypeValue,
3737
}
3838

39-
#[derive(Debug, PartialEq)]
39+
#[derive(Clone, Debug, PartialEq)]
4040
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
4141
pub enum TypeValue {
4242
Boolean,
@@ -73,21 +73,21 @@ impl FromStr for TypeValue {
7373
}
7474
}
7575

76-
#[derive(Debug, PartialEq)]
76+
#[derive(Clone, Debug, PartialEq)]
7777
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
7878
pub struct Enum {
7979
pub nullable: bool,
8080
pub values: HashSet<String>,
8181
}
8282

83-
#[derive(Debug, PartialEq)]
83+
#[derive(Clone, Debug, PartialEq)]
8484
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
8585
pub struct Elements {
8686
pub nullable: bool,
8787
pub schema: Box<Schema>,
8888
}
8989

90-
#[derive(Debug, PartialEq)]
90+
#[derive(Clone, Debug, PartialEq)]
9191
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
9292
pub struct Properties {
9393
pub nullable: bool,
@@ -97,14 +97,14 @@ pub struct Properties {
9797
pub has_required: bool,
9898
}
9999

100-
#[derive(Debug, PartialEq)]
100+
#[derive(Clone, Debug, PartialEq)]
101101
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
102102
pub struct Values {
103103
pub nullable: bool,
104104
pub schema: Box<Schema>,
105105
}
106106

107-
#[derive(Debug, PartialEq)]
107+
#[derive(Clone, Debug, PartialEq)]
108108
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
109109
pub struct Discriminator {
110110
pub nullable: bool,

src/schema.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use serde_json::Value;
44
use std::collections::{HashMap, HashSet};
55
use std::convert::{TryFrom, TryInto};
66

7-
#[derive(Debug, Default, PartialEq)]
7+
#[derive(Clone, Debug, Default, PartialEq)]
88
pub struct Schema {
99
pub definitions: HashMap<String, Schema>,
1010
pub form: form::Form,

src/serde.rs

Lines changed: 226 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
use crate::schema;
12
use serde::{Deserialize, Serialize};
23
use serde_json::Value;
34
use std::collections::HashMap;
45

5-
#[derive(Serialize, Deserialize, Debug, Default, PartialEq)]
6+
#[derive(Clone, Serialize, Deserialize, Debug, Default, PartialEq)]
67
#[serde(rename_all = "camelCase")]
78
#[serde(deny_unknown_fields)]
89
pub struct Schema {
@@ -48,6 +49,127 @@ impl arbitrary::Arbitrary for Schema {
4849
}
4950
}
5051

52+
impl From<schema::Schema> for Schema {
53+
fn from(schema: schema::Schema) -> Schema {
54+
use crate::form;
55+
56+
let mut out = Schema::default();
57+
58+
if !schema.definitions.is_empty() {
59+
out.definitions = Some(
60+
schema
61+
.definitions
62+
.into_iter()
63+
.map(|(k, v)| (k, v.into()))
64+
.collect(),
65+
);
66+
}
67+
68+
match schema.form {
69+
form::Form::Empty => {}
70+
form::Form::Ref(form::Ref {
71+
nullable,
72+
definition,
73+
}) => {
74+
if nullable {
75+
out.nullable = Some(true);
76+
}
77+
78+
out.ref_ = Some(definition);
79+
}
80+
form::Form::Type(form::Type {
81+
nullable,
82+
type_value,
83+
}) => {
84+
if nullable {
85+
out.nullable = Some(true);
86+
}
87+
88+
out.type_ = Some(
89+
match type_value {
90+
form::TypeValue::Boolean => "boolean",
91+
form::TypeValue::Float32 => "float32",
92+
form::TypeValue::Float64 => "float64",
93+
form::TypeValue::Int8 => "int8",
94+
form::TypeValue::Uint8 => "uint8",
95+
form::TypeValue::Int16 => "int16",
96+
form::TypeValue::Uint16 => "uint16",
97+
form::TypeValue::Int32 => "int32",
98+
form::TypeValue::Uint32 => "uint32",
99+
form::TypeValue::String => "string",
100+
form::TypeValue::Timestamp => "timestamp",
101+
}
102+
.to_owned(),
103+
)
104+
}
105+
form::Form::Enum(form::Enum { nullable, values }) => {
106+
if nullable {
107+
out.nullable = Some(true);
108+
}
109+
110+
out.enum_ = Some(values.into_iter().collect());
111+
}
112+
form::Form::Elements(form::Elements { nullable, schema }) => {
113+
if nullable {
114+
out.nullable = Some(true);
115+
}
116+
117+
out.elements = Some(Box::new((*schema).into()));
118+
}
119+
form::Form::Properties(form::Properties {
120+
nullable,
121+
required,
122+
optional,
123+
additional,
124+
has_required,
125+
}) => {
126+
if nullable {
127+
out.nullable = Some(true);
128+
}
129+
130+
if has_required {
131+
out.properties =
132+
Some(required.into_iter().map(|(k, v)| (k, v.into())).collect());
133+
}
134+
135+
if !optional.is_empty() {
136+
out.optional_properties =
137+
Some(optional.into_iter().map(|(k, v)| (k, v.into())).collect());
138+
}
139+
140+
if additional {
141+
out.additional_properties = Some(true);
142+
}
143+
}
144+
form::Form::Values(form::Values { nullable, schema }) => {
145+
if nullable {
146+
out.nullable = Some(true);
147+
}
148+
149+
out.values = Some(Box::new((*schema).into()));
150+
}
151+
form::Form::Discriminator(form::Discriminator {
152+
nullable,
153+
discriminator,
154+
mapping,
155+
}) => {
156+
if nullable {
157+
out.nullable = Some(true);
158+
}
159+
160+
out.discriminator = Some(discriminator);
161+
out.mapping = Some(mapping.into_iter().map(|(k, v)| (k, v.into())).collect());
162+
}
163+
}
164+
165+
if !schema.metadata.is_empty() {
166+
out.metadata = Some(schema.metadata);
167+
}
168+
169+
out
170+
}
171+
}
172+
51173
#[cfg(test)]
52174
mod tests {
53175
use serde_json::json;
@@ -194,4 +316,107 @@ mod tests {
194316
.unwrap()
195317
);
196318
}
319+
320+
#[test]
321+
fn from_empty() {
322+
assert_roundtrip_try_into_from(json!({}));
323+
}
324+
325+
#[test]
326+
fn from_ref() {
327+
assert_roundtrip_try_into_from(json!({"ref": "foo"}));
328+
assert_roundtrip_try_into_from(json!({"ref": "foo", "nullable": true}));
329+
}
330+
331+
#[test]
332+
fn from_type() {
333+
assert_roundtrip_try_into_from(json!({"type": "boolean"}));
334+
assert_roundtrip_try_into_from(json!({"type": "boolean", "nullable": true}));
335+
336+
assert_roundtrip_try_into_from(json!({"type": "int8"}));
337+
assert_roundtrip_try_into_from(json!({"type": "uint8"}));
338+
assert_roundtrip_try_into_from(json!({"type": "int16"}));
339+
assert_roundtrip_try_into_from(json!({"type": "uint16"}));
340+
assert_roundtrip_try_into_from(json!({"type": "int32"}));
341+
assert_roundtrip_try_into_from(json!({"type": "uint32"}));
342+
assert_roundtrip_try_into_from(json!({"type": "string"}));
343+
assert_roundtrip_try_into_from(json!({"type": "timestamp"}));
344+
}
345+
346+
#[test]
347+
fn from_enum() {
348+
assert_roundtrip_try_into_from(json!({ "enum": ["foo"] }));
349+
assert_roundtrip_try_into_from(json!({ "enum": ["foo"], "nullable": true }));
350+
}
351+
352+
#[test]
353+
fn from_elements() {
354+
assert_roundtrip_try_into_from(json!({ "elements": { "type": "boolean" } }));
355+
assert_roundtrip_try_into_from(
356+
json!({ "elements": { "type": "boolean" }, "nullable": true }),
357+
);
358+
}
359+
360+
#[test]
361+
fn from_properties() {
362+
assert_roundtrip_try_into_from(json!({ "properties": { "foo": { "type": "boolean" }}}));
363+
assert_roundtrip_try_into_from(
364+
json!({ "optionalProperties": { "foo": { "type": "boolean" }}}),
365+
);
366+
assert_roundtrip_try_into_from(
367+
json!({ "properties": { "foo": { "type": "boolean" }}, "nullable": true }),
368+
);
369+
assert_roundtrip_try_into_from(
370+
json!({ "optionalProperties": { "foo": { "type": "boolean" }}, "nullable": true }),
371+
);
372+
assert_roundtrip_try_into_from(json!({
373+
"properties": { "foo": { "type": "boolean" }},
374+
"optionalProperties": { "bar": { "type": "boolean" }},
375+
}));
376+
assert_roundtrip_try_into_from(json!({
377+
"properties": { "foo": { "type": "boolean" }},
378+
"optionalProperties": { "bar": { "type": "boolean" }},
379+
"nullable": true,
380+
}));
381+
}
382+
383+
#[test]
384+
fn from_values() {
385+
assert_roundtrip_try_into_from(json!({ "values": { "type": "boolean" } }));
386+
assert_roundtrip_try_into_from(
387+
json!({ "values": { "type": "boolean" }, "nullable": true }),
388+
);
389+
}
390+
391+
#[test]
392+
fn from_discriminator() {
393+
assert_roundtrip_try_into_from(json!({
394+
"discriminator": "foo",
395+
"mapping": {
396+
"foo": {
397+
"properties": { "bar": { "type": "boolean" }},
398+
},
399+
},
400+
}));
401+
402+
assert_roundtrip_try_into_from(json!({
403+
"discriminator": "foo",
404+
"mapping": {
405+
"foo": {
406+
"properties": { "bar": { "type": "boolean" }}
407+
}
408+
},
409+
"nullable": true,
410+
}));
411+
}
412+
413+
fn assert_roundtrip_try_into_from(json: serde_json::Value) {
414+
use crate::schema;
415+
use std::convert::TryInto;
416+
417+
let serde_schema: super::Schema = serde_json::from_value(json).unwrap();
418+
let schema: schema::Schema = serde_schema.clone().try_into().unwrap();
419+
420+
assert_eq!(serde_schema, schema.into());
421+
}
197422
}

0 commit comments

Comments
 (0)