1 //! Data types to decscribe data types.
3 //! This is loosly based on JSON Schema, but uses static rust data types. This way we can build
4 //! completely static API definitions that can be included within the programs read-only text
7 use std
::collections
::HashSet
;
10 use anyhow
::{bail, format_err, Error}
;
11 use serde_json
::{json, Value}
;
13 use crate::ConstRegexPattern
;
15 /// Error type for schema validation
17 /// The validation functions may produce several error message,
18 /// i.e. when validation objects, it can produce one message for each
19 /// erroneous object property.
20 #[derive(Default, Debug)]
21 pub struct ParameterError
{
22 error_list
: Vec
<(String
, Error
)>,
25 /// Like anyhow's `format_err` but producing a `ParameterError`.
27 macro_rules
! param_format_err
{
28 ($field
:expr
, $err
:expr
) => {
29 $
crate::ParameterError
::from(($field
, $err
))
32 ($field
:expr
, $
($msg
:tt
)+) => {
33 $
crate::ParameterError
::from(($field
, ::anyhow
::format_err
!($
($msg
)+)))
37 /// Like anyhow's `bail` but enclosing a `ParameterError`, so
38 /// a `downcast` can extract it later. This is useful for
39 /// API calls that need to do parameter checking manually.
41 macro_rules
! param_bail
{
42 ($field
:expr
, $err
:expr
) => {{
43 return Err($
crate::param_format_err
!($field
, $err
).into());
46 ($field
:expr
, $
($msg
:tt
)+) => {{
47 return Err($
crate::param_format_err
!($field
, $
($msg
)+).into());
51 impl std
::error
::Error
for ParameterError {}
54 pub fn new() -> Self {
56 error_list
: Vec
::new(),
60 pub fn push(&mut self, name
: String
, value
: Error
) {
61 self.error_list
.push((name
, value
));
64 pub fn len(&self) -> usize {
68 pub fn errors(&self) -> &[(String
, Error
)] {
72 pub fn into_inner(self) -> Vec
<(String
, Error
)> {
76 pub fn is_empty(&self) -> bool
{
80 pub fn add_errors(&mut self, prefix
: &str, err
: Error
) {
81 match err
.downcast
::<ParameterError
>() {
86 .map(|(key
, err
)| (format
!("{}/{}", prefix
, key
), err
)),
89 Err(err
) => self.push(prefix
.to_string(), err
),
93 pub(crate) fn from_list(error_list
: Vec
<(String
, Error
)>) -> Self {
98 impl fmt
::Display
for ParameterError
{
99 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
102 let mut msg
= String
::new();
104 if !self.is_empty() {
106 msg
.push_str("parameter verification failed - ");
107 let _
= write
!(msg
, "'{}': {}", self.error_list
[0].0, self.error_list
[0].1);
109 msg
.push_str("parameter verification failed:\n");
110 for (name
, err
) in self.error_list
.iter() {
111 let _
= writeln
!(msg
, "- '{}': {}", name
, err
);
116 write
!(f
, "{}", msg
.trim())
120 impl From
<(String
, Error
)> for ParameterError
{
121 fn from(err
: (String
, Error
)) -> Self {
122 let mut this
= Self::new();
123 this
.push(err
.0, err
.1);
128 impl<'a
> From
<(&'a
str, Error
)> for ParameterError
{
129 fn from(err
: (&'a
str, Error
)) -> Self {
130 Self::from((err
.0.to_string(), err
.1))
134 impl std
::iter
::Extend
<(String
, Error
)> for ParameterError
{
135 fn extend
<T
>(&mut self, iter
: T
)
137 T
: IntoIterator
<Item
= (String
, Error
)>,
139 self.error_list
.extend(iter
);
143 impl<'a
> std
::iter
::Extend
<(&'a
str, Error
)> for ParameterError
{
144 fn extend
<T
>(&mut self, iter
: T
)
146 T
: IntoIterator
<Item
= (&'a
str, Error
)>,
148 self.extend(iter
.into_iter().map(|(s
, e
)| (s
.to_string(), e
)));
152 impl IntoIterator
for ParameterError
{
153 type Item
= (String
, Error
);
154 type IntoIter
= <Vec
<(String
, Error
)> as IntoIterator
>::IntoIter
;
156 fn into_iter(self) -> Self::IntoIter
{
157 self.into_inner().into_iter()
161 impl FromIterator
<(String
, Error
)> for ParameterError
{
162 fn from_iter
<T
>(iter
: T
) -> Self
164 T
: IntoIterator
<Item
= (String
, Error
)>,
166 let mut this
= Self::new();
172 impl<'a
> FromIterator
<(&'a
str, Error
)> for ParameterError
{
173 fn from_iter
<T
>(iter
: T
) -> Self
175 T
: IntoIterator
<Item
= (&'a
str, Error
)>,
177 let mut this
= Self::new();
183 /// Data type to describe boolean values
185 #[cfg_attr(feature = "test-harness", derive(Eq, PartialEq))]
186 pub struct BooleanSchema
{
187 pub description
: &'
static str,
188 /// Optional default value.
189 pub default: Option
<bool
>,
193 pub const fn new(description
: &'
static str) -> Self {
200 pub const fn default(mut self, default: bool
) -> Self {
201 self.default = Some(default);
205 pub const fn schema(self) -> Schema
{
206 Schema
::Boolean(self)
209 /// Verify JSON value using a `BooleanSchema`.
210 pub fn verify_json(&self, data
: &Value
) -> Result
<(), Error
> {
211 if !data
.is_boolean() {
212 bail
!("Expected boolean value.");
218 /// Data type to describe integer values.
220 #[cfg_attr(feature = "test-harness", derive(Eq, PartialEq))]
221 pub struct IntegerSchema
{
222 pub description
: &'
static str,
223 /// Optional minimum.
224 pub minimum
: Option
<isize>,
225 /// Optional maximum.
226 pub maximum
: Option
<isize>,
227 /// Optional default.
228 pub default: Option
<isize>,
232 pub const fn new(description
: &'
static str) -> Self {
241 pub const fn default(mut self, default: isize) -> Self {
242 self.default = Some(default);
246 pub const fn minimum(mut self, minimum
: isize) -> Self {
247 self.minimum
= Some(minimum
);
251 pub const fn maximum(mut self, maximium
: isize) -> Self {
252 self.maximum
= Some(maximium
);
256 pub const fn schema(self) -> Schema
{
257 Schema
::Integer(self)
260 pub fn check_constraints(&self, value
: isize) -> Result
<(), Error
> {
261 if let Some(minimum
) = self.minimum
{
264 "value must have a minimum value of {} (got {})",
271 if let Some(maximum
) = self.maximum
{
274 "value must have a maximum value of {} (got {})",
284 /// Verify JSON value using an `IntegerSchema`.
285 pub fn verify_json(&self, data
: &Value
) -> Result
<(), Error
> {
286 if let Some(value
) = data
.as_i64() {
287 self.check_constraints(value
as isize)
289 bail
!("Expected integer value.");
294 /// Data type to describe (JSON like) number value
296 pub struct NumberSchema
{
297 pub description
: &'
static str,
298 /// Optional minimum.
299 pub minimum
: Option
<f64>,
300 /// Optional maximum.
301 pub maximum
: Option
<f64>,
302 /// Optional default.
303 pub default: Option
<f64>,
307 pub const fn new(description
: &'
static str) -> Self {
316 pub const fn default(mut self, default: f64) -> Self {
317 self.default = Some(default);
321 pub const fn minimum(mut self, minimum
: f64) -> Self {
322 self.minimum
= Some(minimum
);
326 pub const fn maximum(mut self, maximium
: f64) -> Self {
327 self.maximum
= Some(maximium
);
331 pub const fn schema(self) -> Schema
{
335 pub fn check_constraints(&self, value
: f64) -> Result
<(), Error
> {
336 if let Some(minimum
) = self.minimum
{
339 "value must have a minimum value of {} (got {})",
346 if let Some(maximum
) = self.maximum
{
349 "value must have a maximum value of {} (got {})",
359 /// Verify JSON value using an `NumberSchema`.
360 pub fn verify_json(&self, data
: &Value
) -> Result
<(), Error
> {
361 if let Some(value
) = data
.as_f64() {
362 self.check_constraints(value
)
364 bail
!("Expected number value.");
369 #[cfg(feature = "test-harness")]
370 impl Eq
for NumberSchema {}
372 #[cfg(feature = "test-harness")]
373 impl PartialEq
for NumberSchema
{
374 fn eq(&self, rhs
: &Self) -> bool
{
375 fn f64_eq(l
: Option
<f64>, r
: Option
<f64>) -> bool
{
377 (None
, None
) => true,
378 (Some(l
), Some(r
)) => (l
- r
).abs() < 0.0001,
383 self.description
== rhs
.description
384 && f64_eq(self.minimum
, rhs
.minimum
)
385 && f64_eq(self.maximum
, rhs
.maximum
)
386 && f64_eq(self.default, rhs
.default)
390 /// Data type to describe string values.
392 #[cfg_attr(feature = "test-harness", derive(Eq, PartialEq))]
393 pub struct StringSchema
{
394 pub description
: &'
static str,
395 /// Optional default value.
396 pub default: Option
<&'
static str>,
397 /// Optional minimal length.
398 pub min_length
: Option
<usize>,
399 /// Optional maximal length.
400 pub max_length
: Option
<usize>,
401 /// Optional microformat.
402 pub format
: Option
<&'
static ApiStringFormat
>,
403 /// A text representation of the format/type (used to generate documentation).
404 pub type_text
: Option
<&'
static str>,
408 pub const fn new(description
: &'
static str) -> Self {
419 pub const fn default(mut self, text
: &'
static str) -> Self {
420 self.default = Some(text
);
424 pub const fn format(mut self, format
: &'
static ApiStringFormat
) -> Self {
425 self.format
= Some(format
);
429 pub const fn type_text(mut self, type_text
: &'
static str) -> Self {
430 self.type_text
= Some(type_text
);
434 pub const fn min_length(mut self, min_length
: usize) -> Self {
435 self.min_length
= Some(min_length
);
439 pub const fn max_length(mut self, max_length
: usize) -> Self {
440 self.max_length
= Some(max_length
);
444 pub const fn schema(self) -> Schema
{
448 pub(crate) fn check_length(&self, length
: usize) -> Result
<(), Error
> {
449 if let Some(min_length
) = self.min_length
{
450 if length
< min_length
{
451 bail
!("value must be at least {} characters long", min_length
);
455 if let Some(max_length
) = self.max_length
{
456 if length
> max_length
{
457 bail
!("value may only be {} characters long", max_length
);
464 pub fn check_constraints(&self, value
: &str) -> Result
<(), Error
> {
465 self.check_length(value
.chars().count())?
;
467 if let Some(ref format
) = self.format
{
469 ApiStringFormat
::Pattern(regex
) => {
470 if !(regex
.regex_obj
)().is_match(value
) {
471 bail
!("value does not match the regex pattern");
474 ApiStringFormat
::Enum(variants
) => {
475 if !variants
.iter().any(|e
| e
.value
== value
) {
476 bail
!("value '{}' is not defined in the enumeration.", value
);
479 ApiStringFormat
::PropertyString(subschema
) => {
480 crate::de
::verify
::verify(subschema
, value
)?
;
482 ApiStringFormat
::VerifyFn(verify_fn
) => {
491 /// Verify JSON value using this `StringSchema`.
492 pub fn verify_json(&self, data
: &Value
) -> Result
<(), Error
> {
493 if let Some(value
) = data
.as_str() {
494 self.check_constraints(value
)
496 bail
!("Expected string value.");
500 /// Get the [`format`](ApiStringFormat), panics if there is no format.
501 pub const fn unwrap_format(&self) -> &'
static ApiStringFormat
{
504 None
=> panic
!("unwrap_format on StringSchema without format"),
509 /// Data type to describe array of values.
511 /// All array elements are of the same type, as defined in the `items`
514 #[cfg_attr(feature = "test-harness", derive(Eq, PartialEq))]
515 pub struct ArraySchema
{
516 pub description
: &'
static str,
517 /// Element type schema.
518 pub items
: &'
static Schema
,
519 /// Optional minimal length.
520 pub min_length
: Option
<usize>,
521 /// Optional maximal length.
522 pub max_length
: Option
<usize>,
526 pub const fn new(description
: &'
static str, item_schema
: &'
static Schema
) -> Self {
535 pub const fn min_length(mut self, min_length
: usize) -> Self {
536 self.min_length
= Some(min_length
);
540 pub const fn max_length(mut self, max_length
: usize) -> Self {
541 self.max_length
= Some(max_length
);
545 pub const fn schema(self) -> Schema
{
549 pub(crate) fn check_length(&self, length
: usize) -> Result
<(), Error
> {
550 if let Some(min_length
) = self.min_length
{
551 if length
< min_length
{
552 bail
!("array must contain at least {} elements", min_length
);
556 if let Some(max_length
) = self.max_length
{
557 if length
> max_length
{
558 bail
!("array may only contain {} elements", max_length
);
565 /// Verify JSON value using an `ArraySchema`.
566 pub fn verify_json(&self, data
: &Value
) -> Result
<(), Error
> {
567 let list
= match data
{
568 Value
::Array(ref list
) => list
,
569 Value
::Object(_
) => bail
!("Expected array - got object."),
570 _
=> bail
!("Expected array - got scalar value."),
573 self.check_length(list
.len())?
;
575 for (i
, item
) in list
.iter().enumerate() {
576 let result
= self.items
.verify_json(item
);
577 if let Err(err
) = result
{
578 param_bail
!(format
!("[{}]", i
), err
);
586 /// Property entry in an object schema:
588 /// - `name`: The name of the property
589 /// - `optional`: Set when the property is optional
590 /// - `schema`: Property type schema
591 pub type SchemaPropertyEntry
= (&'
static str, bool
, &'
static Schema
);
593 /// Lookup table to Schema properties
595 /// Stores a sorted list of `(name, optional, schema)` tuples:
597 /// - `name`: The name of the property
598 /// - `optional`: Set when the property is optional
599 /// - `schema`: Property type schema
601 /// **Note:** The list has to be storted by name, because we use
602 /// a binary search to find items.
604 /// This is a workaround unless RUST can const_fn `Hash::new()`
605 pub type SchemaPropertyMap
= &'
static [SchemaPropertyEntry
];
607 /// Data type to describe objects (maps).
609 #[cfg_attr(feature = "test-harness", derive(Eq, PartialEq))]
610 pub struct ObjectSchema
{
611 pub description
: &'
static str,
612 /// If set, allow additional properties which are not defined in
614 pub additional_properties
: bool
,
615 /// Property schema definitions.
616 pub properties
: SchemaPropertyMap
,
617 /// Default key name - used by `parse_parameter_string()`
618 pub default_key
: Option
<&'
static str>,
622 pub const fn new(description
: &'
static str, properties
: SchemaPropertyMap
) -> Self {
626 additional_properties
: false,
631 pub const fn additional_properties(mut self, additional_properties
: bool
) -> Self {
632 self.additional_properties
= additional_properties
;
636 pub const fn default_key(mut self, key
: &'
static str) -> Self {
637 self.default_key
= Some(key
);
641 pub const fn schema(self) -> Schema
{
645 pub fn lookup(&self, key
: &str) -> Option
<(bool
, &Schema
)> {
646 if let Ok(ind
) = self
648 .binary_search_by_key(&key
, |(name
, _
, _
)| name
)
650 let (_name
, optional
, prop_schema
) = self.properties
[ind
];
651 Some((optional
, prop_schema
))
657 /// Parse key/value pairs and verify with object schema
659 /// - `test_required`: is set, checks if all required properties are
661 pub fn parse_parameter_strings(
663 data
: &[(String
, String
)],
665 ) -> Result
<Value
, ParameterError
> {
666 ParameterSchema
::from(self).parse_parameter_strings(data
, test_required
)
670 /// Combines multiple *object* schemas into one.
672 /// Note that these are limited to object schemas. Other schemas will produce errors.
674 /// Technically this could also contain an `additional_properties` flag, however, in the JSON
675 /// Schema, this is not supported, so here we simply assume additional properties to be allowed.
677 #[cfg_attr(feature = "test-harness", derive(Eq, PartialEq))]
678 pub struct AllOfSchema
{
679 pub description
: &'
static str,
681 /// The parameter is checked against all of the schemas in the list. Note that all schemas must
682 /// be object schemas.
683 pub list
: &'
static [&'
static Schema
],
687 pub const fn new(description
: &'
static str, list
: &'
static [&'
static Schema
]) -> Self {
688 Self { description, list }
691 pub const fn schema(self) -> Schema
{
695 pub fn lookup(&self, key
: &str) -> Option
<(bool
, &Schema
)> {
696 for entry
in self.list
{
697 if let Some(v
) = entry
699 .expect("non-object-schema in `AllOfSchema`")
709 /// Parse key/value pairs and verify with object schema
711 /// - `test_required`: is set, checks if all required properties are
713 pub fn parse_parameter_strings(
715 data
: &[(String
, String
)],
717 ) -> Result
<Value
, ParameterError
> {
718 ParameterSchema
::from(self).parse_parameter_strings(data
, test_required
)
722 /// An object schema which is basically like a rust enum: exactly one variant may match.
724 /// Contrary to JSON Schema, we require there be a 'type' property to distinguish the types.
725 /// In serde-language, we use an internally tagged enum representation.
727 /// Note that these are limited to object schemas. Other schemas will produce errors.
729 #[cfg_attr(feature = "test-harness", derive(Eq, PartialEq))]
730 pub struct OneOfSchema
{
731 pub description
: &'
static str,
733 /// The type property entry.
735 /// This must be a static reference due to how we implemented the property iterator.
736 pub type_property_entry
: &'
static SchemaPropertyEntry
,
738 /// The parameter is checked against all of the schemas in the list. Note that all schemas must
739 /// be object schemas.
740 pub list
: &'
static [(&'
static str, &'
static Schema
)],
745 description
: &'
static str,
746 type_property_entry
: &'
static SchemaPropertyEntry
,
747 list
: &'
static [(&'
static str, &'
static Schema
)],
756 pub const fn schema(self) -> Schema
{
760 pub fn type_property(&self) -> &'
static str {
761 self.type_property_entry
.0
764 pub fn type_schema(&self) -> &'
static Schema
{
765 self.type_property_entry
.2
768 pub fn lookup(&self, key
: &str) -> Option
<(bool
, &Schema
)> {
769 if key
== self.type_property() {
770 return Some((false, self.type_schema()));
773 for (_variant
, entry
) in self.list
{
774 if let Some(v
) = entry
776 .expect("non-object-schema in `OneOfSchema`")
786 pub fn lookup_variant(&self, name
: &str) -> Option
<&Schema
> {
790 .binary_search_by_key(&name
, |(name
, _
)| name
)
796 /// Parse key/value pairs and verify with object schema
798 /// - `test_required`: is set, checks if all required properties are
800 pub fn parse_parameter_strings(
802 data
: &[(String
, String
)],
804 ) -> Result
<Value
, ParameterError
> {
805 ParameterSchema
::from(self).parse_parameter_strings(data
, test_required
)
809 /// Beside [`ObjectSchema`] we also have an [`AllOfSchema`] which also represents objects.
810 pub trait ObjectSchemaType
{
811 fn description(&self) -> &'
static str;
812 fn lookup(&self, key
: &str) -> Option
<(bool
, &Schema
)>;
813 fn properties(&self) -> ObjectPropertyIterator
;
814 fn additional_properties(&self) -> bool
;
815 fn default_key(&self) -> Option
<&'
static str>;
817 /// Verify JSON value using an object schema.
818 fn verify_json(&self, data
: &Value
) -> Result
<(), Error
> {
819 let map
= match data
{
820 Value
::Object(ref map
) => map
,
821 Value
::Array(_
) => bail
!("Expected object - got array."),
822 _
=> bail
!("Expected object - got scalar value."),
825 let mut errors
= ParameterError
::new();
827 let additional_properties
= self.additional_properties();
829 for (key
, value
) in map
{
830 if let Some((_optional
, prop_schema
)) = self.lookup(key
) {
831 if let Err(err
) = prop_schema
.verify_json(value
) {
832 errors
.add_errors(key
, err
);
834 } else if !additional_properties
{
837 format_err
!("schema does not allow additional properties."),
842 for (name
, optional
, _prop_schema
) in self.properties() {
843 if !(*optional
) && data
[name
] == Value
::Null
{
846 format_err
!("property is missing and it is not optional."),
851 if !errors
.is_empty() {
860 pub enum ObjectPropertyIterator
{
861 Simple(SimpleObjectPropertyIterator
),
862 OneOf(OneOfPropertyIterator
),
865 impl Iterator
for ObjectPropertyIterator
{
866 type Item
= &'
static SchemaPropertyEntry
;
868 fn next(&mut self) -> Option
<&'
static SchemaPropertyEntry
> {
870 Self::Simple(iter
) => iter
.next(),
871 Self::OneOf(iter
) => iter
.next(),
876 impl ObjectSchemaType
for ObjectSchema
{
877 fn description(&self) -> &'
static str {
881 fn lookup(&self, key
: &str) -> Option
<(bool
, &Schema
)> {
882 ObjectSchema
::lookup(self, key
)
885 fn properties(&self) -> ObjectPropertyIterator
{
886 ObjectPropertyIterator
::Simple(SimpleObjectPropertyIterator
{
888 properties
: Some(self.properties
.iter()),
893 fn additional_properties(&self) -> bool
{
894 self.additional_properties
897 fn default_key(&self) -> Option
<&'
static str> {
902 impl ObjectSchemaType
for AllOfSchema
{
903 fn description(&self) -> &'
static str {
907 fn lookup(&self, key
: &str) -> Option
<(bool
, &Schema
)> {
908 AllOfSchema
::lookup(self, key
)
911 fn properties(&self) -> ObjectPropertyIterator
{
912 ObjectPropertyIterator
::Simple(SimpleObjectPropertyIterator
{
913 schemas
: self.list
.iter(),
919 fn additional_properties(&self) -> bool
{
920 self.list
.iter().any(|schema
| {
923 .expect("non-object-schema in `AllOfSchema`")
924 .additional_properties()
928 fn default_key(&self) -> Option
<&'
static str> {
929 for schema
in self.list
{
930 let default_key
= schema
932 .expect("non-object-schema in `AllOfSchema`")
935 if default_key
.is_some() {
945 pub struct SimpleObjectPropertyIterator
{
946 schemas
: std
::slice
::Iter
<'
static, &'
static Schema
>,
947 properties
: Option
<std
::slice
::Iter
<'
static, SchemaPropertyEntry
>>,
948 nested
: Option
<Box
<ObjectPropertyIterator
>>,
951 impl Iterator
for SimpleObjectPropertyIterator
{
952 type Item
= &'
static SchemaPropertyEntry
;
954 fn next(&mut self) -> Option
<&'
static SchemaPropertyEntry
> {
956 match self.nested
.as_mut().and_then(Iterator
::next
) {
957 Some(item
) => return Some(item
),
958 None
=> self.nested
= None
,
961 match self.properties
.as_mut().and_then(Iterator
::next
) {
962 Some(item
) => return Some(item
),
963 None
=> match self.schemas
.next()?
{
964 Schema
::AllOf(o
) => self.nested
= Some(Box
::new(o
.properties())),
965 Schema
::OneOf(o
) => self.nested
= Some(Box
::new(o
.properties())),
966 Schema
::Object(o
) => self.properties
= Some(o
.properties
.iter()),
968 self.properties
= None
;
977 impl ObjectSchemaType
for OneOfSchema
{
978 fn description(&self) -> &'
static str {
982 fn lookup(&self, key
: &str) -> Option
<(bool
, &Schema
)> {
983 OneOfSchema
::lookup(self, key
)
986 fn properties(&self) -> ObjectPropertyIterator
{
987 ObjectPropertyIterator
::OneOf(OneOfPropertyIterator
{
988 type_property_entry
: self.type_property_entry
,
989 schemas
: self.list
.iter(),
990 done
: HashSet
::new(),
995 fn additional_properties(&self) -> bool
{
996 self.list
.iter().any(|(_
, schema
)| {
999 .expect("non-object-schema in `OneOfSchema`")
1000 .additional_properties()
1004 fn default_key(&self) -> Option
<&'
static str> {
1008 fn verify_json(&self, data
: &Value
) -> Result
<(), Error
> {
1009 let map
= match data
{
1010 Value
::Object(ref map
) => map
,
1011 Value
::Array(_
) => bail
!("Expected object - got array."),
1012 _
=> bail
!("Expected object - got scalar value."),
1015 // Without the type we also cannot verify anything else...:
1016 let variant
= match map
.get(self.type_property()) {
1017 None
=> bail
!("Missing '{}' property", self.type_property()),
1018 Some(Value
::String(v
)) => v
,
1019 _
=> bail
!("Expected string in '{}'", self.type_property()),
1023 .lookup_variant(variant
)
1024 .ok_or_else(|| format_err
!("invalid '{}': {}", self.type_property(), variant
))?
;
1026 schema
.verify_json(data
)
1031 pub struct OneOfPropertyIterator
{
1032 type_property_entry
: &'
static SchemaPropertyEntry
,
1033 schemas
: std
::slice
::Iter
<'
static, (&'
static str, &'
static Schema
)>,
1034 done
: HashSet
<&'
static str>,
1035 nested
: Option
<Box
<ObjectPropertyIterator
>>,
1038 impl Iterator
for OneOfPropertyIterator
{
1039 type Item
= &'
static SchemaPropertyEntry
;
1041 fn next(&mut self) -> Option
<&'
static SchemaPropertyEntry
> {
1042 if self.done
.insert(self.type_property_entry
.0) {
1043 return Some(self.type_property_entry
);
1047 match self.nested
.as_mut().and_then(Iterator
::next
) {
1049 if !self.done
.insert(item
.0) {
1054 None
=> self.nested
= None
,
1057 self.nested
= Some(Box
::new(
1062 .expect("non-object-schema in `OneOfSchema`")
1069 /// Schemas are used to describe complex data types.
1071 /// All schema types implement constant builder methods, and a final
1072 /// `schema()` method to convert them into a `Schema`.
1075 /// use proxmox_schema::{Schema, BooleanSchema, IntegerSchema, ObjectSchema};
1077 /// const SIMPLE_OBJECT: Schema = ObjectSchema::new(
1078 /// "A very simple object with 2 properties",
1079 /// &[ // this arrays needs to be storted by name!
1082 /// false /* required */,
1083 /// &IntegerSchema::new("A required integer property.")
1090 /// true /* optional */,
1091 /// &BooleanSchema::new("An optional boolean property.")
1099 #[cfg_attr(feature = "test-harness", derive(Eq, PartialEq))]
1102 Boolean(BooleanSchema
),
1103 Integer(IntegerSchema
),
1104 Number(NumberSchema
),
1105 String(StringSchema
),
1106 Object(ObjectSchema
),
1113 /// Verify JSON value with `schema`.
1114 pub fn verify_json(&self, data
: &Value
) -> Result
<(), Error
> {
1117 if !data
.is_null() {
1118 bail
!("Expected Null, but value is not Null.");
1121 Schema
::Object(s
) => s
.verify_json(data
)?
,
1122 Schema
::Array(s
) => s
.verify_json(data
)?
,
1123 Schema
::Boolean(s
) => s
.verify_json(data
)?
,
1124 Schema
::Integer(s
) => s
.verify_json(data
)?
,
1125 Schema
::Number(s
) => s
.verify_json(data
)?
,
1126 Schema
::String(s
) => s
.verify_json(data
)?
,
1127 Schema
::AllOf(s
) => s
.verify_json(data
)?
,
1128 Schema
::OneOf(s
) => s
.verify_json(data
)?
,
1133 /// Parse a simple value (no arrays and no objects)
1134 pub fn parse_simple_value(&self, value_str
: &str) -> Result
<Value
, Error
> {
1135 let value
= match self {
1137 bail
!("internal error - found Null schema.");
1139 Schema
::Boolean(_boolean_schema
) => {
1140 let res
= parse_boolean(value_str
)?
;
1143 Schema
::Integer(integer_schema
) => {
1144 let res
: isize = value_str
.parse()?
;
1145 integer_schema
.check_constraints(res
)?
;
1146 Value
::Number(res
.into())
1148 Schema
::Number(number_schema
) => {
1149 let res
: f64 = value_str
.parse()?
;
1150 number_schema
.check_constraints(res
)?
;
1151 Value
::Number(serde_json
::Number
::from_f64(res
).unwrap())
1153 Schema
::String(string_schema
) => {
1154 string_schema
.check_constraints(value_str
)?
;
1155 Value
::String(value_str
.into())
1157 _
=> bail
!("unable to parse complex (sub) objects."),
1162 /// Parse a complex property string (`ApiStringFormat::PropertyString`)
1163 pub fn parse_property_string(&'
static self, value_str
: &str) -> Result
<Value
, Error
> {
1164 // helper for object/allof schemas:
1165 fn parse_object
<T
: Into
<ParameterSchema
>>(
1168 default_key
: Option
<&'
static str>,
1169 ) -> Result
<Value
, Error
> {
1170 let mut param_list
= Vec
::new();
1171 for entry
in crate::property_string
::PropertyIterator
::new(value_str
) {
1172 let (key
, value
) = entry?
;
1174 Some(key
) => param_list
.push((key
.to_string(), value
.into_owned())),
1176 if let Some(key
) = default_key
{
1177 param_list
.push((key
.to_string(), value
.into_owned()));
1179 bail
!("Value without key, but schema does not define a default key.");
1187 .parse_parameter_strings(¶m_list
, true)
1188 .map_err(Error
::from
)
1192 Schema
::Object(object_schema
) => {
1193 parse_object(value_str
, object_schema
, object_schema
.default_key
)
1195 Schema
::AllOf(all_of_schema
) => parse_object(value_str
, all_of_schema
, None
),
1196 Schema
::Array(array_schema
) => {
1197 let mut array
: Vec
<Value
> = vec
![];
1198 let list
: Vec
<&str> = value_str
1199 .split(|c
: char| c
== '
,'
|| c
== '
;'
|| char::is_ascii_whitespace(&c
))
1200 .filter(|s
| !s
.is_empty())
1204 match array_schema
.items
.parse_simple_value(value
.trim()) {
1205 Ok(res
) => array
.push(res
),
1206 Err(err
) => bail
!("unable to parse array element: {}", err
),
1209 array_schema
.check_length(array
.len())?
;
1213 _
=> bail
!("Got unexpected schema type."),
1217 /// Gets the underlying [`BooleanSchema`], panics on different schemas.
1218 pub const fn unwrap_boolean_schema(&self) -> &BooleanSchema
{
1220 Schema
::Boolean(s
) => s
,
1221 _
=> panic
!("unwrap_boolean_schema on different schema"),
1225 /// Gets the underlying [`IntegerSchema`], panics on different schemas.
1226 pub const fn unwrap_integer_schema(&self) -> &IntegerSchema
{
1228 Schema
::Integer(s
) => s
,
1229 _
=> panic
!("unwrap_integer_schema on different schema"),
1233 /// Gets the underlying [`NumberSchema`], panics on different schemas.
1234 pub const fn unwrap_number_schema(&self) -> &NumberSchema
{
1236 Schema
::Number(s
) => s
,
1237 _
=> panic
!("unwrap_number_schema on different schema"),
1241 /// Gets the underlying [`StringSchema`], panics on different schemas.
1242 pub const fn unwrap_string_schema(&self) -> &StringSchema
{
1244 Schema
::String(s
) => s
,
1245 _
=> panic
!("unwrap_string_schema on different schema"),
1249 /// Gets the underlying [`ObjectSchema`], panics on different schemas.
1250 pub const fn unwrap_object_schema(&self) -> &ObjectSchema
{
1252 Schema
::Object(s
) => s
,
1253 _
=> panic
!("unwrap_object_schema on different schema"),
1257 /// Gets the underlying [`ArraySchema`], panics on different schemas.
1258 pub const fn unwrap_array_schema(&self) -> &ArraySchema
{
1260 Schema
::Array(s
) => s
,
1261 _
=> panic
!("unwrap_array_schema on different schema"),
1265 /// Gets the underlying [`AllOfSchema`], panics on different schemas.
1266 pub const fn unwrap_all_of_schema(&self) -> &AllOfSchema
{
1268 Schema
::AllOf(s
) => s
,
1269 _
=> panic
!("unwrap_all_of_schema on different schema"),
1273 /// Gets the underlying [`OneOfSchema`], panics on different schemas.
1274 pub const fn unwrap_one_of_schema(&self) -> &OneOfSchema
{
1276 Schema
::OneOf(s
) => s
,
1277 _
=> panic
!("unwrap_one_of_schema on different schema"),
1281 /// Gets the underlying [`BooleanSchema`].
1282 pub const fn boolean(&self) -> Option
<&BooleanSchema
> {
1284 Schema
::Boolean(s
) => Some(s
),
1289 /// Gets the underlying [`IntegerSchema`].
1290 pub const fn integer(&self) -> Option
<&IntegerSchema
> {
1292 Schema
::Integer(s
) => Some(s
),
1297 /// Gets the underlying [`NumberSchema`].
1298 pub const fn number(&self) -> Option
<&NumberSchema
> {
1300 Schema
::Number(s
) => Some(s
),
1305 /// Gets the underlying [`StringSchema`].
1306 pub const fn string(&self) -> Option
<&StringSchema
> {
1308 Schema
::String(s
) => Some(s
),
1313 /// Gets the underlying [`ObjectSchema`].
1314 pub const fn object(&self) -> Option
<&ObjectSchema
> {
1316 Schema
::Object(s
) => Some(s
),
1321 /// Gets the underlying [`ArraySchema`].
1322 pub const fn array(&self) -> Option
<&ArraySchema
> {
1324 Schema
::Array(s
) => Some(s
),
1329 /// Gets the underlying [`AllOfSchema`].
1330 pub const fn all_of(&self) -> Option
<&AllOfSchema
> {
1332 Schema
::AllOf(s
) => Some(s
),
1337 /// Gets the underlying [`AllOfSchema`].
1338 pub const fn one_of(&self) -> Option
<&OneOfSchema
> {
1340 Schema
::OneOf(s
) => Some(s
),
1345 pub fn any_object(&self) -> Option
<&dyn ObjectSchemaType
> {
1347 Schema
::Object(s
) => Some(s
),
1348 Schema
::AllOf(s
) => Some(s
),
1349 Schema
::OneOf(s
) => Some(s
),
1355 /// A string enum entry. An enum entry must have a value and a description.
1356 #[derive(Clone, Debug)]
1357 #[cfg_attr(feature = "test-harness", derive(Eq, PartialEq))]
1358 pub struct EnumEntry
{
1359 pub value
: &'
static str,
1360 pub description
: &'
static str,
1364 /// Convenience method as long as we only have 2 mandatory fields in an `EnumEntry`.
1365 pub const fn new(value
: &'
static str, description
: &'
static str) -> Self {
1366 Self { value, description }
1370 /// String microformat definitions.
1372 /// Strings are probably the most flexible data type, and there are
1373 /// several ways to define their content.
1377 /// Simple list all possible values.
1380 /// use proxmox_schema::{ApiStringFormat, EnumEntry};
1382 /// const format: ApiStringFormat = ApiStringFormat::Enum(&[
1383 /// EnumEntry::new("vm", "A guest VM run via qemu"),
1384 /// EnumEntry::new("ct", "A guest container run via lxc"),
1388 /// ## Regular Expressions
1390 /// Use a regular expression to describe valid strings.
1393 /// use proxmox_schema::{const_regex, ApiStringFormat};
1396 /// pub SHA256_HEX_REGEX = r"^[a-f0-9]{64}$";
1398 /// const format: ApiStringFormat = ApiStringFormat::Pattern(&SHA256_HEX_REGEX);
1401 /// ## Property Strings
1403 /// Use a schema to describe complex types encoded as string.
1405 /// Arrays are parsed as comma separated lists, i.e: `"1,2,3"`. The
1406 /// list may be sparated by comma, semicolon or whitespace.
1408 /// Objects are parsed as comma (or semicolon) separated `key=value` pairs, i.e:
1409 /// `"prop1=2,prop2=test"`. Any whitespace is trimmed from key and value.
1412 /// **Note:** This only works for types which does not allow using the
1413 /// comma, semicolon or whitespace separator inside the value,
1414 /// i.e. this only works for arrays of simple data types, and objects
1415 /// with simple properties (no nesting).
1418 /// use proxmox_schema::{ApiStringFormat, ArraySchema, IntegerSchema, Schema, StringSchema};
1419 /// use proxmox_schema::{parse_simple_value, parse_property_string};
1421 /// const PRODUCT_LIST_SCHEMA: Schema =
1422 /// ArraySchema::new("Product List.", &IntegerSchema::new("Product ID").schema())
1426 /// const SCHEMA: Schema = StringSchema::new("A list of Product IDs, comma separated.")
1427 /// .format(&ApiStringFormat::PropertyString(&PRODUCT_LIST_SCHEMA))
1430 /// let res = parse_simple_value("", &SCHEMA);
1431 /// assert!(res.is_err());
1433 /// let res = parse_simple_value("1,2,3", &SCHEMA); // parse as String
1434 /// assert!(res.is_ok());
1436 /// let data = parse_property_string("1,2", &PRODUCT_LIST_SCHEMA); // parse as Array
1437 /// assert!(data.is_ok());
1439 pub enum ApiStringFormat
{
1440 /// Enumerate all valid strings
1441 Enum(&'
static [EnumEntry
]),
1442 /// Use a regular expression to describe valid strings.
1443 Pattern(&'
static ConstRegexPattern
),
1444 /// Use a schema to describe complex types encoded as string.
1445 PropertyString(&'
static Schema
),
1446 /// Use a verification function.
1447 VerifyFn(ApiStringVerifyFn
),
1450 /// Type of a verification function for [`StringSchema`]s.
1451 pub type ApiStringVerifyFn
= fn(&str) -> Result
<(), Error
>;
1453 impl ApiStringFormat
{
1454 /// Gets the underlying [`&[EnumEntry]`](EnumEntry) list, panics on different formats.
1455 pub const fn unwrap_enum_format(&self) -> &'
static [EnumEntry
] {
1457 ApiStringFormat
::Enum(v
) => v
,
1458 _
=> panic
!("unwrap_enum_format on a different ApiStringFormat"),
1462 /// Gets the underlying [`&ConstRegexPattern`](ConstRegexPattern), panics on different formats.
1463 pub const fn unwrap_pattern_format(&self) -> &'
static ConstRegexPattern
{
1465 ApiStringFormat
::Pattern(v
) => v
,
1466 _
=> panic
!("unwrap_pattern_format on a different ApiStringFormat"),
1470 /// Gets the underlying property [`&Schema`](Schema), panics on different formats.
1471 pub const fn unwrap_property_string_format(&self) -> &'
static Schema
{
1473 ApiStringFormat
::PropertyString(v
) => v
,
1474 _
=> panic
!("unwrap_property_string_format on a different ApiStringFormat"),
1479 impl std
::fmt
::Debug
for ApiStringFormat
{
1480 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
1482 ApiStringFormat
::VerifyFn(fnptr
) => write
!(f
, "VerifyFn({:p}", fnptr
),
1483 ApiStringFormat
::Enum(variants
) => write
!(f
, "Enum({:?}", variants
),
1484 ApiStringFormat
::Pattern(regex
) => write
!(f
, "Pattern({:?}", regex
),
1485 ApiStringFormat
::PropertyString(schema
) => write
!(f
, "PropertyString({:?}", schema
),
1490 #[cfg(feature = "test-harness")]
1491 impl Eq
for ApiStringFormat {}
1493 #[cfg(feature = "test-harness")]
1494 impl PartialEq
for ApiStringFormat
{
1495 fn eq(&self, rhs
: &Self) -> bool
{
1497 (ApiStringFormat
::Enum(l
), ApiStringFormat
::Enum(r
)) => l
== r
,
1498 (ApiStringFormat
::Pattern(l
), ApiStringFormat
::Pattern(r
)) => l
== r
,
1499 (ApiStringFormat
::PropertyString(l
), ApiStringFormat
::PropertyString(r
)) => l
== r
,
1500 (ApiStringFormat
::VerifyFn(l
), ApiStringFormat
::VerifyFn(r
)) => std
::ptr
::eq(l
, r
),
1506 /// Parameters are objects, but we have two types of object schemas, the regular one and the
1508 #[derive(Clone, Copy, Debug)]
1509 #[cfg_attr(feature = "test-harness", derive(Eq, PartialEq))]
1510 pub enum ParameterSchema
{
1511 Object(&'
static ObjectSchema
),
1512 AllOf(&'
static AllOfSchema
),
1513 OneOf(&'
static OneOfSchema
),
1516 impl ParameterSchema
{
1517 /// Parse key/value pairs and verify with object schema
1519 /// - `test_required`: is set, checks if all required properties are
1521 pub fn parse_parameter_strings(
1523 data
: &[(String
, String
)],
1524 test_required
: bool
,
1525 ) -> Result
<Value
, ParameterError
> {
1526 do_parse_parameter_strings(self, data
, test_required
)
1530 impl ObjectSchemaType
for ParameterSchema
{
1531 fn description(&self) -> &'
static str {
1533 ParameterSchema
::Object(o
) => o
.description(),
1534 ParameterSchema
::AllOf(o
) => o
.description(),
1535 ParameterSchema
::OneOf(o
) => o
.description(),
1539 fn lookup(&self, key
: &str) -> Option
<(bool
, &Schema
)> {
1541 ParameterSchema
::Object(o
) => o
.lookup(key
),
1542 ParameterSchema
::AllOf(o
) => o
.lookup(key
),
1543 ParameterSchema
::OneOf(o
) => o
.lookup(key
),
1547 fn properties(&self) -> ObjectPropertyIterator
{
1549 ParameterSchema
::Object(o
) => o
.properties(),
1550 ParameterSchema
::AllOf(o
) => o
.properties(),
1551 ParameterSchema
::OneOf(o
) => o
.properties(),
1555 fn additional_properties(&self) -> bool
{
1557 ParameterSchema
::Object(o
) => o
.additional_properties(),
1558 ParameterSchema
::AllOf(o
) => o
.additional_properties(),
1559 ParameterSchema
::OneOf(o
) => o
.additional_properties(),
1563 fn default_key(&self) -> Option
<&'
static str> {
1565 ParameterSchema
::Object(o
) => o
.default_key(),
1566 ParameterSchema
::AllOf(o
) => o
.default_key(),
1567 ParameterSchema
::OneOf(o
) => o
.default_key(),
1572 impl From
<&'
static ObjectSchema
> for ParameterSchema
{
1573 fn from(schema
: &'
static ObjectSchema
) -> Self {
1574 ParameterSchema
::Object(schema
)
1578 impl From
<&'
static AllOfSchema
> for ParameterSchema
{
1579 fn from(schema
: &'
static AllOfSchema
) -> Self {
1580 ParameterSchema
::AllOf(schema
)
1584 impl From
<&'
static OneOfSchema
> for ParameterSchema
{
1585 fn from(schema
: &'
static OneOfSchema
) -> Self {
1586 ParameterSchema
::OneOf(schema
)
1590 /// Helper function to parse boolean values
1592 /// - true: `1 | on | yes | true`
1593 /// - false: `0 | off | no | false`
1594 pub fn parse_boolean(value_str
: &str) -> Result
<bool
, Error
> {
1595 match value_str
.to_lowercase().as_str() {
1596 "1" | "on" | "yes" | "true" => Ok(true),
1597 "0" | "off" | "no" | "false" => Ok(false),
1598 _
=> bail
!("Unable to parse boolean option."),
1602 /// Parse a complex property string (`ApiStringFormat::PropertyString`)
1603 #[deprecated(note = "this is now a method of Schema")]
1604 pub fn parse_property_string(value_str
: &str, schema
: &'
static Schema
) -> Result
<Value
, Error
> {
1605 schema
.parse_property_string(value_str
)
1608 /// Parse a simple value (no arrays and no objects)
1609 #[deprecated(note = "this is now a method of Schema")]
1610 pub fn parse_simple_value(value_str
: &str, schema
: &Schema
) -> Result
<Value
, Error
> {
1611 schema
.parse_simple_value(value_str
)
1614 /// Parse key/value pairs and verify with object schema
1616 /// - `test_required`: is set, checks if all required properties are
1618 #[deprecated(note = "this is now a method of parameter schema types")]
1619 pub fn parse_parameter_strings
<T
: Into
<ParameterSchema
>>(
1620 data
: &[(String
, String
)],
1622 test_required
: bool
,
1623 ) -> Result
<Value
, ParameterError
> {
1624 do_parse_parameter_strings(schema
.into(), data
, test_required
)
1627 fn do_parse_parameter_strings(
1628 schema
: ParameterSchema
,
1629 data
: &[(String
, String
)],
1630 test_required
: bool
,
1631 ) -> Result
<Value
, ParameterError
> {
1632 let mut params
= json
!({}
);
1634 let mut errors
= ParameterError
::new();
1636 let additional_properties
= schema
.additional_properties();
1638 for (key
, value
) in data
{
1639 if let Some((_optional
, prop_schema
)) = schema
.lookup(key
) {
1641 Schema
::Array(array_schema
) => {
1642 if params
[key
] == Value
::Null
{
1643 params
[key
] = json
!([]);
1646 Value
::Array(ref mut array
) => {
1647 match array_schema
.items
.parse_simple_value(value
) {
1648 Ok(res
) => array
.push(res
), // fixme: check_length??
1649 Err(err
) => errors
.push(key
.into(), err
),
1653 errors
.push(key
.into(), format_err
!("expected array - type missmatch"))
1657 _
=> match prop_schema
.parse_simple_value(value
) {
1659 if params
[key
] == Value
::Null
{
1662 errors
.push(key
.into(), format_err
!("duplicate parameter."));
1665 Err(err
) => errors
.push(key
.into(), err
),
1668 } else if additional_properties
{
1671 params
[key
] = Value
::String(value
.to_owned());
1673 Value
::String(ref old
) => {
1674 params
[key
] = Value
::Array(vec
![
1675 Value
::String(old
.to_owned()),
1676 Value
::String(value
.to_owned()),
1679 Value
::Array(ref mut array
) => {
1680 array
.push(Value
::String(value
.to_string()));
1682 _
=> errors
.push(key
.into(), format_err
!("expected array - type missmatch")),
1687 format_err
!("schema does not allow additional properties."),
1692 if test_required
&& errors
.is_empty() {
1693 for (name
, optional
, _prop_schema
) in schema
.properties() {
1694 if !(*optional
) && params
[name
] == Value
::Null
{
1697 format_err
!("parameter is missing and it is not optional."),
1703 if !errors
.is_empty() {
1710 /// Verify JSON value with `schema`.
1711 #[deprecated(note = "use the method schema.verify_json() instead")]
1712 pub fn verify_json(data
: &Value
, schema
: &Schema
) -> Result
<(), Error
> {
1713 schema
.verify_json(data
)
1716 /// Verify JSON value using a `StringSchema`.
1717 #[deprecated(note = "use the method string_schema.verify_json() instead")]
1718 pub fn verify_json_string(data
: &Value
, schema
: &StringSchema
) -> Result
<(), Error
> {
1719 schema
.verify_json(data
)
1722 /// Verify JSON value using a `BooleanSchema`.
1723 #[deprecated(note = "use the method boolean_schema.verify_json() instead")]
1724 pub fn verify_json_boolean(data
: &Value
, schema
: &BooleanSchema
) -> Result
<(), Error
> {
1725 schema
.verify_json(data
)
1728 /// Verify JSON value using an `IntegerSchema`.
1729 #[deprecated(note = "use the method integer_schema.verify_json() instead")]
1730 pub fn verify_json_integer(data
: &Value
, schema
: &IntegerSchema
) -> Result
<(), Error
> {
1731 schema
.verify_json(data
)
1734 /// Verify JSON value using an `NumberSchema`.
1735 #[deprecated(note = "use the method number_schema.verify_json() instead")]
1736 pub fn verify_json_number(data
: &Value
, schema
: &NumberSchema
) -> Result
<(), Error
> {
1737 schema
.verify_json(data
)
1740 /// Verify JSON value using an `ArraySchema`.
1741 #[deprecated(note = "use the method array_schema.verify_json() instead")]
1742 pub fn verify_json_array(data
: &Value
, schema
: &ArraySchema
) -> Result
<(), Error
> {
1743 schema
.verify_json(data
)
1746 /// Verify JSON value using an `ObjectSchema`.
1747 #[deprecated(note = "use the verify_json() method via the ObjectSchemaType trait instead")]
1748 pub fn verify_json_object(data
: &Value
, schema
: &dyn ObjectSchemaType
) -> Result
<(), Error
> {
1749 schema
.verify_json(data
)
1752 /// API types should define an "updater type" via this trait in order to support derived "Updater"
1753 /// structs more easily.
1755 /// Most trivial types can simply use an `Option<Self>` as updater. For types which do not use the
1756 /// `#[api]` macro, this will need to be explicitly created (or derived via
1757 /// `#[derive(UpdaterType)]`.
1758 pub trait UpdaterType
: Sized
{
1759 type Updater
: Updater
;
1762 #[cfg(feature = "api-macro")]
1763 pub use proxmox_api_macro
::UpdaterType
;
1765 #[cfg(feature = "api-macro")]
1767 pub use proxmox_api_macro
::Updater
;
1769 macro_rules
! basic_updater_type
{
1772 impl UpdaterType
for $ty
{
1773 type Updater
= Option
<Self>;
1778 basic_updater_type
! { bool u8 u16 u32 u64 i8 i16 i32 i64 usize isize f32 f64 String char }
1780 impl<T
> UpdaterType
for Option
<T
>
1784 type Updater
= T
::Updater
;
1787 // this will replace the whole Vec
1788 impl<T
> UpdaterType
for Vec
<T
> {
1789 type Updater
= Option
<Self>;
1792 /// Trait signifying that a type contains an API schema.
1794 const API_SCHEMA
: Schema
;
1797 impl<T
: ApiType
> ApiType
for Option
<T
> {
1798 const API_SCHEMA
: Schema
= T
::API_SCHEMA
;
1801 /// A helper type for "Updater" structs. This trait is *not* implemented for an api "base" type
1802 /// when deriving an `Updater` for it, though the generated *updater* type does implement this
1805 /// This trait is mostly to figure out if an updater is empty (iow. it should not be applied).
1806 /// In that, it is useful when a type which should have an updater also has optional fields which
1807 /// should not be serialized. Instead of `#[serde(skip_serializing_if = "Option::is_none")]`, this
1808 /// trait's `is_empty` needs to be used via `#[serde(skip_serializing_if = "Updater::is_empty")]`.
1810 /// Check if the updater is "none" or "empty".
1811 fn is_empty(&self) -> bool
;
1814 impl<T
> Updater
for Vec
<T
> {
1815 fn is_empty(&self) -> bool
{
1820 impl<T
> Updater
for Option
<T
> {
1821 fn is_empty(&self) -> bool
{
1826 /// Return type schema. Return types may be any schema and additionally be optional.
1827 #[cfg_attr(feature = "test-harness", derive(Eq, PartialEq))]
1828 pub struct ReturnType
{
1829 /// A return type may be optional, meaning the method may return null or some fixed data.
1831 /// If true, the return type in pseudo openapi terms would be `"oneOf": [ "null", "T" ]`.
1834 /// The method's return type.
1835 pub schema
: &'
static Schema
,
1838 impl std
::fmt
::Debug
for ReturnType
{
1839 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
1841 write
!(f
, "optional {:?}", self.schema
)
1843 write
!(f
, "{:?}", self.schema
)
1849 pub const fn new(optional
: bool
, schema
: &'
static Schema
) -> Self {
1850 Self { optional, schema }