Skip to content

Commit 98bcbed

Browse files
committed
feat: deserialize lists into fields
1 parent e7e4011 commit 98bcbed

1 file changed

Lines changed: 73 additions & 9 deletions

File tree

crates/core/src/api/fields.rs

Lines changed: 73 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use serde::{Deserialize, Serialize};
1+
use serde::{Deserialize, Deserializer, Serialize};
22
use std::{
33
convert::Infallible,
44
fmt::{Display, Formatter},
@@ -13,7 +13,7 @@ use std::{
1313
/// them are in results. Frequently, not all fields in an Item are used, so this
1414
/// specification provides a mechanism for clients to request that servers to
1515
/// explicitly include or exclude certain fields.
16-
#[derive(Clone, Debug, Serialize, Deserialize, Default, PartialEq)]
16+
#[derive(Clone, Debug, Serialize, Default, PartialEq)]
1717
pub struct Fields {
1818
/// Fields to include.
1919
#[serde(skip_serializing_if = "Vec::is_empty", default)]
@@ -24,22 +24,59 @@ pub struct Fields {
2424
pub exclude: Vec<String>,
2525
}
2626

27-
impl FromStr for Fields {
28-
type Err = Infallible;
29-
30-
fn from_str(s: &str) -> Result<Self, Self::Err> {
27+
impl Fields {
28+
fn from_iter<I>(fields: I) -> Fields
29+
where
30+
I: IntoIterator<Item = String>,
31+
{
3132
let mut include = Vec::new();
3233
let mut exclude = Vec::new();
33-
for field in s.split(',').filter(|s| !s.is_empty()) {
34+
for field in fields {
3435
if let Some(field) = field.strip_prefix('-') {
3536
exclude.push(field.to_string());
3637
} else if let Some(field) = field.strip_prefix('+') {
3738
include.push(field.to_string());
3839
} else {
39-
include.push(field.to_string());
40+
include.push(field);
4041
}
4142
}
42-
Ok(Fields { include, exclude })
43+
Fields { include, exclude }
44+
}
45+
}
46+
47+
impl FromStr for Fields {
48+
type Err = Infallible;
49+
50+
fn from_str(s: &str) -> Result<Self, Self::Err> {
51+
Ok(Fields::from_iter(
52+
s.split(',')
53+
.filter(|s| !s.is_empty())
54+
.map(ToString::to_string),
55+
))
56+
}
57+
}
58+
59+
impl<'de> Deserialize<'de> for Fields {
60+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
61+
where
62+
D: Deserializer<'de>,
63+
{
64+
#[derive(Deserialize)]
65+
#[serde(untagged)]
66+
enum Repr {
67+
List(Vec<String>),
68+
Struct {
69+
#[serde(default)]
70+
include: Vec<String>,
71+
#[serde(default)]
72+
exclude: Vec<String>,
73+
},
74+
}
75+
76+
Ok(match Repr::deserialize(deserializer)? {
77+
Repr::List(fields) => Fields::from_iter(fields),
78+
Repr::Struct { include, exclude } => Fields { include, exclude },
79+
})
4380
}
4481
}
4582

@@ -136,4 +173,31 @@ mod tests {
136173
fn permissive_deserialization() {
137174
let _ = serde_json::from_str::<Fields>("{}").unwrap();
138175
}
176+
177+
#[test]
178+
fn deserialize_struct() {
179+
assert_eq!(
180+
Fields {
181+
include: vec!["foo".to_string()],
182+
exclude: vec!["bar".to_string()],
183+
},
184+
serde_json::from_str(r#"{"include": ["foo"], "exclude": ["bar"]}"#).unwrap()
185+
);
186+
}
187+
188+
#[test]
189+
fn deserialize_list() {
190+
assert_eq!(
191+
Fields {
192+
include: vec!["foo".to_string(), "baz".to_string()],
193+
exclude: vec!["bar".to_string()],
194+
},
195+
serde_json::from_str(r#"["foo", "-bar", "+baz"]"#).unwrap()
196+
);
197+
}
198+
199+
#[test]
200+
fn deserialize_empty_list() {
201+
assert_eq!(Fields::default(), serde_json::from_str("[]").unwrap());
202+
}
139203
}

0 commit comments

Comments
 (0)