use crate::{activity::Activity, BadResponseError, Error, Result};
use serde::Serialize;
use serde_json::{json, Value};
const MAX_PAYLOAD_SIZE: u32 = 2048;
#[derive(Debug, Clone, Serialize)]
pub struct Packet {
pub opcode: Opcode,
pub payload: Value,
}
impl Packet {
pub fn new(opcode: Opcode, payload: impl Into<Value>) -> Self {
Self {
opcode,
payload: payload.into(),
}
}
pub fn new_activity(activity: Option<&Activity>, nonce: Option<&str>) -> Self {
let activity = json!({
"cmd": "SET_ACTIVITY",
"args": {
"pid": std::process::id(),
"activity": activity
},
"nonce": nonce.map(|v| v.to_string()).unwrap_or_else(Self::generate_nonce)
});
Self::new(Opcode::Frame, activity)
}
pub fn generate_nonce() -> String {
uuid::Uuid::new_v4().to_string()
}
pub fn decode_header(header: &[u8; 8]) -> Result<(u32, u32)> {
let opcode = u32::from_le_bytes(
header[0..4]
.try_into()
.map_err(|_| Error::DecodeError("Invalid header length".to_string()))?,
);
let payload_len = u32::from_le_bytes(
header[4..8]
.try_into()
.map_err(|_| Error::DecodeError("Invalid payload length".to_string()))?,
);
if payload_len > MAX_PAYLOAD_SIZE {
return Err(Error::DecodeError(format!(
"Payload exceeds {} bytes: {}",
MAX_PAYLOAD_SIZE, payload_len
)));
}
Ok((opcode, payload_len))
}
pub fn filter(self) -> std::result::Result<Packet, BadResponseError> {
let get_err_message = |v: &Value| -> Option<String> {
let code = v.get("code")?;
let message = v.get("message")?;
Some(format!("({}) {}", code, message))
};
if self.payload.get("evt") == Some(&Value::from("ERROR")) {
if let Some(data) = self.payload.get("data") {
if let Some(message) = get_err_message(data) {
return Err(BadResponseError {
packet: self,
message,
});
}
}
}
if let Some(message) = get_err_message(&self.payload) {
return Err(BadResponseError {
packet: self,
message,
});
}
Ok(self)
}
}
impl std::fmt::Display for Packet {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{:#?}", self)
}
}
#[derive(Debug, Clone, Serialize)]
#[repr(u32)]
pub enum Opcode {
Handshake = 0,
Frame = 1,
Close = 2,
Ping = 3,
Pong = 4,
}
impl TryFrom<u32> for Opcode {
type Error = Error;
fn try_from(value: u32) -> Result<Self> {
match value {
0 => Ok(Opcode::Handshake),
1 => Ok(Opcode::Frame),
2 => Ok(Opcode::Close),
3 => Ok(Opcode::Ping),
4 => Ok(Opcode::Pong),
op => Err(Error::DecodeError(format!("Unknown opcode: {}", op))),
}
}
}
impl std::fmt::Display for Opcode {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Opcode::Handshake => write!(f, "Handshake"),
Opcode::Frame => write!(f, "Frame"),
Opcode::Close => write!(f, "Close"),
Opcode::Ping => write!(f, "Ping"),
Opcode::Pong => write!(f, "Pong"),
}
}
}
impl From<Opcode> for String {
fn from(opcode: Opcode) -> Self {
opcode.to_string()
}
}