Read #[validate(...)] attributes
This commit is contained in:
parent
dada8582ee
commit
6ab567f3a5
13 changed files with 532 additions and 33 deletions
|
@ -62,13 +62,17 @@ impl<T: JsonSchema> JsonSchema for Option<T> {
|
|||
parent: &mut SchemaObject,
|
||||
name: String,
|
||||
metadata: Option<Metadata>,
|
||||
_required: bool,
|
||||
required: Option<bool>,
|
||||
) {
|
||||
let mut schema = gen.subschema_for::<Self>();
|
||||
schema = gen.apply_metadata(schema, metadata);
|
||||
if required == Some(true) {
|
||||
T::add_schema_as_property(gen, parent, name, metadata, required)
|
||||
} else {
|
||||
let mut schema = gen.subschema_for::<Self>();
|
||||
schema = gen.apply_metadata(schema, metadata);
|
||||
|
||||
let object = parent.object();
|
||||
object.properties.insert(name, schema);
|
||||
let object = parent.object();
|
||||
object.properties.insert(name, schema);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ macro_rules! forward_impl {
|
|||
parent: &mut crate::schema::SchemaObject,
|
||||
name: String,
|
||||
metadata: Option<crate::schema::Metadata>,
|
||||
required: bool,
|
||||
required: Option<bool>,
|
||||
) {
|
||||
<$target>::add_schema_as_property(gen, parent, name, metadata, required)
|
||||
}
|
||||
|
|
|
@ -375,13 +375,13 @@ pub trait JsonSchema {
|
|||
parent: &mut SchemaObject,
|
||||
name: String,
|
||||
metadata: Option<schema::Metadata>,
|
||||
required: bool,
|
||||
required: Option<bool>,
|
||||
) {
|
||||
let mut schema = gen.subschema_for::<Self>();
|
||||
schema = gen.apply_metadata(schema, metadata);
|
||||
|
||||
let object = parent.object();
|
||||
if required {
|
||||
if required.unwrap_or(true) {
|
||||
object.required.insert(name.clone());
|
||||
}
|
||||
object.properties.insert(name, schema);
|
||||
|
|
|
@ -9,6 +9,7 @@ use crate::JsonSchema;
|
|||
use crate::{Map, Set};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use std::ops::Deref;
|
||||
|
||||
/// A JSON Schema.
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
|
@ -191,7 +192,13 @@ where
|
|||
macro_rules! get_or_insert_default_fn {
|
||||
($name:ident, $ret:ty) => {
|
||||
get_or_insert_default_fn!(
|
||||
concat!("Returns a mutable reference to this schema's [`", stringify!($ret), "`](#structfield.", stringify!($name), "), creating it if it was `None`."),
|
||||
concat!(
|
||||
"Returns a mutable reference to this schema's [`",
|
||||
stringify!($ret),
|
||||
"`](#structfield.",
|
||||
stringify!($name),
|
||||
"), creating it if it was `None`."
|
||||
),
|
||||
$name,
|
||||
$ret
|
||||
);
|
||||
|
@ -224,6 +231,13 @@ impl SchemaObject {
|
|||
self.reference.is_some()
|
||||
}
|
||||
|
||||
// TODO document
|
||||
pub fn has_type(&self, ty: InstanceType) -> bool {
|
||||
self.instance_type
|
||||
.as_ref()
|
||||
.map_or(true, |x| x.contains(&ty))
|
||||
}
|
||||
|
||||
get_or_insert_default_fn!(metadata, Metadata);
|
||||
get_or_insert_default_fn!(subschemas, SubschemaValidation);
|
||||
get_or_insert_default_fn!(number, NumberValidation);
|
||||
|
@ -506,3 +520,13 @@ impl<T> From<Vec<T>> for SingleOrVec<T> {
|
|||
SingleOrVec::Vec(vec)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialEq> SingleOrVec<T> {
|
||||
// TODO document
|
||||
pub fn contains(&self, x: &T) -> bool {
|
||||
match self {
|
||||
SingleOrVec::Single(s) => s.deref() == x,
|
||||
SingleOrVec::Vec(v) => v.contains(x),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
81
schemars/tests/expected/validate.json
Normal file
81
schemars/tests/expected/validate.json
Normal file
|
@ -0,0 +1,81 @@
|
|||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "Struct",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"contains_str1",
|
||||
"contains_str2",
|
||||
"email_address",
|
||||
"homepage",
|
||||
"map_contains",
|
||||
"min_max",
|
||||
"non_empty_str",
|
||||
"pair",
|
||||
"regex_str1",
|
||||
"regex_str2",
|
||||
"required_option",
|
||||
"tel"
|
||||
],
|
||||
"properties": {
|
||||
"min_max": {
|
||||
"type": "number",
|
||||
"format": "float",
|
||||
"maximum": 100.0,
|
||||
"minimum": 0.01
|
||||
},
|
||||
"regex_str1": {
|
||||
"type": "string",
|
||||
"pattern": "^[Hh]ello\\b"
|
||||
},
|
||||
"regex_str2": {
|
||||
"type": "string",
|
||||
"pattern": "^[Hh]ello\\b"
|
||||
},
|
||||
"contains_str1": {
|
||||
"type": "string",
|
||||
"pattern": "substring\\.\\.\\."
|
||||
},
|
||||
"contains_str2": {
|
||||
"type": "string",
|
||||
"pattern": "substring\\.\\.\\."
|
||||
},
|
||||
"email_address": {
|
||||
"type": "string",
|
||||
"format": "email"
|
||||
},
|
||||
"tel": {
|
||||
"type": "string",
|
||||
"format": "phone"
|
||||
},
|
||||
"homepage": {
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
},
|
||||
"non_empty_str": {
|
||||
"type": "string",
|
||||
"maxLength": 100,
|
||||
"minLength": 1
|
||||
},
|
||||
"pair": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"maxItems": 2,
|
||||
"minItems": 2
|
||||
},
|
||||
"map_contains": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"map_key"
|
||||
],
|
||||
"additionalProperties": {
|
||||
"type": "null"
|
||||
}
|
||||
},
|
||||
"required_option": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
40
schemars/tests/validate.rs
Normal file
40
schemars/tests/validate.rs
Normal file
|
@ -0,0 +1,40 @@
|
|||
mod util;
|
||||
use schemars::JsonSchema;
|
||||
use std::collections::HashMap;
|
||||
use util::*;
|
||||
|
||||
// In real code, this would typically be a Regex, potentially created in a `lazy_static!`.
|
||||
static STARTS_WITH_HELLO: &'static str = r"^[Hh]ello\b";
|
||||
|
||||
#[derive(Debug, JsonSchema)]
|
||||
pub struct Struct {
|
||||
#[validate(range(min = 0.01, max = 100))]
|
||||
min_max: f32,
|
||||
#[validate(regex = "STARTS_WITH_HELLO")]
|
||||
regex_str1: String,
|
||||
#[validate(regex(path = "STARTS_WITH_HELLO", code = "foo"))]
|
||||
regex_str2: String,
|
||||
#[validate(contains = "substring...")]
|
||||
contains_str1: String,
|
||||
#[validate(contains(pattern = "substring...", message = "bar"))]
|
||||
contains_str2: String,
|
||||
#[validate(email)]
|
||||
email_address: String,
|
||||
#[validate(phone)]
|
||||
tel: String,
|
||||
#[validate(url)]
|
||||
homepage: String,
|
||||
#[validate(length(min = 1, max = 100))]
|
||||
non_empty_str: String,
|
||||
#[validate(length(equal = 2))]
|
||||
pair: Vec<i32>,
|
||||
#[validate(contains = "map_key")]
|
||||
map_contains: HashMap<String, ()>,
|
||||
#[validate(required)]
|
||||
required_option: Option<bool>,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate() -> TestResult {
|
||||
test_default_generated_schema::<Struct>("validate")
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue