1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
//! JSON-RPC IDs

use crate::utils::uuid_str;
use serde::{Deserialize, Serialize};
use std::fmt;

/// JSON-RPC ID: request-specific identifier
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq, Ord, PartialOrd)]
#[serde(untagged)]
pub enum Id {
    /// Numerical JSON ID
    Num(i64),
    /// String JSON ID
    Str(String),
    /// null JSON ID
    None,
}

impl Id {
    /// Create a JSON-RPC ID containing a UUID v4 (i.e. random)
    pub fn uuid_v4() -> Self {
        Self::Str(uuid_str())
    }
}

impl fmt::Display for Id {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Id::Num(i) => write!(f, "{}", i),
            Id::Str(s) => write!(f, "{}", s),
            Id::None => write!(f, ""),
        }
    }
}

#[cfg(test)]
mod tests {
    use serde::{de::DeserializeOwned, Serialize};
    use std::fmt::Debug;

    use super::*;

    #[test]
    fn round_tripping_jsonrpc_id() {
        let str = r#""42""#;
        serialization_roundtrip::<Id>(str);

        let str2 = r#""936DA01F-9ABD-4D9D-80C7-02AF85C822A8""#;
        serialization_roundtrip::<Id>(str2);

        let num = r#"42"#;
        serialization_roundtrip::<Id>(num);

        let zero = r#"0"#;
        serialization_roundtrip::<Id>(zero);

        let null = r#"null"#;
        serialization_roundtrip::<Id>(null);
    }

    fn serialization_roundtrip<T>(json_data: &str)
    where
        T: Debug + PartialEq + Serialize + DeserializeOwned,
    {
        let parsed0 = serde_json::from_str::<T>(json_data);
        assert!(parsed0.is_ok());
        let parsed0 = parsed0.unwrap();

        let serialized = serde_json::to_string(&parsed0);
        assert!(serialized.is_ok());
        let serialized = serialized.unwrap();

        let parsed1 = serde_json::from_str::<T>(&serialized);
        assert!(parsed1.is_ok());
        let parsed1 = parsed1.unwrap();

        assert_eq!(parsed0, parsed1);
    }
}