use std::{error, fmt, mem::size_of};
pub type ProviderId = u16;
pub type ResourceId = u32;
pub type RequestId = u32;
const OPCODE_SIZE: usize = 1;
pub const MAX_RESOURCE_READ_SIZE: usize =
crate::v4::MAX_PACKET_SIZE - ResourceResponse::max_overhead() - OPCODE_SIZE;
#[derive(Debug)]
pub enum ParseError {
MissingData,
InvalidEnumVariant(u8),
}
impl error::Error for ParseError {}
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ParseError::MissingData => write!(f, "Missing data"),
ParseError::InvalidEnumVariant(v) => write!(f, "Invalid enum variant ({v})"),
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct ResourceResponse {
pub request_id: u32,
pub part: u8,
pub total_parts: u8,
pub result: GetResourceResult,
}
impl ResourceResponse {
pub fn parse(buf: &[u8]) -> Result<Self, ParseError> {
if buf.len() < Self::max_overhead() {
return Err(ParseError::MissingData);
}
let request_id = u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]);
let part = buf[4];
let total_parts = buf[5];
let result = GetResourceResult::parse(&buf[6..])?;
Ok(Self {
request_id,
part,
total_parts,
result,
})
}
pub fn serialize(&self) -> Vec<u8> {
[
self.request_id.to_le_bytes().as_slice(),
&[self.part],
&[self.total_parts],
self.result.serialize().as_slice(),
]
.concat()
}
pub const fn max_overhead() -> usize {
size_of::<u32>() + size_of::<u8>() * 3
}
pub fn header_success(
request_id: u32,
part: u8,
total_parts: u8,
) -> [u8; Self::max_overhead()] {
let id = request_id.to_le_bytes();
[
id[0],
id[1],
id[2],
id[3],
part,
total_parts,
GetResourceResult::success_tag(),
]
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum GetResourceResult {
NotFound,
Success(Vec<u8>),
}
impl GetResourceResult {
pub fn parse(buf: &[u8]) -> Result<Self, ParseError> {
if buf.is_empty() {
return Err(ParseError::MissingData);
}
match buf[0] {
0x00 => Ok(Self::NotFound),
0x01 => Ok(Self::Success(buf[1..].to_vec())),
v => Err(ParseError::InvalidEnumVariant(v)),
}
}
pub fn serialize(&self) -> Vec<u8> {
match self {
GetResourceResult::NotFound => vec![0x00],
GetResourceResult::Success(buf) => [&[Self::success_tag()], buf.as_slice()].concat(),
}
}
const fn success_tag() -> u8 {
0x01
}
}
pub fn create_url(provider_id: u16, resource_id: u32) -> String {
format!("fcomp://{provider_id}.fcast/{resource_id}")
}
#[derive(Default)]
pub struct RequestIdGenerator(RequestId);
impl RequestIdGenerator {
pub fn next(&mut self) -> RequestId {
self.0 += 1;
self.0 - 1
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn resource_response() {
let inp = ResourceResponse {
request_id: 123,
result: GetResourceResult::NotFound,
part: 1,
total_parts: 2,
};
assert_eq!(ResourceResponse::parse(&inp.serialize()).unwrap(), inp,);
let inp = ResourceResponse {
request_id: 123,
result: GetResourceResult::Success(vec![1, 2, 3, 4]),
part: 1,
total_parts: 2,
};
assert_eq!(ResourceResponse::parse(&inp.serialize()).unwrap(), inp,);
}
#[test]
fn get_resource_result() {
assert_eq!(
GetResourceResult::parse(&[0x00]).unwrap(),
GetResourceResult::NotFound,
);
assert_eq!(
GetResourceResult::parse(&[0x01, 1, 2, 3]).unwrap(),
GetResourceResult::Success(vec![1, 2, 3]),
);
assert_eq!(
GetResourceResult::parse(&[0x01]).unwrap(),
GetResourceResult::Success(vec![]),
);
}
}