Skip to main content

fcast_protocol/
companion.rs

1use std::{error, fmt, mem::size_of};
2
3pub type ProviderId = u16;
4pub type ResourceId = u32;
5pub type RequestId = u32;
6
7const OPCODE_SIZE: usize = 1;
8pub const MAX_RESOURCE_READ_SIZE: usize =
9    crate::v4::MAX_PACKET_SIZE - ResourceResponse::max_overhead() - OPCODE_SIZE;
10
11#[derive(Debug)]
12pub enum ParseError {
13    MissingData,
14    InvalidEnumVariant(u8),
15}
16
17impl error::Error for ParseError {}
18
19impl fmt::Display for ParseError {
20    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21        match self {
22            ParseError::MissingData => write!(f, "Missing data"),
23            ParseError::InvalidEnumVariant(v) => write!(f, "Invalid enum variant ({v})"),
24        }
25    }
26}
27
28#[derive(Debug, PartialEq, Eq)]
29pub struct ResourceResponse {
30    pub request_id: u32,
31    pub part: u8,
32    pub total_parts: u8,
33    pub result: GetResourceResult,
34}
35
36impl ResourceResponse {
37    pub fn parse(buf: &[u8]) -> Result<Self, ParseError> {
38        if buf.len() < Self::max_overhead() {
39            return Err(ParseError::MissingData);
40        }
41
42        let request_id = u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]);
43        let part = buf[4];
44        let total_parts = buf[5];
45        let result = GetResourceResult::parse(&buf[6..])?;
46
47        Ok(Self {
48            request_id,
49            part,
50            total_parts,
51            result,
52        })
53    }
54
55    pub fn serialize(&self) -> Vec<u8> {
56        [
57            self.request_id.to_le_bytes().as_slice(),
58            &[self.part],
59            &[self.total_parts],
60            self.result.serialize().as_slice(),
61        ]
62        .concat()
63    }
64
65    pub const fn max_overhead() -> usize {
66        size_of::<u32>() + size_of::<u8>() * 3
67    }
68
69    pub fn header_success(
70        request_id: u32,
71        part: u8,
72        total_parts: u8,
73    ) -> [u8; Self::max_overhead()] {
74        let id = request_id.to_le_bytes();
75        [
76            id[0],
77            id[1],
78            id[2],
79            id[3],
80            part,
81            total_parts,
82            GetResourceResult::success_tag(),
83        ]
84    }
85}
86
87#[derive(Debug, PartialEq, Eq)]
88pub enum GetResourceResult {
89    NotFound,
90    Success(Vec<u8>),
91}
92
93impl GetResourceResult {
94    pub fn parse(buf: &[u8]) -> Result<Self, ParseError> {
95        if buf.is_empty() {
96            return Err(ParseError::MissingData);
97        }
98
99        match buf[0] {
100            0x00 => Ok(Self::NotFound),
101            0x01 => Ok(Self::Success(buf[1..].to_vec())),
102            v => Err(ParseError::InvalidEnumVariant(v)),
103        }
104    }
105
106    pub fn serialize(&self) -> Vec<u8> {
107        match self {
108            GetResourceResult::NotFound => vec![0x00],
109            GetResourceResult::Success(buf) => [&[Self::success_tag()], buf.as_slice()].concat(),
110        }
111    }
112
113    const fn success_tag() -> u8 {
114        0x01
115    }
116}
117
118pub fn create_url(provider_id: u16, resource_id: u32) -> String {
119    format!("fcomp://{provider_id}.fcast/{resource_id}")
120}
121
122#[derive(Default)]
123pub struct RequestIdGenerator(RequestId);
124
125impl RequestIdGenerator {
126    pub fn next(&mut self) -> RequestId {
127        self.0 += 1;
128        self.0 - 1
129    }
130}
131
132#[cfg(test)]
133mod tests {
134    use super::*;
135
136    #[test]
137    fn resource_response() {
138        let inp = ResourceResponse {
139            request_id: 123,
140            result: GetResourceResult::NotFound,
141            part: 1,
142            total_parts: 2,
143        };
144        assert_eq!(ResourceResponse::parse(&inp.serialize()).unwrap(), inp,);
145        let inp = ResourceResponse {
146            request_id: 123,
147            result: GetResourceResult::Success(vec![1, 2, 3, 4]),
148            part: 1,
149            total_parts: 2,
150        };
151        assert_eq!(ResourceResponse::parse(&inp.serialize()).unwrap(), inp,);
152    }
153
154    #[test]
155    fn get_resource_result() {
156        assert_eq!(
157            GetResourceResult::parse(&[0x00]).unwrap(),
158            GetResourceResult::NotFound,
159        );
160        assert_eq!(
161            GetResourceResult::parse(&[0x01, 1, 2, 3]).unwrap(),
162            GetResourceResult::Success(vec![1, 2, 3]),
163        );
164        assert_eq!(
165            GetResourceResult::parse(&[0x01]).unwrap(),
166            GetResourceResult::Success(vec![]),
167        );
168    }
169}