fcast_protocol/
companion.rs1use 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}