use serde::Serialize;
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize)]
pub enum UpdateType {
SyncCipherUpdate = 1,
SyncCipherCreate = 2,
SyncLoginDelete = 3,
SyncFolderDelete = 4,
SyncCiphers = 5,
SyncVault = 6,
SyncOrgKeys = 7,
SyncFolderCreate = 8,
SyncFolderUpdate = 9,
SyncCipherDelete = 10,
SyncSettings = 11,
SyncLogOut = 12,
SyncSendCreate = 13,
SyncSendUpdate = 14,
SyncSendDelete = 15,
AuthRequest = 16,
AuthRequestResponse = 17,
}
#[derive(Debug, Serialize)]
pub struct Notification {
#[serde(rename = "Type")]
pub update_type: UpdateType,
#[serde(rename = "Payload", skip_serializing_if = "Option::is_none")]
pub payload: Option<serde_json::Value>,
}
impl Notification {
#[must_use]
pub fn new(update_type: UpdateType, payload: Option<serde_json::Value>) -> Self {
Self {
update_type,
payload,
}
}
pub fn to_msgpack(&self) -> Result<Vec<u8>, rmp_serde::encode::Error> {
let payload_str = self
.payload
.as_ref()
.and_then(|v| serde_json::to_string(v).ok())
.unwrap_or_default();
rmp_serde::encode::to_vec_named(&(self.update_type as u8, payload_str))
}
}
pub fn serialize_msgpack<T: Serialize>(value: &T) -> Result<Vec<u8>, rmp_serde::encode::Error> {
rmp_serde::encode::to_vec_named(value)
}
#[must_use]
pub fn decode_varint(data: &[u8]) -> Option<(u64, usize)> {
let mut value: u64 = 0;
let mut shift = 0;
for (i, &byte) in data.iter().enumerate().take(5) {
value |= u64::from(byte & 0x7F) << shift;
if byte & 0x80 == 0 {
return Some((value, i + 1));
}
shift += 7;
if shift >= 35 {
return None;
}
}
None
}
#[must_use]
pub fn encode_varint(mut value: u32) -> Vec<u8> {
let mut buf = Vec::with_capacity(5);
loop {
let mut byte = (value & 0x7F) as u8;
value >>= 7;
if value != 0 {
byte |= 0x80;
}
buf.push(byte);
if value == 0 {
break;
}
}
buf
}
#[must_use]
pub fn decode_signalr_frame(data: &[u8]) -> Option<(u8, &[u8])> {
let (len, consumed) = decode_varint(data)?;
let len = usize::try_from(len).ok()?;
let payload = data.get(consumed..consumed + len)?;
let value: serde_json::Value = rmp_serde::from_slice(payload).ok()?;
let msg_type = u8::try_from(value.as_array()?.first()?.as_u64()?).ok()?;
Some((msg_type, payload))
}
pub fn encode_signalr_notification(notif: &Notification) -> Result<Vec<u8>, String> {
let payload_json = notif
.payload
.as_ref()
.and_then(|v| serde_json::to_string(v).ok())
.unwrap_or_default();
let target = match notif.update_type {
UpdateType::SyncCipherCreate => "SyncCipherCreate",
UpdateType::SyncCipherUpdate => "SyncCipherUpdate",
UpdateType::SyncLoginDelete => "SyncLoginDelete",
UpdateType::SyncFolderDelete => "SyncFolderDelete",
UpdateType::SyncCiphers => "SyncCiphers",
UpdateType::SyncVault => "SyncVault",
UpdateType::SyncOrgKeys => "SyncOrgKeys",
UpdateType::SyncFolderCreate => "SyncFolderCreate",
UpdateType::SyncFolderUpdate => "SyncFolderUpdate",
UpdateType::SyncCipherDelete => "SyncCipherDelete",
UpdateType::SyncSettings => "SyncSettings",
UpdateType::SyncLogOut => "SyncLogOut",
UpdateType::SyncSendCreate => "SyncSendCreate",
UpdateType::SyncSendUpdate => "SyncSendUpdate",
UpdateType::SyncSendDelete => "SyncSendDelete",
UpdateType::AuthRequest => "AuthRequest",
UpdateType::AuthRequestResponse => "AuthRequestResponse",
};
let invocation = rmp_serde::encode::to_vec_named(&(
1u8,
serde_json::json!({
"Target": target,
"Arguments": [payload_json],
}),
))
.map_err(|e| e.to_string())?;
let frame_len = u32::try_from(invocation.len()).map_err(|e| e.to_string())?;
let mut frame = encode_varint(frame_len);
frame.extend_from_slice(&invocation);
Ok(frame)
}