1use std::fmt;
2
3use serde::{
4 Deserialize, Deserializer, Serialize, Serializer,
5 de::{self, Visitor},
6};
7
8#[derive(Clone, Debug, PartialEq, Eq, Hash)]
13pub enum Id {
14 Number(i64),
16 String(String),
18 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}