use alloc::borrow::Cow;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde_json::{Value, json};
use super::MessageHandleError;
use crate::{Event, Filter, JsonUtil, SubscriptionId};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ClientMessage<'a> {
Event(Cow<'a, Event>),
Req {
subscription_id: Cow<'a, SubscriptionId>,
filters: Vec<Cow<'a, Filter>>,
},
Count {
subscription_id: Cow<'a, SubscriptionId>,
filter: Cow<'a, Filter>,
},
Close(Cow<'a, SubscriptionId>),
Auth(Cow<'a, Event>),
NegOpen {
subscription_id: Cow<'a, SubscriptionId>,
filter: Cow<'a, Filter>,
id_size: Option<u8>,
initial_message: Cow<'a, str>,
},
NegMsg {
subscription_id: Cow<'a, SubscriptionId>,
message: Cow<'a, str>,
},
NegClose {
subscription_id: Cow<'a, SubscriptionId>,
},
}
impl ClientMessage<'_> {
#[inline]
pub fn event(event: Event) -> Self {
Self::Event(Cow::Owned(event))
}
#[inline]
pub fn req<T>(subscription_id: SubscriptionId, filters: T) -> Self
where
T: Into<Vec<Filter>>,
{
Self::Req {
subscription_id: Cow::Owned(subscription_id),
filters: filters.into().into_iter().map(Cow::Owned).collect(),
}
}
#[inline]
pub fn count(subscription_id: SubscriptionId, filter: Filter) -> Self {
Self::Count {
subscription_id: Cow::Owned(subscription_id),
filter: Cow::Owned(filter),
}
}
#[inline]
pub fn close(subscription_id: SubscriptionId) -> Self {
Self::Close(Cow::Owned(subscription_id))
}
#[inline]
pub fn auth(event: Event) -> Self {
Self::Auth(Cow::Owned(event))
}
pub fn neg_open(
subscription_id: SubscriptionId,
filter: Filter,
initial_message: String,
) -> Self {
Self::NegOpen {
subscription_id: Cow::Owned(subscription_id),
filter: Cow::Owned(filter),
id_size: None,
initial_message: Cow::Owned(initial_message),
}
}
#[inline]
pub fn is_event(&self) -> bool {
matches!(self, ClientMessage::Event(_))
}
#[inline]
pub fn is_req(&self) -> bool {
matches!(self, ClientMessage::Req { .. })
}
#[inline]
pub fn is_close(&self) -> bool {
matches!(self, ClientMessage::Close(_))
}
#[inline]
pub fn is_auth(&self) -> bool {
matches!(self, ClientMessage::Auth(_))
}
pub fn as_value(&self) -> Value {
match self {
Self::Event(event) => json!(["EVENT", event]),
Self::Req {
subscription_id,
filters,
} => {
let mut json = json!(["REQ", subscription_id]);
let mut filters = json!(filters);
if let Some(json) = json.as_array_mut() {
if let Some(filters) = filters.as_array_mut() {
json.append(filters);
}
}
json
}
Self::Count {
subscription_id,
filter,
} => {
json!(["COUNT", subscription_id, filter])
}
Self::Close(subscription_id) => json!(["CLOSE", subscription_id]),
Self::Auth(event) => json!(["AUTH", event]),
Self::NegOpen {
subscription_id,
filter,
id_size,
initial_message,
} => match id_size {
Some(id_size) => json!([
"NEG-OPEN",
subscription_id,
filter,
id_size,
initial_message
]),
None => json!(["NEG-OPEN", subscription_id, filter, initial_message]),
},
Self::NegMsg {
subscription_id,
message,
} => json!(["NEG-MSG", subscription_id, message]),
Self::NegClose { subscription_id } => json!(["NEG-CLOSE", subscription_id]),
}
}
pub fn from_value(msg: Value) -> Result<Self, MessageHandleError> {
let v = msg
.as_array()
.ok_or(MessageHandleError::InvalidMessageFormat)?;
if v.is_empty() {
return Err(MessageHandleError::InvalidMessageFormat);
}
let v_len: usize = v.len();
if v[0] == "EVENT" {
if v_len >= 2 {
let event: Event = serde_json::from_value(v[1].clone())?;
return Ok(Self::event(event));
} else {
return Err(MessageHandleError::InvalidMessageFormat);
}
}
if v[0] == "REQ" {
if v_len >= 3 {
let subscription_id: SubscriptionId = serde_json::from_value(v[1].clone())?;
let filters: Vec<Cow<Filter>> =
serde_json::from_value(Value::Array(v[2..].to_vec()))?;
return Ok(Self::Req {
subscription_id: Cow::Owned(subscription_id),
filters,
});
} else {
return Err(MessageHandleError::InvalidMessageFormat);
}
}
if v[0] == "COUNT" {
if v_len >= 3 {
let subscription_id: SubscriptionId = serde_json::from_value(v[1].clone())?;
let filter: Filter = serde_json::from_value(v[2].clone())?;
return Ok(Self::count(subscription_id, filter));
} else {
return Err(MessageHandleError::InvalidMessageFormat);
}
}
if v[0] == "CLOSE" {
if v_len >= 2 {
let subscription_id: SubscriptionId = serde_json::from_value(v[1].clone())?;
return Ok(Self::close(subscription_id));
} else {
return Err(MessageHandleError::InvalidMessageFormat);
}
}
if v[0] == "AUTH" {
if v_len >= 2 {
let event: Event = serde_json::from_value(v[1].clone())?;
return Ok(Self::auth(event));
} else {
return Err(MessageHandleError::InvalidMessageFormat);
}
}
if v[0] == "NEG-OPEN" {
if v_len == 4 {
let subscription_id: SubscriptionId = serde_json::from_value(v[1].clone())?;
let filter: Filter = Filter::from_json(v[2].to_string())?;
let initial_message: String = serde_json::from_value(v[3].clone())?;
return Ok(Self::neg_open(subscription_id, filter, initial_message));
}
if v_len == 5 {
let subscription_id: SubscriptionId = serde_json::from_value(v[1].clone())?;
let filter: Filter = Filter::from_json(v[2].to_string())?;
let id_size: u8 =
v[3].as_u64()
.ok_or(MessageHandleError::InvalidMessageFormat)? as u8;
let initial_message: String = serde_json::from_value(v[4].clone())?;
return Ok(Self::NegOpen {
subscription_id: Cow::Owned(subscription_id),
filter: Cow::Owned(filter),
id_size: Some(id_size),
initial_message: Cow::Owned(initial_message),
});
}
return Err(MessageHandleError::InvalidMessageFormat);
}
if v[0] == "NEG-MSG" {
if v_len >= 3 {
let subscription_id: SubscriptionId = serde_json::from_value(v[1].clone())?;
let message: String = serde_json::from_value(v[2].clone())?;
return Ok(Self::NegMsg {
subscription_id: Cow::Owned(subscription_id),
message: Cow::Owned(message),
});
} else {
return Err(MessageHandleError::InvalidMessageFormat);
}
}
if v[0] == "NEG-CLOSE" {
if v_len >= 2 {
let subscription_id: SubscriptionId = serde_json::from_value(v[1].clone())?;
return Ok(Self::NegClose {
subscription_id: Cow::Owned(subscription_id),
});
} else {
return Err(MessageHandleError::InvalidMessageFormat);
}
}
Err(MessageHandleError::InvalidMessageFormat)
}
}
impl Serialize for ClientMessage<'_> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let json_value: Value = self.as_value();
json_value.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for ClientMessage<'_> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let json_value = Value::deserialize(deserializer)?;
ClientMessage::from_value(json_value).map_err(serde::de::Error::custom)
}
}
impl JsonUtil for ClientMessage<'_> {
type Err = MessageHandleError;
fn from_json<T>(json: T) -> Result<Self, Self::Err>
where
T: AsRef<[u8]>,
{
let msg: &[u8] = json.as_ref();
if msg.is_empty() {
return Err(MessageHandleError::InvalidMessageFormat);
}
let value: Value = serde_json::from_slice(msg)?;
Self::from_value(value)
}
}
#[cfg(test)]
mod tests {
use core::str::FromStr;
use super::*;
use crate::{Kind, PublicKey};
#[test]
fn test_client_message_req() {
let pk =
PublicKey::from_str("379e863e8357163b5bce5d2688dc4f1dcc2d505222fb8d74db600f30535dfdfe")
.unwrap();
let client_req = ClientMessage::req(SubscriptionId::new("test"), Filter::new().pubkey(pk));
assert_eq!(
client_req.as_json(),
r##"["REQ","test",{"#p":["379e863e8357163b5bce5d2688dc4f1dcc2d505222fb8d74db600f30535dfdfe"]}]"##
);
}
#[test]
fn test_client_message_custom_kind() {
let client_req = ClientMessage::req(
SubscriptionId::new("test"),
Filter::new().kind(Kind::Custom(22)),
);
assert_eq!(client_req.as_json(), r##"["REQ","test",{"kinds":[22]}]"##);
}
}