1 //! Property string deserialization.
8 use serde
::de
::{self, IntoDeserializer}
;
10 use crate::schema
::{self, ArraySchema, Schema}
;
18 pub use extract
::ExtractValueDeserializer
;
20 use cow3
::{str_slice_to_range, Cow3}
;
22 // Used to disable calling `check_constraints` on a `StringSchema` if it is being deserialized
23 // for a `PropertyString`, which performs its own checking.
25 static IN_PROPERTY_STRING
: Cell
<bool
> = Cell
::new(false);
28 pub(crate) struct InPropertyStringGuard
;
30 pub(crate) fn set_in_property_string() -> InPropertyStringGuard
{
31 IN_PROPERTY_STRING
.with(|v
| v
.set(true));
35 impl Drop
for InPropertyStringGuard
{
37 IN_PROPERTY_STRING
.with(|v
| v
.set(false));
42 pub struct Error(Cow
<'
static, str>);
44 impl std
::error
::Error
for Error {}
46 impl fmt
::Display
for Error
{
47 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
48 fmt
::Display
::fmt(&self.0, f
)
53 pub(crate) fn msg
<T
: Into
<Cow
<'
static, str>>>(msg
: T
) -> Self {
57 fn invalid
<T
: fmt
::Display
>(msg
: T
) -> Self {
58 Self::msg(format
!("schema validation failed: {}", msg
))
62 impl serde
::de
::Error
for Error
{
63 fn custom
<T
: fmt
::Display
>(msg
: T
) -> Self {
64 Self(msg
.to_string().into())
68 impl From
<serde_json
::Error
> for Error
{
69 fn from(error
: serde_json
::Error
) -> Self {
70 Self(error
.to_string().into())
74 impl From
<fmt
::Error
> for Error
{
75 fn from(err
: fmt
::Error
) -> Self {
76 Self::msg(err
.to_string())
80 /// Deserializer for parts a part of a property string given a schema.
81 pub struct SchemaDeserializer
<'de
, 'i
> {
82 input
: Cow3
<'de
, 'i
, str>,
83 schema
: &'
static Schema
,
86 impl<'de
, 'i
> SchemaDeserializer
<'de
, 'i
> {
87 pub fn new_cow(input
: Cow3
<'de
, 'i
, str>, schema
: &'
static Schema
) -> Self {
88 Self { input, schema }
91 pub fn new
<T
>(input
: T
, schema
: &'
static Schema
) -> Self
93 T
: Into
<Cow
<'de
, str>>,
96 input
: Cow3
::from_original(input
.into()),
101 fn deserialize_str
<V
>(
104 schema
: &'
static schema
::StringSchema
,
105 ) -> Result
<V
::Value
, Error
>
109 if !IN_PROPERTY_STRING
.with(|v
| v
.get()) {
111 .check_constraints(&self.input
)
112 .map_err(Error
::invalid
)?
;
115 Cow3
::Original(input
) => visitor
.visit_borrowed_str(input
),
116 Cow3
::Intermediate(input
) => visitor
.visit_str(input
),
117 Cow3
::Owned(input
) => visitor
.visit_string(input
),
121 fn deserialize_property_string
<V
>(
124 schema
: &'
static Schema
,
125 ) -> Result
<V
::Value
, Error
>
130 Schema
::Object(schema
) => visitor
.visit_map(MapAccess
::new_cow(self.input
, schema
)),
131 Schema
::AllOf(schema
) => visitor
.visit_map(MapAccess
::new_cow(self.input
, schema
)),
133 "non-object-like schema in ApiStringFormat::PropertyString while deserializing a property string",
138 fn deserialize_array_string
<V
>(
141 schema
: &'
static Schema
,
142 ) -> Result
<V
::Value
, Error
>
147 Schema
::Array(schema
) => visitor
.visit_seq(SeqAccess
::new(self.input
, schema
)),
149 "non-array schema in ApiStringFormat::PropertyString while deserializing an array",
155 impl<'de
, 'i
> de
::Deserializer
<'de
> for SchemaDeserializer
<'de
, 'i
> {
158 fn deserialize_any
<V
>(self, visitor
: V
) -> Result
<V
::Value
, Error
>
163 Schema
::Array(schema
) => visitor
.visit_seq(SeqAccess
::new(self.input
, schema
)),
164 Schema
::AllOf(schema
) => visitor
.visit_map(MapAccess
::new_cow(self.input
, schema
)),
165 Schema
::Object(schema
) => visitor
.visit_map(MapAccess
::new_cow(self.input
, schema
)),
166 Schema
::Null
=> Err(Error
::msg("null")),
167 Schema
::Boolean(_
) => visitor
.visit_bool(
168 schema
::parse_boolean(&self.input
)
169 .map_err(|_
| Error
::msg(format
!("not a boolean: {:?}", self.input
)))?
,
171 Schema
::Integer(schema
) => {
172 // FIXME: isize vs explicit i64, needs fixing in schema check_constraints api
173 let value
: isize = self
176 .map_err(|_
| Error
::msg(format
!("not an integer: {:?}", self.input
)))?
;
178 schema
.check_constraints(value
).map_err(Error
::invalid
)?
;
180 let value
: i64 = i64::try_from(value
)
181 .map_err(|_
| Error
::invalid("isize did not fit into i64"))?
;
183 if let Ok(value
) = u64::try_from(value
) {
184 visitor
.visit_u64(value
)
186 visitor
.visit_i64(value
)
189 Schema
::Number(schema
) => {
190 let value
: f64 = self
193 .map_err(|_
| Error
::msg(format
!("not a valid number: {:?}", self.input
)))?
;
195 schema
.check_constraints(value
).map_err(Error
::invalid
)?
;
197 visitor
.visit_f64(value
)
199 Schema
::String(schema
) => {
200 // If not requested differently, strings stay strings, otherwise deserializing to a
201 // `Value` will get objects here instead of strings, which we do not expect
203 self.deserialize_str(visitor
, schema
)
208 fn deserialize_option
<V
>(self, visitor
: V
) -> Result
<V
::Value
, Error
>
212 if self.input
.is_empty() {
215 visitor
.visit_some(self)
219 fn deserialize_str
<V
>(self, visitor
: V
) -> Result
<V
::Value
, Error
>
224 Schema
::String(schema
) => self.deserialize_str(visitor
, schema
),
226 "tried to deserialize a string with a non-string-schema",
231 fn deserialize_string
<V
>(self, visitor
: V
) -> Result
<V
::Value
, Error
>
236 Schema
::String(schema
) => self.deserialize_str(visitor
, schema
),
238 "tried to deserialize a string with a non-string-schema",
243 fn deserialize_newtype_struct
<V
>(
247 ) -> Result
<V
::Value
, Error
>
251 visitor
.visit_newtype_struct(self)
254 fn deserialize_struct
<V
>(
257 _fields
: &'
static [&'
static str],
259 ) -> Result
<V
::Value
, Error
>
264 Schema
::Object(schema
) => visitor
.visit_map(MapAccess
::new_cow(self.input
, schema
)),
265 Schema
::AllOf(schema
) => visitor
.visit_map(MapAccess
::new_cow(self.input
, schema
)),
266 Schema
::String(schema
) => match schema
.format
{
267 Some(schema
::ApiStringFormat
::PropertyString(schema
)) => {
268 self.deserialize_property_string(visitor
, schema
)
270 _
=> Err(Error
::msg(format
!(
271 "cannot deserialize struct '{}' with a string schema",
275 _
=> Err(Error
::msg(format
!(
276 "cannot deserialize struct '{}' with non-object schema",
282 fn deserialize_map
<V
>(self, visitor
: V
) -> Result
<V
::Value
, Error
>
287 Schema
::Object(schema
) => visitor
.visit_map(MapAccess
::new_cow(self.input
, schema
)),
288 Schema
::AllOf(schema
) => visitor
.visit_map(MapAccess
::new_cow(self.input
, schema
)),
289 Schema
::String(schema
) => match schema
.format
{
290 Some(schema
::ApiStringFormat
::PropertyString(schema
)) => {
291 self.deserialize_property_string(visitor
, schema
)
293 _
=> Err(Error
::msg("cannot deserialize map with a string schema")),
295 _
=> Err(Error
::msg("cannot deserialize map with non-object schema")),
299 fn deserialize_seq
<V
>(self, visitor
: V
) -> Result
<V
::Value
, Error
>
304 Schema
::Array(schema
) => visitor
.visit_seq(SeqAccess
::new(self.input
, schema
)),
305 Schema
::String(schema
) => match schema
.format
{
306 Some(schema
::ApiStringFormat
::PropertyString(schema
)) => {
307 self.deserialize_array_string(visitor
, schema
)
309 _
=> Err(Error
::msg("cannot deserialize array with a string schema")),
312 "cannot deserialize array with non-object schema",
317 fn deserialize_enum
<V
>(
320 _variants
: &'
static [&'
static str],
322 ) -> Result
<V
::Value
, Error
>
327 Schema
::String(_
) => visitor
.visit_enum(self.input
.into_deserializer()),
328 _
=> Err(Error
::msg(format
!(
329 "cannot deserialize enum '{}' with non-string schema",
335 serde
::forward_to_deserialize_any
! {
349 fn next_str_entry(input
: &str, at
: &mut usize, has_null
: bool
) -> Option
<Range
<usize>> {
350 while *at
!= input
.len() {
353 let part
= &input
[*at
..];
355 let part_end
= if has_null
{
358 part
.find(|c
: char| c
== '
,'
|| c
== '
;'
|| char::is_ascii_whitespace(&c
))
361 let end
= match part_end
{
372 if input
[..end
].is_empty() {
376 return Some(begin
..end
);
382 /// Parse an array with a schema.
384 /// Provides both `SeqAccess` and `Deserializer` implementations.
385 pub struct SeqAccess
<'o
, 'i
, 's
> {
386 schema
: &'s ArraySchema
,
388 input
: Cow3
<'o
, 'i
, str>,
394 impl<'o
, 'i
, 's
> SeqAccess
<'o
, 'i
, 's
> {
395 pub fn new(input
: Cow3
<'o
, 'i
, str>, schema
: &'s ArraySchema
) -> Self {
398 was_empty
: input
.is_empty(),
399 has_null
: input
.contains('
\0'
),
407 impl<'de
, 'i
, 's
> de
::SeqAccess
<'de
> for SeqAccess
<'de
, 'i
, 's
> {
410 fn next_element_seed
<T
>(&mut self, seed
: T
) -> Result
<Option
<T
::Value
>, Error
>
412 T
: de
::DeserializeSeed
<'de
>,
418 while let Some(el_range
) = next_str_entry(&self.input
, &mut self.at
, self.has_null
) {
419 if el_range
.is_empty() {
423 if let Some(max
) = self.schema
.max_length
{
424 if self.count
== max
{
425 return Err(Error
::msg("too many elements"));
432 .deserialize(SchemaDeserializer
::new_cow(
433 self.input
.slice(el_range
),
439 if let Some(min
) = self.schema
.min_length
{
440 if self.count
< min
{
441 return Err(Error
::msg("not enough elements"));
449 impl<'de
, 'i
, 's
> de
::Deserializer
<'de
> for SeqAccess
<'de
, 'i
, 's
> {
452 fn deserialize_any
<V
>(self, visitor
: V
) -> Result
<V
::Value
, Error
>
456 visitor
.visit_seq(self)
459 fn deserialize_option
<V
>(self, visitor
: V
) -> Result
<V
::Value
, Error
>
466 visitor
.visit_some(self)
470 serde
::forward_to_deserialize_any
! {
471 i8 i16 i32 i64 u8 u16 u32 u64 f32 f64
479 identifier ignored_any
483 /// Provides serde's `MapAccess` for parsing a property string.
484 pub struct MapAccess
<'de
, 'i
> {
485 // The property string iterator and quoted string handler.
486 input
: Cow3
<'de
, 'i
, str>,
487 input_at
: usize, // for when using `Cow3::Owned`.
489 /// As a `Deserializer` we want to be able to handle `deserialize_option` and need to know
490 /// whether this was an empty string.
493 /// The schema used to verify the contents and distinguish between structs and property
495 schema
: &'
static dyn schema
::ObjectSchemaType
,
497 /// The current next value's key, value and schema (if available).
498 value
: Option
<(Cow
<'de
, str>, Cow
<'de
, str>, Option
<&'
static Schema
>)>,
501 impl<'de
, 'i
> MapAccess
<'de
, 'i
> {
502 pub fn new
<S
: schema
::ObjectSchemaType
>(input
: &'de
str, schema
: &'
static S
) -> Self {
504 was_empty
: input
.is_empty(),
505 input
: Cow3
::Original(input
),
512 pub fn new_cow
<S
: schema
::ObjectSchemaType
>(
513 input
: Cow3
<'de
, 'i
, str>,
517 was_empty
: input
.is_empty(),
525 pub fn new_intermediate
<S
: schema
::ObjectSchemaType
>(
530 was_empty
: input
.is_empty(),
531 input
: Cow3
::Intermediate(input
),
539 impl<'de
, 'i
> de
::MapAccess
<'de
> for MapAccess
<'de
, 'i
> {
542 fn next_key_seed
<K
>(&mut self, seed
: K
) -> Result
<Option
<K
::Value
>, Error
>
544 K
: de
::DeserializeSeed
<'de
>,
546 use crate::property_string
::next_property
;
553 let (key
, value
, rem
) = match next_property(&self.input
[self.input_at
..]) {
554 None
=> return Ok(None
),
555 Some(entry
) => entry?
,
559 self.input_at
= self.input
.len();
561 let ofs
= unsafe { rem.as_ptr().offset_from(self.input.as_ptr()) }
;
562 if ofs
< 0 || (ofs
as usize) > self.input
.len() {
563 // 'rem' is either an empty string (rem.is_empty() is true), or a valid offset into
564 // the input string...
565 panic
!("unexpected remainder in next_property");
567 self.input_at
= ofs
as usize;
570 let value
= match value
{
571 Cow
::Owned(value
) => Cow
::Owned(value
),
572 Cow
::Borrowed(value
) => match str_slice_to_range(&self.input
, value
) {
573 None
=> Cow
::Owned(value
.to_string()),
574 Some(range
) => match &self.input
{
575 Cow3
::Original(orig
) => Cow
::Borrowed(&orig
[range
]),
576 _
=> Cow
::Owned(value
.to_string()),
581 let (key
, schema
) = match key
{
583 let schema
= self.schema
.lookup(key
);
584 let key
= match str_slice_to_range(&self.input
, key
) {
585 None
=> Cow
::Owned(key
.to_string()),
586 Some(range
) => match &self.input
{
587 Cow3
::Original(orig
) => Cow
::Borrowed(&orig
[range
]),
588 _
=> Cow
::Owned(key
.to_string()),
593 None
=> match self.schema
.default_key() {
598 .ok_or(Error
::msg("bad default key"))?
;
599 (Cow
::Borrowed(key
), Some(schema
))
602 return Err(Error
::msg(
603 "value without key, but schema does not define a default key",
608 let schema
= schema
.map(|(_optional
, schema
)| schema
);
610 let out
= match &key
{
611 Cow
::Borrowed(key
) => {
612 seed
.deserialize(de
::value
::BorrowedStrDeserializer
::<'de
, Error
>::new(key
))?
615 seed
.deserialize(IntoDeserializer
::<Error
>::into_deserializer(key
.as_str()))?
619 self.value
= Some((key
, value
, schema
));
624 fn next_value_seed
<V
>(&mut self, seed
: V
) -> Result
<V
::Value
, Error
>
626 V
: de
::DeserializeSeed
<'de
>,
628 let (key
, input
, schema
) = self.value
.take().ok_or(Error
::msg("bad map access"))?
;
630 if let Some(schema
) = schema
{
631 seed
.deserialize(SchemaDeserializer
::new(input
, schema
))
633 if !verify
::is_verifying() && !self.schema
.additional_properties() {
634 return Err(Error
::msg(format
!("unknown key {:?}", key
.as_ref())));
637 // additional properties are treated as strings...
638 let deserializer
= no_schema
::NoSchemaDeserializer
::new(input
);
639 seed
.deserialize(deserializer
)