Skip to main content

json_rpc/
id.rs

1use std::fmt;
2
3use serde::{
4    Deserialize, Deserializer, Serialize, Serializer,
5    de::{self, Visitor},
6};
7
8/// A JSON-RPC request or response identifier.
9///
10/// Per the spec, an `Id` MUST be a `String`, `Number`, or `Null`.
11/// Fractional numbers SHOULD NOT be used.
12#[derive(Clone, Debug, PartialEq, Eq, Hash)]
13pub enum Id {
14    /// An integer identifier.
15    Number(i64),
16    /// A string identifier.
17    String(String),
18    /// A null identifier (discouraged per spec).
19    Null,
20}
21
22impl Id {
23    pub fn as_i64(&self) -> Option<i64> {
24        match self {
25            Self::Number(n) => Some(*n),
26            _ => None,
27        }
28    }
29
30    pub fn as_str(&self) -> Option<&str> {
31        match self {
32            Self::String(s) => Some(s.as_str()),
33            _ => None,
34        }
35    }
36
37    pub fn is_null(&self) -> bool {
38        matches!(self, Self::Null)
39    }
40}
41
42impl fmt::Display for Id {
43    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44        match self {
45            Self::Number(n) => write!(f, "{n}"),
46            Self::String(s) => write!(f, "{s}"),
47            Self::Null => write!(f, "null"),
48        }
49    }
50}
51
52impl From<i64> for Id {
53    fn from(n: i64) -> Self {
54        Self::Number(n)
55    }
56}
57
58impl From<i32> for Id {
59    fn from(n: i32) -> Self {
60        Self::Number(n as i64)
61    }
62}
63
64impl From<u64> for Id {
65    fn from(n: u64) -> Self {
66        Self::Number(n as i64)
67    }
68}
69
70impl From<String> for Id {
71    fn from(s: String) -> Self {
72        Self::String(s)
73    }
74}
75
76impl From<&str> for Id {
77    fn from(s: &str) -> Self {
78        Self::String(s.to_owned())
79    }
80}
81
82impl Serialize for Id {
83    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
84        match self {
85            Self::Number(n) => serializer.serialize_i64(*n),
86            Self::String(s) => serializer.serialize_str(s),
87            Self::Null => serializer.serialize_unit(),
88        }
89    }
90}
91
92impl<'de> Deserialize<'de> for Id {
93    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
94        struct IdVisitor;
95
96        impl<'de> Visitor<'de> for IdVisitor {
97            type Value = Id;
98
99            fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100                f.write_str("a JSON-RPC id: a string, an integer, or null")
101            }
102
103            fn visit_i64<E: de::Error>(self, v: i64) -> Result<Self::Value, E> {
104                Ok(Id::Number(v))
105            }
106
107            fn visit_u64<E: de::Error>(self, v: u64) -> Result<Self::Value, E> {
108                Ok(Id::Number(v as i64))
109            }
110
111            fn visit_f64<E: de::Error>(self, v: f64) -> Result<Self::Value, E> {
112                if v.fract() == 0.0 {
113                    Ok(Id::Number(v as i64))
114                } else {
115                    Err(de::Error::invalid_value(de::Unexpected::Float(v), &self))
116                }
117            }
118
119            fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
120                Ok(Id::String(v.to_owned()))
121            }
122
123            fn visit_string<E: de::Error>(self, v: String) -> Result<Self::Value, E> {
124                Ok(Id::String(v))
125            }
126
127            fn visit_unit<E: de::Error>(self) -> Result<Self::Value, E> {
128                Ok(Id::Null)
129            }
130        }
131
132        deserializer.deserialize_any(IdVisitor)
133    }
134}
135
136#[cfg(test)]
137mod tests {
138    use super::*;
139
140    #[test]
141    fn test_id_serde_number() {
142        let json = "42";
143        let id: Id = serde_json::from_str(json).unwrap();
144        assert_eq!(id, Id::Number(42));
145        assert_eq!(serde_json::to_string(&id).unwrap(), json);
146    }
147
148    #[test]
149    fn test_id_serde_string() {
150        let json = r#""abc""#;
151        let id: Id = serde_json::from_str(json).unwrap();
152        assert_eq!(id, Id::String("abc".into()));
153        assert_eq!(serde_json::to_string(&id).unwrap(), json);
154    }
155
156    #[test]
157    fn test_id_serde_null() {
158        let json = "null";
159        let id: Id = serde_json::from_str(json).unwrap();
160        assert_eq!(id, Id::Null);
161        assert_eq!(serde_json::to_string(&id).unwrap(), json);
162    }
163
164    #[test]
165    fn test_id_deserialize_float_rejected() {
166        let err = serde_json::from_str::<Id>("1.5").unwrap_err();
167        assert!(err.to_string().contains("floating point"));
168    }
169
170    #[test]
171    fn test_id_deserialize_whole_float_accepted() {
172        let id: Id = serde_json::from_str("1.0").unwrap();
173        assert_eq!(id, Id::Number(1));
174    }
175
176    #[test]
177    fn test_id_from_i64() {
178        let id: Id = 42i64.into();
179        assert_eq!(id, Id::Number(42));
180    }
181
182    #[test]
183    fn test_id_from_str() {
184        let id: Id = "foo".into();
185        assert_eq!(id, Id::String("foo".into()));
186    }
187
188    #[test]
189    fn test_id_display_number() {
190        assert_eq!(Id::Number(42).to_string(), "42");
191    }
192
193    #[test]
194    fn test_id_display_string() {
195        assert_eq!(Id::String("abc".into()).to_string(), "abc");
196    }
197
198    #[test]
199    fn test_id_display_null() {
200        assert_eq!(Id::Null.to_string(), "null");
201    }
202
203    #[test]
204    fn test_id_as_i64_some() {
205        assert_eq!(Id::Number(10).as_i64(), Some(10));
206    }
207
208    #[test]
209    fn test_id_as_i64_none() {
210        assert_eq!(Id::String("x".into()).as_i64(), None);
211        assert_eq!(Id::Null.as_i64(), None);
212    }
213
214    #[test]
215    fn test_id_as_str_some() {
216        assert_eq!(Id::String("x".into()).as_str(), Some("x"));
217    }
218
219    #[test]
220    fn test_id_as_str_none() {
221        assert_eq!(Id::Number(1).as_str(), None);
222        assert_eq!(Id::Null.as_str(), None);
223    }
224}