1use crate::RpcRequestParsingError;
2use serde::{Deserialize, Deserializer, Serialize, Serializer};
3use serde_json::Value;
4use std::sync::Arc;
5use uuid::Uuid;
6
7#[derive(Debug, Clone, PartialEq, Eq, Hash)]
11pub enum RpcId {
12 String(Arc<str>),
13 Number(i64),
14 Null,
15}
16
17impl RpcId {
20 pub fn from_scheme(kind: IdSchemeKind, enc: IdSchemeEncoding) -> Self {
22 let s = enc.encode(kind.generate());
23 RpcId::String(Arc::from(s))
24 }
25
26 pub fn new_uuid_v4() -> Self {
29 Self::from_scheme(IdSchemeKind::UuidV4, IdSchemeEncoding::Standard)
30 }
31 pub fn new_uuid_v4_base64() -> Self {
32 Self::from_scheme(IdSchemeKind::UuidV4, IdSchemeEncoding::Base64)
33 }
34 pub fn new_uuid_v4_base64url() -> Self {
35 Self::from_scheme(IdSchemeKind::UuidV4, IdSchemeEncoding::Base64UrlNoPad)
36 }
37 pub fn new_uuid_v4_base58() -> Self {
38 Self::from_scheme(IdSchemeKind::UuidV4, IdSchemeEncoding::Base58)
39 }
40
41 pub fn new_uuid_v7() -> Self {
42 Self::from_scheme(IdSchemeKind::UuidV7, IdSchemeEncoding::Standard)
43 }
44 pub fn new_uuid_v7_base64() -> Self {
45 Self::from_scheme(IdSchemeKind::UuidV7, IdSchemeEncoding::Base64)
46 }
47 pub fn new_uuid_v7_base64url() -> Self {
48 Self::from_scheme(IdSchemeKind::UuidV7, IdSchemeEncoding::Base64UrlNoPad)
49 }
50 pub fn new_uuid_v7_base58() -> Self {
51 Self::from_scheme(IdSchemeKind::UuidV7, IdSchemeEncoding::Base58)
52 }
53
54 }
56
57#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
59pub enum IdSchemeKind {
60 UuidV4,
61 UuidV7,
62 }
64
65#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
67pub enum IdSchemeEncoding {
68 Standard,
69 Base64,
70 Base64UrlNoPad,
71 Base58,
72}
73
74impl IdSchemeKind {
75 fn generate(&self) -> Vec<u8> {
76 match self {
77 IdSchemeKind::UuidV4 => Uuid::new_v4().as_bytes().to_vec(),
78 IdSchemeKind::UuidV7 => Uuid::now_v7().as_bytes().to_vec(),
79 }
81 }
82}
83
84impl IdSchemeEncoding {
85 fn encode(&self, bytes: Vec<u8>) -> String {
87 match self {
88 IdSchemeEncoding::Standard => {
89 Uuid::from_slice(&bytes).map(|u| u.to_string()).unwrap_or_default()
91 }
92 IdSchemeEncoding::Base64 => data_encoding::BASE64.encode(&bytes),
93 IdSchemeEncoding::Base64UrlNoPad => data_encoding::BASE64URL_NOPAD.encode(&bytes),
94 IdSchemeEncoding::Base58 => bs58::encode(&bytes).into_string(),
95 }
96 }
97}
98
99impl core::fmt::Display for RpcId {
104 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
105 match self {
106 RpcId::String(s) => write!(f, "{}", s),
107 RpcId::Number(n) => write!(f, "{}", n),
108 RpcId::Null => write!(f, "null"),
109 }
110 }
111}
112
113impl RpcId {
118 pub fn to_value(&self) -> Value {
120 match self {
121 RpcId::String(s) => Value::String(s.to_string()),
122 RpcId::Number(n) => Value::Number((*n).into()),
123 RpcId::Null => Value::Null,
124 }
125 }
126
127 pub fn from_value(value: Value) -> core::result::Result<Self, RpcRequestParsingError> {
130 match value {
131 Value::String(s) => Ok(RpcId::String(s.into())),
132 Value::Number(n) => n.as_i64().map(RpcId::Number).ok_or_else(|| RpcRequestParsingError::IdInvalid {
133 actual: format!("{n}"),
134 cause: "Number is not a valid i64".into(),
135 }),
136 Value::Null => Ok(RpcId::Null),
137 _ => Err(RpcRequestParsingError::IdInvalid {
138 actual: format!("{value:?}"),
139 cause: "ID must be a String, Number, or Null".into(),
140 }),
141 }
142 }
143}
144
145impl Default for RpcId {
148 fn default() -> Self {
150 RpcId::Null
151 }
152}
153
154impl Serialize for RpcId {
157 fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
158 where
159 S: Serializer,
160 {
161 match self {
162 RpcId::String(s) => serializer.serialize_str(s),
163 RpcId::Number(n) => serializer.serialize_i64(*n),
164 RpcId::Null => serializer.serialize_none(),
165 }
166 }
167}
168
169impl<'de> Deserialize<'de> for RpcId {
170 fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
171 where
172 D: Deserializer<'de>,
173 {
174 let value = Value::deserialize(deserializer)?;
175 RpcId::from_value(value).map_err(serde::de::Error::custom)
176 }
177}
178
179impl From<String> for RpcId {
182 fn from(s: String) -> Self {
183 RpcId::String(s.into())
184 }
185}
186
187impl From<&str> for RpcId {
188 fn from(s: &str) -> Self {
189 RpcId::String(s.into())
190 }
191}
192
193impl From<i64> for RpcId {
194 fn from(n: i64) -> Self {
195 RpcId::Number(n)
196 }
197}
198
199impl From<i32> for RpcId {
200 fn from(n: i32) -> Self {
201 RpcId::Number(n as i64)
202 }
203}
204
205impl From<u32> for RpcId {
206 fn from(n: u32) -> Self {
207 RpcId::Number(n as i64)
208 }
209}
210
211#[cfg(test)]
214mod tests {
215 use super::*;
216 use serde_json::{from_value, json, to_value};
217
218 type TestResult<T> = core::result::Result<T, Box<dyn std::error::Error>>; #[test]
221 fn test_rpc_id_ser_de() -> TestResult<()> {
222 let ids = [
224 RpcId::String("id-1".into()),
225 RpcId::Number(123),
226 RpcId::Null,
227 RpcId::String("".into()), ];
229 let expected_values = [
230 json!("id-1"),
231 json!(123),
232 json!(null),
233 json!(""), ];
235
236 for (i, id) in ids.iter().enumerate() {
238 let value = to_value(id)?;
239 assert_eq!(value, expected_values[i], "Serialization check for id[{i}]");
240
241 let deserialized_id: RpcId = from_value(value.clone())?;
242 assert_eq!(&deserialized_id, id, "Deserialization check for id[{i}]");
243
244 let from_value_id = RpcId::from_value(value)?;
245 assert_eq!(from_value_id, *id, "from_value check for id[{i}]");
246 }
247
248 Ok(())
249 }
250
251 #[test]
252 fn test_rpc_id_from_value_invalid() -> TestResult<()> {
253 let invalid_values = vec![
255 json!(true),
256 json!(123.45), json!([1, 2]),
258 json!({"a": 1}),
259 ];
260
261 for value in invalid_values {
263 let res = RpcId::from_value(value.clone());
264 assert!(
265 matches!(res, Err(RpcRequestParsingError::IdInvalid { .. })),
266 "Expected RpcIdInvalid for value: {:?}",
267 value
268 );
269 }
270
271 Ok(())
272 }
273
274 #[test]
275 fn test_rpc_id_to_value() -> TestResult<()> {
276 let id_str = RpcId::String("hello".into());
278 let id_num = RpcId::Number(42);
279 let id_null = RpcId::Null;
280
281 let val_str = id_str.to_value();
283 let val_num = id_num.to_value();
284 let val_null = id_null.to_value();
285
286 assert_eq!(val_str, json!("hello"));
288 assert_eq!(val_num, json!(42));
289 assert_eq!(val_null, json!(null));
290
291 Ok(())
292 }
293
294 #[test]
295 fn test_rpc_id_from_impls() -> TestResult<()> {
296 assert_eq!(RpcId::from("test_str"), RpcId::String("test_str".into()));
298 assert_eq!(
299 RpcId::from(String::from("test_string")),
300 RpcId::String("test_string".into())
301 );
302
303 assert_eq!(RpcId::from(100i64), RpcId::Number(100));
305 assert_eq!(RpcId::from(200i32), RpcId::Number(200));
306 assert_eq!(RpcId::from(300u32), RpcId::Number(300));
307
308 Ok(())
309 }
310
311 #[test]
312 fn test_rpc_id_default() -> TestResult<()> {
313 assert_eq!(RpcId::default(), RpcId::Null);
315
316 Ok(())
317 }
318}
319
320