use std::fmt;
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct BusAbi {
id: &'static str,
}
impl BusAbi {
pub const fn id(&self) -> &'static str {
self.id
}
}
impl fmt::Debug for BusAbi {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "BusAbi({})", self.id)
}
}
impl fmt::Display for BusAbi {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.id)
}
}
pub const BUS_ABI: BusAbi = BusAbi {
id: "phoxal-bus/v0",
};
#[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 family: String,
pub api_version: String,
pub codec: u8,
}
impl EncodingMetadata {
pub fn codec_id(&self) -> Option<CodecId> {
CodecId::from_u8(self.codec)
}
}
pub fn encoding_string(family: &str, api_version: &str, codec: CodecId) -> String {
format!(
"phoxal/v0;family={family};api={api_version};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 family = None;
let mut api_version = None;
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 {
"family" => set_once(&mut family, key, value.to_string())?,
"api" => set_once(&mut api_version, key, value.to_string())?,
"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 {
family: family.ok_or_else(|| "encoding string is missing family".to_string())?,
api_version: api_version.ok_or_else(|| "encoding string is missing api".to_string())?,
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(())
}
}