Skip to content

Commit 79200fa

Browse files
authored
Deserialize missing properties as none (#159)
* Deserialize missing properties as none fixed #147 * Don't use let..else
1 parent 0660928 commit 79200fa

File tree

4 files changed

+79
-4
lines changed

4 files changed

+79
-4
lines changed

lib/src/types/serde/element.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -338,18 +338,23 @@ impl<'de, T: ElementData<'de>> Deserializer<'de> for ElementDataDeserializer<'de
338338
visitor.visit_unit()
339339
}
340340

341+
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>
342+
where
343+
V: Visitor<'de>,
344+
{
345+
visitor.visit_none()
346+
}
347+
341348
forward_to_deserialize_any! {
342349
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
343-
bytes byte_buf option seq tuple map enum identifier
350+
bytes byte_buf seq tuple map enum identifier
344351
}
345352

346353
fn deserialize_any<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
347354
where
348355
V: Visitor<'de>,
349356
{
350-
Err(DeError::custom(
351-
"deserializing additional data requires a struct",
352-
))
357+
Err(DeError::PropertyMissingButRequired)
353358
}
354359
}
355360

@@ -499,6 +504,7 @@ impl<'de> IntoDeserializer<'de, DeError> for BorrowedStr<'de> {
499504
}
500505
}
501506

507+
#[derive(Copy, Clone, Debug)]
502508
enum AdditionalData<'de, T> {
503509
Property(&'de BoltType),
504510
Element(T),

lib/src/types/serde/error.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,15 @@ pub enum DeError {
5454
#[error("The property does not exist")]
5555
NoSuchProperty,
5656

57+
#[error(
58+
"The property is missing but the deserializer still expects a value. \
59+
If you have an optional property with a default value, you need to \
60+
use an Option<T> instead (the default attribute does not work in \
61+
this particular instance). If you meant to extract additional data \
62+
other than properties, you need to use the appropriate struct wrapper."
63+
)]
64+
PropertyMissingButRequired,
65+
5766
#[error("{0}")]
5867
Other(String),
5968

lib/src/types/serde/node.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,35 @@ mod tests {
278278
});
279279
}
280280

281+
#[test]
282+
fn extract_missing_properties_with_option() {
283+
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
284+
struct Person {
285+
favorite_rust_crate: Option<String>,
286+
}
287+
288+
test_extract_node(Person {
289+
favorite_rust_crate: None,
290+
});
291+
}
292+
293+
#[test]
294+
fn extract_missing_properties_with_default() {
295+
fn favorite_rust_crate() -> String {
296+
"graph".to_owned()
297+
}
298+
299+
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
300+
struct Person {
301+
#[serde(default = "favorite_rust_crate")]
302+
favorite_rust_crate: String,
303+
}
304+
305+
let node = test_node();
306+
let actual = node.to::<Person>().unwrap_err();
307+
assert!(matches!(actual, DeError::PropertyMissingButRequired));
308+
}
309+
281310
#[test]
282311
fn extract_node_id() {
283312
test_extract_node_extra(Id(1337));

lib/tests/missing_properties.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
use neo4rs::{query, Node};
2+
use serde::Deserialize;
3+
4+
mod container;
5+
6+
#[tokio::test]
7+
async fn missing_properties() {
8+
let neo4j = container::Neo4jContainer::new().await;
9+
let graph = neo4j.graph();
10+
11+
let a_val = None::<String>;
12+
let mut result = graph
13+
.execute(query("CREATE (ts:TestStruct {a: $a}) RETURN ts").param("a", a_val))
14+
.await
15+
.unwrap();
16+
let row = result.next().await.unwrap().unwrap();
17+
let expected = StructWithOption { a: None, b: None };
18+
19+
let test_struct: StructWithOption = row.to().unwrap();
20+
assert_eq!(test_struct, expected);
21+
22+
let node = row.get::<Node>("ts").unwrap();
23+
let test_struct: StructWithOption = node.to().unwrap();
24+
assert_eq!(test_struct, expected);
25+
}
26+
27+
#[derive(Deserialize, PartialEq, Eq, Debug)]
28+
struct StructWithOption {
29+
a: Option<String>,
30+
b: Option<String>,
31+
}

0 commit comments

Comments
 (0)