anki/rslib/src/serde.rs
Damien Elmes 64ebc32b3d tidy up Rust imports
rustfmt can do this automatically, but only when run with a nightly
toolchain, so it needs to be manually done for now - see rslib/rusfmt.toml
2021-04-18 18:38:54 +10:00

98 lines
2.4 KiB
Rust

// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
use serde::{Deserialize as DeTrait, Deserializer};
pub(crate) use serde_aux::field_attributes::{
deserialize_bool_from_anything, deserialize_number_from_string,
};
use serde_json::Value;
use crate::timestamp::TimestampSecs;
/// Note: if you wish to cover the case where a field is missing, make sure you also
/// use the `serde(default)` flag.
pub(crate) fn default_on_invalid<'de, T, D>(deserializer: D) -> Result<T, D::Error>
where
T: Default + DeTrait<'de>,
D: serde::de::Deserializer<'de>,
{
let v: Value = DeTrait::deserialize(deserializer)?;
Ok(T::deserialize(v).unwrap_or_default())
}
pub(crate) fn deserialize_int_from_number<'de, T, D>(deserializer: D) -> Result<T, D::Error>
where
D: Deserializer<'de>,
T: serde::Deserialize<'de> + FromI64,
{
#[derive(DeTrait)]
#[serde(untagged)]
enum IntOrFloat {
Int(i64),
Float(f64),
}
match IntOrFloat::deserialize(deserializer)? {
IntOrFloat::Float(f) => Ok(T::from_i64(f as i64)),
IntOrFloat::Int(i) => Ok(T::from_i64(i)),
}
}
// It may be possible to use the num_traits crate instead in the future.
pub(crate) trait FromI64 {
fn from_i64(val: i64) -> Self;
}
impl FromI64 for i32 {
fn from_i64(val: i64) -> Self {
val as Self
}
}
impl FromI64 for u32 {
fn from_i64(val: i64) -> Self {
val.max(0) as Self
}
}
impl FromI64 for i64 {
fn from_i64(val: i64) -> Self {
val
}
}
impl FromI64 for TimestampSecs {
fn from_i64(val: i64) -> Self {
TimestampSecs(val as i64)
}
}
#[cfg(test)]
mod test {
use serde::Deserialize;
use super::*;
#[derive(Deserialize, Debug, PartialEq)]
struct MaybeInvalid {
#[serde(deserialize_with = "default_on_invalid", default)]
field: Option<usize>,
}
#[test]
fn invalid_or_missing() {
assert_eq!(
serde_json::from_str::<MaybeInvalid>(r#"{"field": 5}"#).unwrap(),
MaybeInvalid { field: Some(5) }
);
assert_eq!(
serde_json::from_str::<MaybeInvalid>(r#"{"field": "5"}"#).unwrap(),
MaybeInvalid { field: None }
);
assert_eq!(
serde_json::from_str::<MaybeInvalid>(r#"{"another": 5}"#).unwrap(),
MaybeInvalid { field: None }
);
}
}