1use anyhow::format_err;
6use core::fmt::{self, Display};
7
8use crate::frame::FrameParseError;
9use crate::{RfcommError, Role};
10
11#[derive(Clone, Copy, Hash, Eq, Debug, PartialEq)]
19pub struct DLCI(u8);
20
21impl DLCI {
22 pub const MUX_CONTROL_DLCI: DLCI = DLCI(0);
24 const MIN_USER_DLCI: DLCI = DLCI(2);
26 const MAX_USER_DLCI: DLCI = DLCI(61);
28
29 pub fn is_mux_control(&self) -> bool {
30 *self == Self::MUX_CONTROL_DLCI
31 }
32
33 pub fn is_user(&self) -> bool {
34 self.0 >= Self::MIN_USER_DLCI.0 && self.0 <= Self::MAX_USER_DLCI.0
35 }
36
37 pub fn validate(&self, role: Role) -> Result<(), RfcommError> {
45 if !self.is_user() {
46 return Err(RfcommError::InvalidDLCI(*self));
47 }
48
49 let valid_bit = match role {
50 Role::Responder => 0,
51 Role::Initiator => 1,
52 role => {
53 return Err(RfcommError::InvalidRole(role));
54 }
55 };
56
57 if self.0 % 2 == valid_bit { Ok(()) } else { Err(RfcommError::InvalidDLCI(*self)) }
58 }
59
60 pub fn initiator(&self, role: Role) -> Result<bool, RfcommError> {
64 if !self.is_user() {
65 return Err(RfcommError::InvalidDLCI(*self));
66 }
67
68 match role.opposite_role() {
72 Role::Responder => Ok(self.0 % 2 == 0),
73 Role::Initiator => Ok(self.0 % 2 == 1),
74 role => {
75 return Err(RfcommError::InvalidRole(role));
76 }
77 }
78 }
79}
80
81impl TryFrom<u8> for DLCI {
82 type Error = FrameParseError;
83
84 fn try_from(value: u8) -> Result<DLCI, Self::Error> {
85 if value != DLCI::MUX_CONTROL_DLCI.0
86 && (value < DLCI::MIN_USER_DLCI.0 || value > DLCI::MAX_USER_DLCI.0)
87 {
88 return Err(FrameParseError::InvalidDLCI(value));
89 }
90 Ok(DLCI(value))
91 }
92}
93
94impl From<DLCI> for u8 {
95 fn from(value: DLCI) -> u8 {
96 value.0
97 }
98}
99
100impl TryFrom<DLCI> for ServerChannel {
101 type Error = RfcommError;
102
103 fn try_from(dlci: DLCI) -> Result<ServerChannel, Self::Error> {
104 if !dlci.is_user() {
105 return Err(RfcommError::InvalidDLCI(dlci));
106 }
107
108 ServerChannel::try_from(dlci.0 >> 1)
110 }
111}
112
113impl Display for DLCI {
114 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
115 write!(formatter, "{}", self.0)
116 }
117}
118
119#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
128pub struct ServerChannel(u8);
129
130impl ServerChannel {
131 const MAX: ServerChannel = ServerChannel(30);
132 const MIN: ServerChannel = ServerChannel(1);
133
134 pub fn all() -> impl Iterator<Item = ServerChannel> {
136 (Self::MIN.0..=Self::MAX.0).map(|x| ServerChannel(x))
137 }
138
139 pub fn to_dlci(&self, role: Role) -> Result<DLCI, RfcommError> {
142 let direction_bit = match role {
143 Role::Initiator => 1,
144 Role::Responder => 0,
145 r => {
146 return Err(RfcommError::InvalidRole(r));
147 }
148 };
149
150 let v = (self.0 << 1) | direction_bit;
151 DLCI::try_from(v).map_err(RfcommError::from)
152 }
153}
154
155impl TryFrom<u8> for ServerChannel {
156 type Error = RfcommError;
157 fn try_from(src: u8) -> Result<ServerChannel, Self::Error> {
158 if src < Self::MIN.0 || src > Self::MAX.0 {
159 return Err(RfcommError::Other(format_err!("Out of range: {:?}", src).into()));
160 }
161 Ok(ServerChannel(src))
162 }
163}
164
165impl From<ServerChannel> for u8 {
166 fn from(value: ServerChannel) -> u8 {
167 value.0
168 }
169}
170
171impl Display for ServerChannel {
172 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
173 write!(formatter, "{}", self.0)
174 }
175}
176
177#[cfg(test)]
178mod tests {
179 use super::*;
180
181 use assert_matches::assert_matches;
182
183 #[test]
184 fn test_create_dlci() {
185 let v1 = 10;
186 let dlci = DLCI::try_from(v1);
187 let expected_sc1 = ServerChannel::try_from(5).unwrap();
188 assert!(dlci.is_ok());
189 assert_eq!(ServerChannel::try_from(dlci.unwrap()).unwrap(), expected_sc1);
190
191 let v2 = 0;
192 let dlci = DLCI::try_from(v2).unwrap();
193 assert_matches!(ServerChannel::try_from(dlci), Err(RfcommError::InvalidDLCI(_)));
194
195 let v3 = 2;
196 let dlci = DLCI::try_from(v3);
197 let expected_sc3 = ServerChannel::try_from(1).unwrap();
198 assert!(dlci.is_ok());
199 assert_eq!(ServerChannel::try_from(dlci.unwrap()).unwrap(), expected_sc3);
200
201 let v4 = 61;
202 let dlci = DLCI::try_from(v4);
203 let expected_sc4 = ServerChannel::try_from(30).unwrap();
204 assert!(dlci.is_ok());
205 assert_eq!(ServerChannel::try_from(dlci.unwrap()).unwrap(), expected_sc4);
206
207 let v5 = 1;
208 let dlci = DLCI::try_from(v5);
209 assert!(dlci.is_err());
210
211 let v6 = 62;
212 let dlci = DLCI::try_from(v6);
213 assert!(dlci.is_err());
214
215 let v7 = 63;
216 let dlci = DLCI::try_from(v7);
217 assert!(dlci.is_err());
218 }
219
220 #[test]
221 fn validate_dlci_as_initiator_role() {
222 let role = Role::Initiator;
223
224 let dlci = DLCI::MUX_CONTROL_DLCI;
225 assert_matches!(dlci.validate(role), Err(RfcommError::InvalidDLCI(_)));
226
227 let dlci = DLCI::MIN_USER_DLCI;
228 assert_matches!(dlci.validate(role), Err(RfcommError::InvalidDLCI(_)));
229
230 let dlci = DLCI::try_from(9).unwrap();
231 assert!(dlci.validate(role).is_ok());
232 }
233
234 #[test]
235 fn validate_dlci_as_responder_role() {
236 let role = Role::Responder;
237
238 let dlci = DLCI::MUX_CONTROL_DLCI;
239 assert_matches!(dlci.validate(role), Err(RfcommError::InvalidDLCI(_)));
240
241 let dlci = DLCI::try_from(7).unwrap();
242 assert_matches!(dlci.validate(role), Err(RfcommError::InvalidDLCI(_)));
243
244 let dlci = DLCI::try_from(10).unwrap();
245 assert!(dlci.validate(role).is_ok());
246 }
247
248 #[test]
249 fn validate_dlci_with_invalid_role_returns_error() {
250 let role = Role::Unassigned;
251 let dlci = DLCI::try_from(10).unwrap();
252 assert_matches!(dlci.validate(role), Err(RfcommError::InvalidRole(_)));
253
254 let role = Role::Negotiating;
255 let dlci = DLCI::try_from(11).unwrap();
256 assert_matches!(dlci.validate(role), Err(RfcommError::InvalidRole(_)));
257 }
258
259 #[test]
260 fn dlci_check_is_initiator() {
261 let dlci = DLCI::MUX_CONTROL_DLCI;
262 assert_matches!(dlci.initiator(Role::Initiator), Err(RfcommError::InvalidDLCI(_)));
263 assert_matches!(dlci.initiator(Role::Responder), Err(RfcommError::InvalidDLCI(_)));
264
265 let dlci = DLCI::try_from(20).unwrap();
266 assert_matches!(dlci.initiator(Role::Initiator), Ok(true));
267 assert_matches!(dlci.initiator(Role::Responder), Ok(false));
268
269 let dlci = DLCI::try_from(25).unwrap();
270 assert_matches!(dlci.initiator(Role::Initiator), Ok(false));
271 assert_matches!(dlci.initiator(Role::Responder), Ok(true));
272 }
273
274 #[test]
275 fn dlci_check_as_initiator_with_invalid_role_returns_error() {
276 let role = Role::Unassigned;
277 let dlci = DLCI::try_from(10).unwrap();
278 assert_matches!(dlci.initiator(role), Err(RfcommError::InvalidRole(_)));
279
280 let role = Role::Negotiating;
281 let dlci = DLCI::try_from(11).unwrap();
282 assert_matches!(dlci.initiator(role), Err(RfcommError::InvalidRole(_)));
283 }
284
285 #[test]
286 fn convert_server_channel_to_dlci_invalid_role() {
287 let invalid_role = Role::Unassigned;
288 let server_channel = ServerChannel::try_from(10).unwrap();
289 assert_matches!(server_channel.to_dlci(invalid_role), Err(_));
290
291 let invalid_role = Role::Negotiating;
292 let server_channel = ServerChannel::try_from(13).unwrap();
293 assert_matches!(server_channel.to_dlci(invalid_role), Err(_));
294 }
295
296 #[test]
297 fn convert_server_channel_to_dlci_success() {
298 let server_channel = ServerChannel::try_from(5).unwrap();
299 let expected_dlci = DLCI::try_from(11).unwrap();
300 assert_eq!(server_channel.to_dlci(Role::Initiator).unwrap(), expected_dlci);
301
302 let expected_dlci = DLCI::try_from(10).unwrap();
303 assert_eq!(server_channel.to_dlci(Role::Responder).unwrap(), expected_dlci);
304
305 let server_channel = ServerChannel::MIN;
306 let expected_dlci = DLCI::try_from(2).unwrap();
307 assert_eq!(server_channel.to_dlci(Role::Responder).unwrap(), expected_dlci);
308
309 let server_channel = ServerChannel::MAX;
310 let expected_dlci = DLCI::try_from(61).unwrap();
311 assert_eq!(server_channel.to_dlci(Role::Initiator).unwrap(), expected_dlci);
312 }
313
314 #[test]
315 fn server_channel_from_primitive() {
316 let normal = 10;
317 let sc = ServerChannel::try_from(normal);
318 assert!(sc.is_ok());
319
320 let invalid = 0;
321 let sc = ServerChannel::try_from(invalid);
322 assert_matches!(sc, Err(_));
323
324 let too_large = 31;
325 let sc = ServerChannel::try_from(too_large);
326 assert_matches!(sc, Err(_));
327
328 let u8_max = u8::MAX;
329 let sc = ServerChannel::try_from(u8_max);
330 assert_matches!(sc, Err(_));
331 }
332}