#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum CodecId {
MessagePack = 1,
}
impl CodecId {
pub const fn as_u8(self) -> u8 {
self as u8
}
pub fn from_u8(value: u8) -> Option<Self> {
match value {
1 => Some(CodecId::MessagePack),
_ => None,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct EncodingMetadata {
pub codec: u8,
}
impl EncodingMetadata {
pub fn codec_id(&self) -> Option<CodecId> {
CodecId::from_u8(self.codec)
}
}
pub fn encoding_string(codec: CodecId) -> String {
format!("phoxal/v0;codec={}", codec.as_u8())
}
pub fn parse_encoding_string(value: &str) -> std::result::Result<EncodingMetadata, String> {
let mut parts = value.split(';');
let prefix = parts
.next()
.ok_or_else(|| "encoding string is empty".to_string())?;
if prefix != "phoxal/v0" {
return Err(format!(
"expected encoding prefix 'phoxal/v0', got '{prefix}'"
));
}
let mut codec = None;
for field in parts {
let (key, value) = field
.split_once('=')
.ok_or_else(|| format!("encoding field '{field}' is missing '='"))?;
if value.is_empty() {
return Err(format!("encoding field '{key}' is empty"));
}
match key {
"codec" => {
let parsed = value
.parse::<u8>()
.map_err(|_| format!("encoding field 'codec' is not a u8: '{value}'"))?;
set_once(&mut codec, key, parsed)?;
}
_ => return Err(format!("unknown encoding field '{key}'")),
}
}
Ok(EncodingMetadata {
codec: codec.ok_or_else(|| "encoding string is missing codec".to_string())?,
})
}
fn set_once<T>(slot: &mut Option<T>, key: &str, value: T) -> std::result::Result<(), String> {
if slot.replace(value).is_some() {
Err(format!("duplicate encoding field '{key}'"))
} else {
Ok(())
}
}