Skip to content

Commit cee1a1b

Browse files
authored
deer: implement Deserialize for core::ops (#2422)
* feat: init * feat: `FieldVisitor` for Bound * fix: attach `Location` information * feat: schema for `Bound` * test: option error location * fix: option test * feat: implement `Bound` * feat: do not use `Option<()>` as implementation detail * feat: start implementation of `Range` * docs: explain identifier macro * feat: `Range` and `RangeInclusive` * feat: implement ops * test: `core::ops` * chore: clippy * fix: identifier example * chore: remove `//` for formatting * fix: readability * chore: re-report alloc
1 parent bf1caee commit cee1a1b

File tree

12 files changed

+932
-27
lines changed

12 files changed

+932
-27
lines changed

libs/deer/src/error.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ use core::{
6363
fmt::{self, Debug, Display, Formatter},
6464
};
6565

66+
pub use duplicate::{DuplicateField, DuplicateFieldError, DuplicateKey, DuplicateKeyError};
6667
use error_stack::{Context, Frame, IntoReport, Report, Result};
6768
pub use extra::{
6869
ArrayLengthError, ExpectedLength, ObjectItemsExtraError, ObjectLengthError, ReceivedKey,
@@ -80,6 +81,7 @@ pub use value::{MissingError, ReceivedValue, ValueError};
8081

8182
use crate::error::serialize::{impl_serialize, Export};
8283

84+
mod duplicate;
8385
mod extra;
8486
mod internal;
8587
mod location;
@@ -469,3 +471,18 @@ impl<C: Context> ReportExt<C> for Report<C> {
469471
Export::new(self)
470472
}
471473
}
474+
475+
pub(crate) trait ResultExtPrivate<C: Context> {
476+
fn extend_one(&mut self, error: Report<C>);
477+
}
478+
479+
impl<T, C: Context> ResultExtPrivate<C> for Result<T, C> {
480+
fn extend_one(&mut self, error: Report<C>) {
481+
match self {
482+
Err(errors) => {
483+
errors.extend_one(error);
484+
}
485+
errors => *errors = Err(error),
486+
}
487+
}
488+
}

libs/deer/src/error/duplicate.rs

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
use alloc::string::String;
2+
use core::{
3+
fmt,
4+
fmt::{Display, Formatter},
5+
};
6+
7+
use crate::{
8+
error::{ErrorProperties, ErrorProperty, Id, Location, Namespace, Variant, NAMESPACE},
9+
id,
10+
};
11+
12+
pub struct DuplicateField(&'static str);
13+
14+
impl DuplicateField {
15+
#[must_use]
16+
pub const fn new(name: &'static str) -> Self {
17+
Self(name)
18+
}
19+
}
20+
21+
impl ErrorProperty for DuplicateField {
22+
type Value<'a> = Option<&'static str> where Self: 'a;
23+
24+
fn key() -> &'static str {
25+
"field"
26+
}
27+
28+
fn value<'a>(mut stack: impl Iterator<Item = &'a Self>) -> Self::Value<'a> {
29+
stack.next().map(|field| field.0)
30+
}
31+
}
32+
33+
#[derive(Debug)]
34+
pub struct DuplicateFieldError;
35+
36+
impl Variant for DuplicateFieldError {
37+
type Properties = (Location, DuplicateField);
38+
39+
const ID: Id = id!["duplicate", "field"];
40+
const NAMESPACE: Namespace = NAMESPACE;
41+
42+
fn message(
43+
&self,
44+
fmt: &mut Formatter,
45+
properties: &<Self::Properties as ErrorProperties>::Value<'_>,
46+
) -> fmt::Result {
47+
let (_, field) = properties;
48+
49+
if let Some(field) = field {
50+
write!(fmt, "duplicate field `{field}`")
51+
} else {
52+
Display::fmt(self, fmt)
53+
}
54+
}
55+
}
56+
57+
impl Display for DuplicateFieldError {
58+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
59+
f.write_str("duplicate field")
60+
}
61+
}
62+
63+
pub struct DuplicateKey(String);
64+
65+
impl DuplicateKey {
66+
pub fn new(key: impl Into<String>) -> Self {
67+
Self(key.into())
68+
}
69+
}
70+
71+
impl ErrorProperty for DuplicateKey {
72+
type Value<'a> = Option<&'a str> where Self: 'a;
73+
74+
fn key() -> &'static str {
75+
"key"
76+
}
77+
78+
fn value<'a>(mut stack: impl Iterator<Item = &'a Self>) -> Self::Value<'a> {
79+
stack.next().map(|key| key.0.as_str())
80+
}
81+
}
82+
83+
#[derive(Debug)]
84+
pub struct DuplicateKeyError;
85+
86+
impl Variant for DuplicateKeyError {
87+
type Properties = (Location, DuplicateKey);
88+
89+
const ID: Id = id!["duplicate", "key"];
90+
const NAMESPACE: Namespace = NAMESPACE;
91+
92+
fn message(
93+
&self,
94+
fmt: &mut Formatter,
95+
properties: &<Self::Properties as ErrorProperties>::Value<'_>,
96+
) -> fmt::Result {
97+
let (_, key) = properties;
98+
99+
if let Some(key) = key {
100+
write!(fmt, "duplicate key `{key}`")
101+
} else {
102+
Display::fmt(self, fmt)
103+
}
104+
}
105+
}
106+
107+
impl Display for DuplicateKeyError {
108+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
109+
f.write_str("duplicate key")
110+
}
111+
}
112+
113+
// TODO: unit test

libs/deer/src/helpers.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
use error_stack::{Result, ResultExt};
2+
use serde::{ser::SerializeMap, Serialize, Serializer};
23

34
use crate::{
45
error::{DeserializeError, VisitorError},
56
ext::TupleExt,
7+
schema::Reference,
68
Deserialize, Deserializer, Document, EnumVisitor, FieldVisitor, ObjectAccess, Reflection,
79
Schema, Visitor,
810
};
@@ -123,3 +125,20 @@ impl<'de> Deserialize<'de> for ExpectNone {
123125
}
124126

125127
// TODO: consider adding an error attachment marker type for "short-circuit"
128+
129+
pub struct Properties<const N: usize>(pub [(&'static str, Reference); N]);
130+
131+
impl<const N: usize> Serialize for Properties<N> {
132+
fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
133+
where
134+
S: Serializer,
135+
{
136+
let mut map = serializer.serialize_map(Some(self.0.len()))?;
137+
138+
for (key, value) in self.0 {
139+
map.serialize_entry(key, &value)?;
140+
}
141+
142+
map.end()
143+
}
144+
}

libs/deer/src/impls.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,26 @@
1+
use error_stack::Result;
2+
3+
use crate::{error::VisitorError, Deserialize, Document, OptionalVisitor};
4+
15
mod core;
6+
7+
pub(crate) struct UnitVariantVisitor;
8+
9+
impl<'de> OptionalVisitor<'de> for UnitVariantVisitor {
10+
type Value = ();
11+
12+
fn expecting(&self) -> Document {
13+
// TODO: in theory also none, cannot be expressed with current schema
14+
<() as Deserialize>::reflection()
15+
}
16+
17+
fn visit_none(self) -> Result<Self::Value, VisitorError> {
18+
Ok(())
19+
}
20+
21+
fn visit_null(self) -> Result<Self::Value, VisitorError> {
22+
Ok(())
23+
}
24+
25+
// we do not implement `visit_some` because we do not allow for some values
26+
}

libs/deer/src/impls/core.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ mod cmp;
66
mod marker;
77
mod mem;
88
mod num;
9+
mod ops;
910
mod option;
1011
mod result;
1112
mod string;

0 commit comments

Comments
 (0)