Skip to content

Commit

Permalink
feat: Bound<T>
Browse files Browse the repository at this point in the history
  • Loading branch information
indietyp committed Mar 22, 2023
1 parent 8c1a08b commit 77f680d
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 41 deletions.
119 changes: 99 additions & 20 deletions libs/deer/src/impls/core/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,30 @@
//!
//! This follows exactly has serde implements `Range`, the only range is:
//! * `RangeFrom`, `RangeTo`, `RangeFull` are supported
//! * `Bounds::Unbounded` = `{"Unbounded": null}` instead of `"Unbounded"`
//!
//! Why is `Bounds::Unbounded` different? This ensures that everything is always consistent with
//! each other, this also means that we only need to invoke `visit_object`.
//!
//! Ideally we'd want to move away from serde to better represent each range, instead of `{start,
//! end}` do `{start: Bound, end: Bound}`. For the sake of compatability as `deer` has no serialize
//! equivalent this uses the same as `serde`.
// TODO: deserialize as string as a special container instruction? or automatic?
// TODO: do we want a special visitor just for enums, e.g. EnumVisitor? that can only be supplied to
// visit_enum?

use core::{marker::PhantomData, ops::Bound};

use error_stack::{Report, Result, ResultExt};
use error_stack::{FutureExt, IntoReport, Report, Result, ResultExt};

use crate::{
error::{
DeserializeError, ExpectedLength, FieldAccessError, ObjectLengthError, ReceivedLength,
Variant, VisitorError,
DeserializeError, ExpectedLength, ExpectedVariant, FieldAccessError, ObjectLengthError,
ReceivedLength, ReceivedVariant, UnknownVariantError, Variant, VisitorError,
},
Deserialize, Deserializer, Document, FieldAccess, ObjectAccess, Visitor,
impls::helpers::{FieldDiscriminatorKey, FieldDiscriminatorKeyAccess},
schema::Reference,
Deserialize, Deserializer, Document, FieldAccess, ObjectAccess, Reflection, Schema, Visitor,
};

struct BoundFieldAccess<T>(PhantomData<fn() -> *const T>);
Expand All @@ -33,8 +42,9 @@ where
D: Deserializer<'de>,
{
match key {
BoundField::Excluded => T::deserialize(deserializer).map(BoundField::Excluded),
BoundField::Included => T::deserialize(deserializer).map(BoundField::Included),
BoundField::Excluded => T::deserialize(deserializer).map(Bound::Excluded),
BoundField::Included => T::deserialize(deserializer).map(Bound::Included),
BoundField::Unbounded => <()>::deserialize(deserializer).map(|_| Bound::Unbounded),
}
.change_context(FieldAccessError)
}
Expand All @@ -43,8 +53,44 @@ where
enum BoundField {
Excluded,
Included,
// Unbounded is a str
// Unbounded,
Unbounded,
}

impl Reflection for BoundField {
fn schema(_: &mut Document) -> Schema {
Schema::new("string").with("enum", ["Included", "Excluded", "Unbounded"])
}
}

impl FieldDiscriminatorKey for BoundField {
const VARIANTS: &'static [&'static str] = &["Included", "Excluded", "Unbounded"];

fn try_str(value: &str) -> Option<Self> {
match value {
"Included" => Some(BoundField::Included),
"Excluded" => Some(BoundField::Excluded),
"Unbounded" => Some(BoundField::Unbounded),
_ => None,
}
}

fn try_bytes(value: &[u8]) -> Option<Self> {
match value {
b"Included" => Some(BoundField::Included),
b"Excluded" => Some(BoundField::Excluded),
b"Unbounded" => Some(BoundField::Unbounded),
_ => None,
}
}
}

impl<'de> Deserialize<'de> for BoundField {
type Reflection = Self;

fn deserialize<D: Deserializer<'de>>(de: D) -> Result<Self, DeserializeError> {
de.deserialize_str(FieldDiscriminatorKeyAccess::new())
.change_context(DeserializeError)
}
}

struct BoundVisitor<T>(PhantomData<fn() -> *const T>);
Expand All @@ -59,14 +105,6 @@ where
Self::Value::reflection()
}

fn visit_str(self, v: &str) -> Result<Self::Value, VisitorError> {
todo!()
}

fn visit_bytes(self, v: &[u8]) -> Result<Self::Value, VisitorError> {
todo!()
}

fn visit_object<U>(self, mut v: U) -> Result<Self::Value, VisitorError>
where
U: ObjectAccess<'de>,
Expand All @@ -82,14 +120,55 @@ where

v.end().change_context(VisitorError)?;

todo!()
field.map(|(_, value)| value).change_context(VisitorError)
}
}

struct BoundReflection<T: ?Sized>(PhantomData<*const T>);

impl<T> Reflection for BoundReflection<T>
where
T: Reflection + ?Sized,
{
/// # Schema
///
/// ```json
/// {
/// "type": "object",
/// "additionalProperties": false,
/// "oneOf": [
/// {"properties": {"Included": <ref>}}
/// {"properties": {"Excluded": <ref>}}
/// {"properties": {"Unbounded": <ref>}}
/// ]
/// }
/// ```
fn schema(doc: &mut Document) -> Schema {
#[derive(serde::Serialize)]
enum Properties {
Included(Reference),
Excluded(Reference),
Unbounded(Reference),
}

Schema::new("object")
.with("oneOf", [
Properties::Included(doc.add::<T>()),
Properties::Excluded(doc.add::<T>()),
Properties::Unbounded(doc.add::<()>()),
])
.with("additionalProperties", false)
}
}

impl<'de, T: 'de> Deserialize<'de> for Bound<T> {
type Reflection = ();
impl<'de, T> Deserialize<'de> for Bound<T>
where
T: Deserialize<'de>,
{
type Reflection = BoundReflection<T::Reflection>;

fn deserialize<D: Deserializer<'de>>(de: D) -> Result<Self, DeserializeError> {
todo!()
de.deserialize_object(BoundVisitor(PhantomData))
.change_context(DeserializeError)
}
}
19 changes: 8 additions & 11 deletions libs/deer/src/impls/core/result.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use alloc::string::String;
use core::marker::PhantomData;

use error_stack::{Context, FutureExt, IntoReport, Report, ResultExt};
use error_stack::{FutureExt, IntoReport, Report, Result, ResultExt};

use crate::{
error::{
Expand All @@ -24,11 +23,7 @@ where
type Key = ResultField;
type Value = Result<T, E>;

fn value<D>(
&self,
key: &Self::Key,
deserializer: D,
) -> error_stack::Result<Self::Value, FieldAccessError>
fn value<D>(&self, key: &Self::Key, deserializer: D) -> Result<Self::Value, FieldAccessError>
where
D: Deserializer<'de>,
{
Expand All @@ -52,6 +47,8 @@ impl Reflection for ResultField {
}

impl FieldDiscriminatorKey for ResultField {
const VARIANTS: &'static [&'static str] = &["Ok", "Err"];

fn try_str(value: &str) -> Option<Self> {
match value {
"Ok" => Some(ResultField::Ok),
Expand All @@ -72,7 +69,7 @@ impl FieldDiscriminatorKey for ResultField {
impl<'de> Deserialize<'de> for ResultField {
type Reflection = Self;

fn deserialize<D: Deserializer<'de>>(de: D) -> error_stack::Result<Self, DeserializeError> {
fn deserialize<D: Deserializer<'de>>(de: D) -> Result<Self, DeserializeError> {
de.deserialize_str(FieldDiscriminatorKeyAccess::new())
.change_context(DeserializeError)
}
Expand All @@ -84,10 +81,10 @@ impl<'de, T: Deserialize<'de>, E: Deserialize<'de>> Visitor<'de> for ResultVisit
type Value = Result<T, E>;

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

fn visit_object<O>(self, mut v: O) -> error_stack::Result<Self::Value, VisitorError>
fn visit_object<O>(self, mut v: O) -> Result<Self::Value, VisitorError>
where
O: ObjectAccess<'de>,
{
Expand Down Expand Up @@ -144,7 +141,7 @@ where
impl<'de, T: Deserialize<'de>, E: Deserialize<'de>> Deserialize<'de> for Result<T, E> {
type Reflection = ResultReflection<T::Reflection, E::Reflection>;

fn deserialize<D: Deserializer<'de>>(de: D) -> error_stack::Result<Self, DeserializeError> {
fn deserialize<D: Deserializer<'de>>(de: D) -> Result<Self, DeserializeError> {
de.deserialize_object(ResultVisitor(PhantomData))
.change_context(DeserializeError)
}
Expand Down
30 changes: 20 additions & 10 deletions libs/deer/src/impls/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ use crate::{
};

pub(super) trait FieldDiscriminatorKey: Reflection {
const VARIANTS: &'static [&'static str];

fn try_str(value: &str) -> Option<Self>;
fn try_bytes(value: &[u8]) -> Option<Self>;
}
Expand All @@ -22,13 +24,23 @@ impl<T> FieldDiscriminatorKeyAccess<T> {
}
}

impl<T> FieldDiscriminatorKeyAccess<T> {
impl<T> FieldDiscriminatorKeyAccess<T>
where
T: FieldDiscriminatorKey,
{
fn attach_expected_variants<C>(mut error: Report<C>) -> Report<C> {
for variant in T::VARIANTS {
error = error.attach(ExpectedVariant::new(*variant));
}

error
}

fn unknown_variant_error(name: impl Into<String>) -> Report<impl Context> {
Report::new(UnknownVariantError.into_error())
.attach(ReceivedVariant::new(name))
.attach(ExpectedVariant::new("Ok"))
.attach(ExpectedVariant::new("Err"))
.change_context(VisitorError)
let error =
Report::new(UnknownVariantError.into_error()).attach(ReceivedVariant::new(name));

Self::attach_expected_variants(error).change_context(VisitorError)
}
}

Expand All @@ -51,13 +63,11 @@ where
let value = core::str::from_utf8(v)
.into_report()
.change_context(UnknownVariantError.into_error())
.attach(ExpectedVariant::new("Ok"))
.attach(ExpectedVariant::new("Err"))
.change_context(VisitorError);
.map_err(Self::attach_expected_variants);

match value {
Ok(name) => Self::unknown_variant_error(name),
Err(err) => err,
Err(error) => error.change_context(VisitorError),
}
})
}
Expand Down

0 comments on commit 77f680d

Please sign in to comment.