pub mod frames;
pub mod parser;
pub mod actions;
pub use self::actions::{Action, VarScope};
pub mod frame;
pub use self::frame::{FrameFlags, FramePayload, FrameType, Metadata};
pub mod types;
pub use self::types::TypedData;
pub mod varint;
pub use self::varint::{decode_varint, encode_varint};
pub mod codec;
pub use self::codec::SpopCodec;
pub use semver::Version;
use std::borrow::Cow;
pub trait SpopFrame: std::fmt::Debug + Send {
fn frame_type(&self) -> &FrameType;
fn metadata(&self) -> Cow<'_, Metadata>;
fn payload(&self) -> FramePayload<'_>;
#[allow(clippy::cast_possible_truncation)]
fn serialize(&self) -> std::io::Result<Vec<u8>> {
let metadata = self.metadata();
let payload = self.payload();
let frame_len = 1 + metadata.serialized_len() + payload_serialized_len(&payload)?;
let mut serialized = Vec::with_capacity(4 + frame_len);
serialized.extend_from_slice(&(frame_len as u32).to_be_bytes());
serialized.push(self.frame_type().to_u8());
metadata.write_to(&mut serialized);
encode_payload(&payload, &mut serialized)?;
Ok(serialized)
}
}
impl<'a, T: SpopFrame + Sized + 'a> From<T> for Box<dyn SpopFrame + 'a> {
fn from(value: T) -> Self {
Box::new(value)
}
}
#[allow(clippy::cast_possible_truncation)]
fn encode_payload(payload: &FramePayload, buf: &mut Vec<u8>) -> std::io::Result<()> {
match payload {
FramePayload::ListOfActions(actions) => {
for action in *actions {
match action {
Action::SetVar { scope, name, value } => {
buf.push(0x01);
buf.push(0x03);
buf.push(scope.to_u8());
varint::encode_varint_into(name.len() as u64, buf);
buf.extend_from_slice(name.as_bytes());
value.to_bytes(buf);
}
Action::UnSetVar { scope, name } => {
buf.push(0x02);
buf.push(0x02);
buf.push(scope.to_u8());
varint::encode_varint_into(name.len() as u64, buf);
buf.extend_from_slice(name.as_bytes());
}
}
}
}
FramePayload::KVList(kv_pairs) => {
for (key, value) in kv_pairs {
varint::encode_varint_into(key.len() as u64, buf);
buf.extend_from_slice(key.as_bytes());
match value {
TypedData::String(val) => {
buf.push(0x08);
varint::encode_varint_into(val.len() as u64, buf);
buf.extend_from_slice(val.as_bytes());
}
TypedData::UInt32(val) => {
buf.push(0x03);
varint::encode_varint_into(u64::from(*val), buf);
}
_ => {}
}
}
}
FramePayload::ListOfMessages(_) => {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"Unsupported frame payload type",
));
}
}
Ok(())
}
fn payload_serialized_len(payload: &FramePayload) -> std::io::Result<usize> {
match payload {
FramePayload::ListOfActions(actions) => Ok(actions
.iter()
.map(|action| match action {
Action::SetVar { name, value, .. } => {
3 + varint::varint_len(name.len() as u64) + name.len() + value.serialized_len()
}
Action::UnSetVar { name, .. } => {
3 + varint::varint_len(name.len() as u64) + name.len()
}
})
.sum()),
FramePayload::KVList(kv_pairs) => Ok(kv_pairs
.iter()
.map(|(key, value)| {
varint::varint_len(key.len() as u64)
+ key.len()
+ match value {
TypedData::String(val) => {
1 + varint::varint_len(val.len() as u64) + val.len()
}
TypedData::UInt32(val) => 1 + varint::varint_len(u64::from(*val)),
_ => 0,
}
})
.sum()),
FramePayload::ListOfMessages(_) => Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"Unsupported frame payload type",
)),
}
}