Skip to content

Commit cb6d9f7

Browse files
authored
deer: implement Deserialize for core::option (#2383)
* chore: import from #1875 * chore: docs * test: `Option`
1 parent f66d077 commit cb6d9f7

File tree

3 files changed

+72
-0
lines changed

3 files changed

+72
-0
lines changed

libs/deer/src/impls/core.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ mod floating;
66
mod integral;
77
mod mem;
88
mod non_zero;
9+
mod option;
910
mod string;
1011
mod unit;

libs/deer/src/impls/core/option.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
use core::marker::PhantomData;
2+
3+
use error_stack::{Result, ResultExt};
4+
5+
use crate::{
6+
error::{DeserializeError, VisitorError},
7+
Deserialize, Deserializer, Document, OptionalVisitor, Reflection, Schema,
8+
};
9+
10+
struct OptionVisitor<T>(PhantomData<fn() -> *const T>);
11+
12+
impl<'de, T: Deserialize<'de>> OptionalVisitor<'de> for OptionVisitor<T> {
13+
type Value = Option<T>;
14+
15+
fn expecting(&self) -> Document {
16+
Self::Value::reflection()
17+
}
18+
19+
fn visit_none(self) -> Result<Self::Value, VisitorError> {
20+
Ok(None)
21+
}
22+
23+
fn visit_null(self) -> Result<Self::Value, VisitorError> {
24+
Ok(None)
25+
}
26+
27+
fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, VisitorError>
28+
where
29+
D: Deserializer<'de>,
30+
{
31+
T::deserialize(deserializer)
32+
.change_context(VisitorError)
33+
.map(Some)
34+
}
35+
}
36+
37+
pub struct OptionReflection<T: ?Sized>(PhantomData<fn() -> *const T>);
38+
39+
impl<T: Reflection + ?Sized> Reflection for OptionReflection<T> {
40+
fn schema(doc: &mut Document) -> Schema {
41+
// TODO: an accurate reflection is not really possible right now
42+
// we need to do oneOf null/none/T and `Schema` does not support it right now
43+
// this needs to be fixed until `0.1`
44+
// For now we just fallback to `T`
45+
T::schema(doc)
46+
}
47+
}
48+
49+
impl<'de, T: Deserialize<'de>> Deserialize<'de> for Option<T> {
50+
type Reflection = OptionReflection<T::Reflection>;
51+
52+
fn deserialize<D: Deserializer<'de>>(de: D) -> Result<Self, DeserializeError> {
53+
de.deserialize_optional(OptionVisitor(PhantomData))
54+
.change_context(DeserializeError)
55+
}
56+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
use deer_desert::{assert_tokens, Token};
2+
use proptest::prelude::*;
3+
4+
#[cfg(not(miri))]
5+
proptest! {
6+
#[test]
7+
fn option_some_ok(value in any::<u8>()) {
8+
assert_tokens(&Some(value), &[Token::Number(value.into())]);
9+
}
10+
}
11+
12+
#[test]
13+
fn option_none_ok() {
14+
assert_tokens(&None::<u8>, &[Token::Null]);
15+
}

0 commit comments

Comments
 (0)