Skip to content

Commit 3e4febe

Browse files
committed
WIP
WIP
1 parent 25d1fdd commit 3e4febe

8 files changed

Lines changed: 653 additions & 178 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/maa-value/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ maa-str-ext = { workspace = true }
1414
maa-value-macro = { workspace = true }
1515
schemars = { workspace = true, optional = true }
1616
serde = { workspace = true, features = ["derive"] }
17+
thiserror = { workspace = true }
1718

1819
[dev-dependencies]
1920
serde_json = { workspace = true }

crates/maa-value/src/de.rs

Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
//! Deserializer implementation for ResolvedMAAValue
2+
//!
3+
//! This allows direct conversion from ResolvedMAAValue to any type implementing Deserialize,
4+
//! without going through an intermediate format like serde_json::Value.
5+
//!
6+
//! # Example
7+
//!
8+
//! ```
9+
//! use maa_value::prelude::*;
10+
//! use serde::Deserialize;
11+
//!
12+
//! #[derive(Deserialize, Debug, PartialEq)]
13+
//! struct Config {
14+
//! name: String,
15+
//! count: i32,
16+
//! }
17+
//!
18+
//! let resolved = object!("name" => "app", "count" => 42).resolve().unwrap();
19+
//!
20+
//! // Direct conversion - no intermediate JSON!
21+
//! let config = Config::deserialize(resolved).unwrap();
22+
//!
23+
//! assert_eq!(config.name, "app");
24+
//! assert_eq!(config.count, 42);
25+
//! ```
26+
27+
use serde::de::{self, Deserializer, MapAccess, SeqAccess, Visitor};
28+
29+
use crate::{primitive::MAAPrimitive, value::ResolvedMAAValue};
30+
31+
#[derive(Debug, thiserror::Error)]
32+
#[error("{0}")]
33+
pub struct Error(String);
34+
35+
type Result<T> = std::result::Result<T, Error>;
36+
37+
impl serde::de::Error for Error {
38+
fn custom<T: std::fmt::Display>(msg: T) -> Self {
39+
Error(msg.to_string())
40+
}
41+
}
42+
43+
impl<'de> Deserializer<'de> for ResolvedMAAValue {
44+
type Error = Error;
45+
46+
// Forward all other deserialize_* methods to deserialize_any
47+
serde::forward_to_deserialize_any! {
48+
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
49+
bytes byte_buf unit unit_struct newtype_struct seq tuple
50+
tuple_struct map identifier ignored_any
51+
}
52+
53+
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
54+
where
55+
V: Visitor<'de>,
56+
{
57+
match self {
58+
ResolvedMAAValue::Primitive(p) => deserialize_primitive(p, visitor),
59+
ResolvedMAAValue::Array(arr) => visitor.visit_seq(SeqDeserializer {
60+
iter: arr.into_iter(),
61+
}),
62+
ResolvedMAAValue::Object(map) => visitor.visit_map(MapDeserializer {
63+
iter: map.into_iter(),
64+
value: None,
65+
}),
66+
}
67+
}
68+
69+
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value>
70+
where
71+
V: Visitor<'de>,
72+
{
73+
visitor.visit_some(self)
74+
}
75+
76+
fn deserialize_struct<V>(
77+
self,
78+
_name: &'static str,
79+
_fields: &'static [&'static str],
80+
visitor: V,
81+
) -> Result<V::Value>
82+
where
83+
V: Visitor<'de>,
84+
{
85+
match self {
86+
ResolvedMAAValue::Object(map) => visitor.visit_map(MapDeserializer {
87+
iter: map.into_iter(),
88+
value: None,
89+
}),
90+
_ => Err(de::Error::custom("expected struct (object)")),
91+
}
92+
}
93+
94+
fn deserialize_enum<V>(
95+
self,
96+
_name: &'static str,
97+
_variants: &'static [&'static str],
98+
_visitor: V,
99+
) -> Result<V::Value>
100+
where
101+
V: Visitor<'de>,
102+
{
103+
// Enums are not directly supported in ResolvedMAAValue
104+
Err(de::Error::custom("enums are not supported"))
105+
}
106+
}
107+
108+
fn deserialize_primitive<'de, V>(p: MAAPrimitive, visitor: V) -> Result<V::Value>
109+
where
110+
V: Visitor<'de>,
111+
{
112+
match p {
113+
MAAPrimitive::Bool(b) => visitor.visit_bool(b),
114+
MAAPrimitive::Int(i) => visitor.visit_i32(i),
115+
MAAPrimitive::Float(f) => visitor.visit_f32(f),
116+
MAAPrimitive::String(s) => visitor.visit_string(s),
117+
}
118+
}
119+
120+
struct SeqDeserializer {
121+
iter: std::vec::IntoIter<ResolvedMAAValue>,
122+
}
123+
124+
impl<'de> SeqAccess<'de> for SeqDeserializer {
125+
type Error = Error;
126+
127+
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
128+
where
129+
T: de::DeserializeSeed<'de>,
130+
{
131+
match self.iter.next() {
132+
Some(value) => seed.deserialize(value).map(Some),
133+
None => Ok(None),
134+
}
135+
}
136+
137+
fn size_hint(&self) -> Option<usize> {
138+
Some(self.iter.len())
139+
}
140+
}
141+
142+
struct MapDeserializer {
143+
iter: std::collections::btree_map::IntoIter<String, ResolvedMAAValue>,
144+
value: Option<ResolvedMAAValue>,
145+
}
146+
147+
impl<'de> MapAccess<'de> for MapDeserializer {
148+
type Error = Error;
149+
150+
fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>>
151+
where
152+
K: de::DeserializeSeed<'de>,
153+
{
154+
match self.iter.next() {
155+
Some((key, value)) => {
156+
self.value = Some(value);
157+
seed.deserialize(KeyDeserializer { key }).map(Some)
158+
}
159+
None => Ok(None),
160+
}
161+
}
162+
163+
fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value>
164+
where
165+
V: de::DeserializeSeed<'de>,
166+
{
167+
match self.value.take() {
168+
Some(value) => seed.deserialize(value),
169+
None => Err(de::Error::custom("value is missing")),
170+
}
171+
}
172+
173+
fn size_hint(&self) -> Option<usize> {
174+
Some(self.iter.len())
175+
}
176+
}
177+
178+
struct KeyDeserializer {
179+
key: String,
180+
}
181+
182+
impl<'de> Deserializer<'de> for KeyDeserializer {
183+
type Error = Error;
184+
185+
serde::forward_to_deserialize_any! {
186+
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
187+
bytes byte_buf option unit unit_struct newtype_struct seq tuple
188+
tuple_struct map struct enum identifier ignored_any
189+
}
190+
191+
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
192+
where
193+
V: Visitor<'de>,
194+
{
195+
visitor.visit_string(self.key)
196+
}
197+
}
198+
199+
#[cfg(test)]
200+
mod tests {
201+
use serde::Deserialize;
202+
203+
use crate::prelude::*;
204+
205+
#[test]
206+
fn deserialize_struct() {
207+
#[derive(Deserialize, Debug, PartialEq)]
208+
struct Config {
209+
name: String,
210+
count: i32,
211+
enabled: bool,
212+
}
213+
214+
let resolved = object!(
215+
"name" => "my-app",
216+
"count" => 42,
217+
"enabled" => true
218+
)
219+
.resolve()
220+
.unwrap();
221+
222+
// Direct conversion!
223+
let config = Config::deserialize(resolved).unwrap();
224+
225+
assert_eq!(config.name, "my-app");
226+
assert_eq!(config.count, 42);
227+
assert!(config.enabled);
228+
}
229+
230+
#[test]
231+
fn deserialize_nested_struct() {
232+
#[derive(Deserialize, Debug, PartialEq)]
233+
struct Inner {
234+
value: String,
235+
}
236+
237+
#[derive(Deserialize, Debug, PartialEq)]
238+
struct Outer {
239+
name: String,
240+
inner: Inner,
241+
}
242+
243+
let resolved = object!(
244+
"name" => "outer",
245+
"inner" => object!("value" => "inner_value")
246+
)
247+
.resolve()
248+
.unwrap();
249+
250+
let config = Outer::deserialize(resolved).unwrap();
251+
252+
assert_eq!(config.name, "outer");
253+
assert_eq!(config.inner.value, "inner_value");
254+
}
255+
256+
#[test]
257+
fn deserialize_array() {
258+
#[derive(Deserialize, Debug, PartialEq)]
259+
struct Config {
260+
items: Vec<i32>,
261+
}
262+
263+
let resolved = object!("items" => [1, 2, 3, 4, 5]).resolve().unwrap();
264+
265+
let config = Config::deserialize(resolved).unwrap();
266+
267+
assert_eq!(config.items, vec![1, 2, 3, 4, 5]);
268+
}
269+
270+
#[test]
271+
fn deserialize_optional_fields() {
272+
#[derive(Deserialize, Debug, PartialEq)]
273+
struct Config {
274+
required: String,
275+
optional: Option<i32>,
276+
}
277+
278+
let resolved = object!("required" => "value").resolve().unwrap();
279+
280+
let config = Config::deserialize(resolved).unwrap();
281+
282+
assert_eq!(config.required, "value");
283+
assert_eq!(config.optional, None);
284+
}
285+
286+
#[test]
287+
fn compare_with_json_approach() {
288+
#[derive(Deserialize, Debug, PartialEq)]
289+
struct Config {
290+
name: String,
291+
count: i32,
292+
}
293+
294+
let resolved = object!("name" => "app", "count" => 100).resolve().unwrap();
295+
296+
// Old way: via JSON
297+
let json = serde_json::to_value(&resolved).unwrap();
298+
let config1: Config = serde_json::from_value(json).unwrap();
299+
300+
// New way: direct
301+
let config2 = Config::deserialize(resolved.clone()).unwrap();
302+
303+
assert_eq!(config1, config2);
304+
}
305+
}

0 commit comments

Comments
 (0)