Skip to content

Commit

Permalink
deer: implement Deserialize for core::option (#2383)
Browse files Browse the repository at this point in the history
* chore: import from #1875

* chore: docs

* test: `Option`
  • Loading branch information
indietyp authored Apr 13, 2023
1 parent f66d077 commit cb6d9f7
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 0 deletions.
1 change: 1 addition & 0 deletions libs/deer/src/impls/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ mod floating;
mod integral;
mod mem;
mod non_zero;
mod option;
mod string;
mod unit;
56 changes: 56 additions & 0 deletions libs/deer/src/impls/core/option.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
use core::marker::PhantomData;

use error_stack::{Result, ResultExt};

use crate::{
error::{DeserializeError, VisitorError},
Deserialize, Deserializer, Document, OptionalVisitor, Reflection, Schema,
};

struct OptionVisitor<T>(PhantomData<fn() -> *const T>);

impl<'de, T: Deserialize<'de>> OptionalVisitor<'de> for OptionVisitor<T> {
type Value = Option<T>;

fn expecting(&self) -> Document {
Self::Value::reflection()
}

fn visit_none(self) -> Result<Self::Value, VisitorError> {
Ok(None)
}

fn visit_null(self) -> Result<Self::Value, VisitorError> {
Ok(None)
}

fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, VisitorError>
where
D: Deserializer<'de>,
{
T::deserialize(deserializer)
.change_context(VisitorError)
.map(Some)
}
}

pub struct OptionReflection<T: ?Sized>(PhantomData<fn() -> *const T>);

impl<T: Reflection + ?Sized> Reflection for OptionReflection<T> {
fn schema(doc: &mut Document) -> Schema {
// TODO: an accurate reflection is not really possible right now
// we need to do oneOf null/none/T and `Schema` does not support it right now
// this needs to be fixed until `0.1`
// For now we just fallback to `T`
T::schema(doc)
}
}

impl<'de, T: Deserialize<'de>> Deserialize<'de> for Option<T> {
type Reflection = OptionReflection<T::Reflection>;

fn deserialize<D: Deserializer<'de>>(de: D) -> Result<Self, DeserializeError> {
de.deserialize_optional(OptionVisitor(PhantomData))
.change_context(DeserializeError)
}
}
15 changes: 15 additions & 0 deletions libs/deer/tests/test_impls_core_option.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use deer_desert::{assert_tokens, Token};
use proptest::prelude::*;

#[cfg(not(miri))]
proptest! {
#[test]
fn option_some_ok(value in any::<u8>()) {
assert_tokens(&Some(value), &[Token::Number(value.into())]);
}
}

#[test]
fn option_none_ok() {
assert_tokens(&None::<u8>, &[Token::Null]);
}

0 comments on commit cb6d9f7

Please sign in to comment.