use crate::RpcRequestParsingError;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde_json::Value;
use std::sync::Arc;
use uuid::Uuid;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum RpcId {
String(Arc<str>),
Number(i64),
Null,
}
impl RpcId {
pub fn from_scheme(kind: IdSchemeKind, enc: IdSchemeEncoding) -> Self {
let s = enc.encode(kind.generate());
RpcId::String(Arc::from(s))
}
pub fn new_uuid_v4() -> Self {
Self::from_scheme(IdSchemeKind::UuidV4, IdSchemeEncoding::Standard)
}
pub fn new_uuid_v4_base64() -> Self {
Self::from_scheme(IdSchemeKind::UuidV4, IdSchemeEncoding::Base64)
}
pub fn new_uuid_v4_base64url() -> Self {
Self::from_scheme(IdSchemeKind::UuidV4, IdSchemeEncoding::Base64UrlNoPad)
}
pub fn new_uuid_v4_base58() -> Self {
Self::from_scheme(IdSchemeKind::UuidV4, IdSchemeEncoding::Base58)
}
pub fn new_uuid_v7() -> Self {
Self::from_scheme(IdSchemeKind::UuidV7, IdSchemeEncoding::Standard)
}
pub fn new_uuid_v7_base64() -> Self {
Self::from_scheme(IdSchemeKind::UuidV7, IdSchemeEncoding::Base64)
}
pub fn new_uuid_v7_base64url() -> Self {
Self::from_scheme(IdSchemeKind::UuidV7, IdSchemeEncoding::Base64UrlNoPad)
}
pub fn new_uuid_v7_base58() -> Self {
Self::from_scheme(IdSchemeKind::UuidV7, IdSchemeEncoding::Base58)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum IdSchemeKind {
UuidV4,
UuidV7,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum IdSchemeEncoding {
Standard,
Base64,
Base64UrlNoPad,
Base58,
}
impl IdSchemeKind {
fn generate(&self) -> Vec<u8> {
match self {
IdSchemeKind::UuidV4 => Uuid::new_v4().as_bytes().to_vec(),
IdSchemeKind::UuidV7 => Uuid::now_v7().as_bytes().to_vec(),
}
}
}
impl IdSchemeEncoding {
fn encode(&self, bytes: Vec<u8>) -> String {
match self {
IdSchemeEncoding::Standard => {
Uuid::from_slice(&bytes).map(|u| u.to_string()).unwrap_or_default()
}
IdSchemeEncoding::Base64 => data_encoding::BASE64.encode(&bytes),
IdSchemeEncoding::Base64UrlNoPad => data_encoding::BASE64URL_NOPAD.encode(&bytes),
IdSchemeEncoding::Base58 => bs58::encode(&bytes).into_string(),
}
}
}
impl core::fmt::Display for RpcId {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
RpcId::String(s) => write!(f, "{}", s),
RpcId::Number(n) => write!(f, "{}", n),
RpcId::Null => write!(f, "null"),
}
}
}
impl RpcId {
pub fn to_value(&self) -> Value {
match self {
RpcId::String(s) => Value::String(s.to_string()),
RpcId::Number(n) => Value::Number((*n).into()),
RpcId::Null => Value::Null,
}
}
pub fn from_value(value: Value) -> core::result::Result<Self, RpcRequestParsingError> {
match value {
Value::String(s) => Ok(RpcId::String(s.into())),
Value::Number(n) => n.as_i64().map(RpcId::Number).ok_or_else(|| RpcRequestParsingError::IdInvalid {
actual: format!("{n}"),
cause: "Number is not a valid i64".into(),
}),
Value::Null => Ok(RpcId::Null),
_ => Err(RpcRequestParsingError::IdInvalid {
actual: format!("{value:?}"),
cause: "ID must be a String, Number, or Null".into(),
}),
}
}
}
impl Default for RpcId {
fn default() -> Self {
RpcId::Null
}
}
impl Serialize for RpcId {
fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
RpcId::String(s) => serializer.serialize_str(s),
RpcId::Number(n) => serializer.serialize_i64(*n),
RpcId::Null => serializer.serialize_none(),
}
}
}
impl<'de> Deserialize<'de> for RpcId {
fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let value = Value::deserialize(deserializer)?;
RpcId::from_value(value).map_err(serde::de::Error::custom)
}
}
impl From<String> for RpcId {
fn from(s: String) -> Self {
RpcId::String(s.into())
}
}
impl From<&str> for RpcId {
fn from(s: &str) -> Self {
RpcId::String(s.into())
}
}
impl From<i64> for RpcId {
fn from(n: i64) -> Self {
RpcId::Number(n)
}
}
impl From<i32> for RpcId {
fn from(n: i32) -> Self {
RpcId::Number(n as i64)
}
}
impl From<u32> for RpcId {
fn from(n: u32) -> Self {
RpcId::Number(n as i64)
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::{from_value, json, to_value};
type TestResult<T> = core::result::Result<T, Box<dyn std::error::Error>>;
#[test]
fn test_rpc_id_ser_de() -> TestResult<()> {
let ids = [
RpcId::String("id-1".into()),
RpcId::Number(123),
RpcId::Null,
RpcId::String("".into()), ];
let expected_values = [
json!("id-1"),
json!(123),
json!(null),
json!(""), ];
for (i, id) in ids.iter().enumerate() {
let value = to_value(id)?;
assert_eq!(value, expected_values[i], "Serialization check for id[{i}]");
let deserialized_id: RpcId = from_value(value.clone())?;
assert_eq!(&deserialized_id, id, "Deserialization check for id[{i}]");
let from_value_id = RpcId::from_value(value)?;
assert_eq!(from_value_id, *id, "from_value check for id[{i}]");
}
Ok(())
}
#[test]
fn test_rpc_id_from_value_invalid() -> TestResult<()> {
let invalid_values = vec![
json!(true),
json!(123.45), json!([1, 2]),
json!({"a": 1}),
];
for value in invalid_values {
let res = RpcId::from_value(value.clone());
assert!(
matches!(res, Err(RpcRequestParsingError::IdInvalid { .. })),
"Expected RpcIdInvalid for value: {:?}",
value
);
}
Ok(())
}
#[test]
fn test_rpc_id_to_value() -> TestResult<()> {
let id_str = RpcId::String("hello".into());
let id_num = RpcId::Number(42);
let id_null = RpcId::Null;
let val_str = id_str.to_value();
let val_num = id_num.to_value();
let val_null = id_null.to_value();
assert_eq!(val_str, json!("hello"));
assert_eq!(val_num, json!(42));
assert_eq!(val_null, json!(null));
Ok(())
}
#[test]
fn test_rpc_id_from_impls() -> TestResult<()> {
assert_eq!(RpcId::from("test_str"), RpcId::String("test_str".into()));
assert_eq!(
RpcId::from(String::from("test_string")),
RpcId::String("test_string".into())
);
assert_eq!(RpcId::from(100i64), RpcId::Number(100));
assert_eq!(RpcId::from(200i32), RpcId::Number(200));
assert_eq!(RpcId::from(300u32), RpcId::Number(300));
Ok(())
}
#[test]
fn test_rpc_id_default() -> TestResult<()> {
assert_eq!(RpcId::default(), RpcId::Null);
Ok(())
}
}