From ca557909c2bebe2b72bbe9734fe38d49129b5e13 Mon Sep 17 00:00:00 2001 From: cloudhead Date: Wed, 15 Nov 2023 14:36:27 +0100 Subject: [PATCH] radicle: Fix some serialization issues For some inexplicable reason, deserializing doesn't work unless we use our custom de-serializer for `LocalTime`. --- radicle/src/node.rs | 22 ++++++++- radicle/src/node/address/types.rs | 3 ++ radicle/src/serde_ext.rs | 77 +++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 1 deletion(-) diff --git a/radicle/src/node.rs b/radicle/src/node.rs index 90d565e9..b2891456 100644 --- a/radicle/src/node.rs +++ b/radicle/src/node.rs @@ -129,6 +129,7 @@ impl fmt::Display for State { /// Repository sync status for our own refs. #[derive(Debug, PartialEq, Eq, Clone, serde::Serialize, serde::Deserialize)] #[serde(tag = "status")] +#[serde(rename_all = "camelCase")] pub enum SyncStatus { /// We're in sync. #[serde(rename_all = "camelCase")] @@ -447,7 +448,9 @@ impl Session { pub struct Seed { pub nid: NodeId, pub addrs: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] pub state: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] pub sync: Option, } @@ -1100,9 +1103,26 @@ mod test { .unwrap(), "{\"error\":\"entity not found\"}" ); + + json::from_str::>( + &serde_json::to_string(&CommandResult::Okay(State::Connected { + since: LocalTime::now(), + ping: Default::default(), + fetching: Default::default(), + })) + .unwrap(), + ) + .unwrap(); + + assert_matches!( + json::from_str::>( + r#"{"connected":{"since":1699636852107,"fetching":[]}}"# + ), + Ok(CommandResult::Okay(_)) + ); assert_matches!( json::from_str::>( - r#"[{"nid":"z6Mkux1aUQD2voWWukVb5nNUR7thrHveQG4pDQua8nVhib7Z","addrs":[],"state":{"connected":{"since":1699636852107,"fetching":[]}}}]"# + r#"[{"nid":"z6MksmpU5b1dS7oaqF2bHXhQi1DWy2hB7Mh9CuN7y1DN6QSz","addrs":[{"addr":"seed.radicle.xyz:8776","source":"peer","lastSuccess":1699983994234,"lastAttempt":1699983994000,"banned":false}],"state":{"connected":{"since":1699983994,"fetching":[]}}}]"# ), Ok(CommandResult::Okay(_)) ); diff --git a/radicle/src/node/address/types.rs b/radicle/src/node/address/types.rs index 22efe119..3b8be1ed 100644 --- a/radicle/src/node/address/types.rs +++ b/radicle/src/node/address/types.rs @@ -137,8 +137,10 @@ pub struct KnownAddress { /// Address of the peer who sent us this address. pub source: Source, /// Last time this address was used to successfully connect to a peer. + #[serde(with = "crate::serde_ext::localtime::option::time")] pub last_success: Option, /// Last time this address was tried. + #[serde(with = "crate::serde_ext::localtime::option::time")] pub last_attempt: Option, /// Whether this address has been banned. pub banned: bool, @@ -187,6 +189,7 @@ pub struct SyncedAt { /// Head of `rad/sigrefs`. pub oid: git_ext::Oid, /// When these refs were synced. + #[serde(with = "crate::serde_ext::localtime::time")] pub timestamp: LocalTime, } diff --git a/radicle/src/serde_ext.rs b/radicle/src/serde_ext.rs index de0b30a0..c5a2fbe9 100644 --- a/radicle/src/serde_ext.rs +++ b/radicle/src/serde_ext.rs @@ -55,6 +55,34 @@ pub mod localtime { } } + pub mod option { + pub mod time { + use localtime::LocalTime; + use serde::{Deserialize, Deserializer, Serializer}; + + pub fn serialize(value: &Option, serializer: S) -> Result + where + S: Serializer, + { + match value { + Some(time) => serializer.serialize_some(&time.as_secs()), + None => serializer.serialize_none(), + } + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + let option = Option::::deserialize(deserializer)?; + match option { + Some(seconds) => Ok(Some(LocalTime::from_secs(seconds))), + None => Ok(None), + } + } + } + } + pub mod duration { use localtime::LocalDuration; use serde::{Deserialize, Deserializer, Serializer}; @@ -81,3 +109,52 @@ pub mod localtime { pub fn is_default(t: &T) -> bool { t == &T::default() } + +#[cfg(test)] +mod test { + use super::*; + + use ::localtime::LocalTime; + + #[test] + fn test_localtime() { + #[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)] + struct Test { + time: LocalTime, + } + let value = Test { + time: LocalTime::from_millis(1699636852107), + }; + + assert_eq!( + serde_json::from_str::(r#"{"time":1699636852107}"#).unwrap(), + value + ); + assert_eq!( + serde_json::from_str::(serde_json::to_string(&value).unwrap().as_str()).unwrap(), + value + ); + } + + #[test] + // Tests serialization into seconds instead of milliseconds. + fn test_localtime_ext() { + #[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)] + struct Test { + #[serde(with = "localtime::time")] + time: LocalTime, + } + let value = Test { + time: LocalTime::from_secs(1699636852107), + }; + + assert_eq!( + serde_json::from_str::(r#"{"time":1699636852107}"#).unwrap(), + value + ); + assert_eq!( + serde_json::from_str::(serde_json::to_string(&value).unwrap().as_str()).unwrap(), + value + ); + } +}