knx_pico/addressing/
group.rs1use crate::error::{KnxError, Result};
14use core::fmt;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
44#[cfg_attr(feature = "defmt", derive(defmt::Format))]
45pub struct GroupAddress {
46 raw: u16,
47}
48
49impl GroupAddress {
50 pub const MAX_MAIN: u8 = 31;
52 pub const MAX_MIDDLE: u8 = 7;
54 pub const MAX_SUB: u8 = 255;
56 pub const MAX_SUB_2LEVEL: u16 = 2047;
58
59 pub fn new(main: u8, middle: u8, sub: u8) -> Result<Self> {
71 if main > Self::MAX_MAIN {
72 return Err(KnxError::address_out_of_range());
73 }
74 if middle > Self::MAX_MIDDLE {
75 return Err(KnxError::address_out_of_range());
76 }
77 let raw = (u16::from(main) << 11) | (u16::from(middle) << 8) | u16::from(sub);
80 Ok(Self { raw })
81 }
82
83 pub fn new_2level(main: u8, sub: u16) -> Result<Self> {
94 if main > Self::MAX_MAIN {
95 return Err(KnxError::address_out_of_range());
96 }
97 if sub > Self::MAX_SUB_2LEVEL {
98 return Err(KnxError::address_out_of_range());
99 }
100
101 let raw = (u16::from(main) << 11) | sub;
102 Ok(Self { raw })
103 }
104
105 pub fn from_array(parts: [u8; 3]) -> Result<Self> {
119 Self::new(parts[0], parts[1], parts[2])
120 }
121
122 #[inline(always)]
124 pub const fn raw(self) -> u16 {
125 self.raw
126 }
127
128 #[inline(always)]
130 pub const fn main(self) -> u8 {
131 ((self.raw >> 11) & 0x1F) as u8
132 }
133
134 #[inline(always)]
136 pub const fn middle(self) -> u8 {
137 ((self.raw >> 8) & 0x07) as u8
138 }
139
140 #[inline(always)]
142 pub const fn sub(self) -> u8 {
143 (self.raw & 0xFF) as u8
144 }
145
146 #[inline(always)]
148 pub const fn sub_2level(self) -> u16 {
149 self.raw & 0x07FF
150 }
151
152 pub fn to_string_3level(&self) -> heapless::String<16> {
159 use core::fmt::Write;
160 let mut s = heapless::String::new();
161 let _ = write!(s, "{}/{}/{}", self.main(), self.middle(), self.sub());
163 s
164 }
165
166 pub fn to_string_2level(&self) -> heapless::String<16> {
173 use core::fmt::Write;
174 let mut s = heapless::String::new();
175 let _ = write!(s, "{}/{}", self.main(), self.sub_2level());
177 s
178 }
179
180 #[inline]
190 pub fn encode(&self, buf: &mut [u8]) -> Result<usize> {
191 if buf.len() < 2 {
192 return Err(KnxError::buffer_too_small());
193 }
194 buf[0..2].copy_from_slice(&self.raw.to_be_bytes());
195 Ok(2)
196 }
197
198 #[inline]
208 pub fn decode(buf: &[u8]) -> Result<Self> {
209 if buf.len() < 2 {
210 return Err(KnxError::buffer_too_small());
211 }
212 let raw = u16::from_be_bytes([buf[0], buf[1]]);
213 Ok(Self { raw })
214 }
215}
216
217impl From<u16> for GroupAddress {
218 #[inline(always)]
219 fn from(raw: u16) -> Self {
220 Self { raw }
221 }
222}
223
224impl From<GroupAddress> for u16 {
225 #[inline(always)]
226 fn from(addr: GroupAddress) -> u16 {
227 addr.raw
228 }
229}
230
231impl fmt::Display for GroupAddress {
232 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
234 write!(f, "{}/{}/{}", self.main(), self.middle(), self.sub())
235 }
236}
237
238impl core::str::FromStr for GroupAddress {
239 type Err = KnxError;
240
241 fn from_str(s: &str) -> Result<Self> {
242 let mut parts = s.split('/');
244
245 let main = parts
246 .next()
247 .and_then(|s| s.parse::<u8>().ok())
248 .ok_or_else(KnxError::invalid_group_address)?;
249
250 let middle = parts
251 .next()
252 .and_then(|s| s.parse::<u16>().ok())
253 .ok_or_else(KnxError::invalid_group_address)?;
254
255 if let Some(sub_str) = parts.next() {
257 let sub = sub_str
259 .parse::<u8>()
260 .ok()
261 .ok_or_else(KnxError::invalid_group_address)?;
262
263 if parts.next().is_some() {
265 return Err(KnxError::invalid_group_address());
266 }
267
268 if middle > 255 {
270 return Err(KnxError::invalid_group_address());
271 }
272
273 Self::new(main, middle as u8, sub)
274 } else {
275 if parts.next().is_some() {
279 return Err(KnxError::invalid_group_address());
280 }
281
282 Self::new_2level(main, middle)
283 }
284 }
285}
286
287#[cfg(test)]
288mod tests {
289 use super::*;
290
291 #[test]
292 fn test_new_3level_valid() {
293 let addr = GroupAddress::new(1, 2, 3).unwrap();
294 assert_eq!(addr.main(), 1);
295 assert_eq!(addr.middle(), 2);
296 assert_eq!(addr.sub(), 3);
297 }
298
299 #[test]
300 fn test_new_3level_invalid_main() {
301 let result = GroupAddress::new(32, 0, 0);
302 assert!(result.is_err());
303 }
304
305 #[test]
306 fn test_new_3level_invalid_middle() {
307 let result = GroupAddress::new(0, 8, 0);
308 assert!(result.is_err());
309 }
310
311 #[test]
312 fn test_new_2level_valid() {
313 let addr = GroupAddress::new_2level(1, 234).unwrap();
314 assert_eq!(addr.main(), 1);
315 assert_eq!(addr.sub_2level(), 234);
316 }
317
318 #[test]
319 fn test_new_2level_invalid() {
320 let result = GroupAddress::new_2level(0, 2048);
321 assert!(result.is_err());
322 }
323
324 #[test]
325 fn test_from_raw() {
326 let addr = GroupAddress::from(0x0A03u16);
328 assert_eq!(addr.main(), 1);
329 assert_eq!(addr.middle(), 2);
330 assert_eq!(addr.sub(), 3);
331 }
332
333 #[test]
334 fn test_to_raw() {
335 let addr = GroupAddress::new(1, 2, 3).unwrap();
336 assert_eq!(u16::from(addr), 0x0A03);
337 }
338
339 #[test]
340 fn test_encode_decode() {
341 let addr = GroupAddress::new(31, 7, 255).unwrap();
342 let mut buf = [0u8; 2];
343 addr.encode(&mut buf).unwrap();
344 let decoded = GroupAddress::decode(&buf).unwrap();
345 assert_eq!(addr, decoded);
346 }
347
348 #[test]
349 fn test_display_3level() {
350 let addr = GroupAddress::new(1, 2, 3).unwrap();
351 assert_eq!(format!("{}", addr), "1/2/3");
352 }
353
354 #[test]
355 fn test_to_string_2level() {
356 let addr = GroupAddress::new_2level(1, 234).unwrap();
357 assert_eq!(addr.to_string_2level(), "1/234");
358 }
359
360 #[test]
361 fn test_from_str_3level() {
362 let addr: GroupAddress = "1/2/3".parse().unwrap();
363 assert_eq!(addr.main(), 1);
364 assert_eq!(addr.middle(), 2);
365 assert_eq!(addr.sub(), 3);
366 }
367
368 #[test]
369 fn test_from_str_2level() {
370 let addr: GroupAddress = "1/234".parse().unwrap();
371 assert_eq!(addr.main(), 1);
372 assert_eq!(addr.sub_2level(), 234);
373 }
374
375 #[test]
376 fn test_from_str_invalid() {
377 let result = "1".parse::<GroupAddress>();
379 assert!(result.is_err());
380
381 let result = "32/0/0".parse::<GroupAddress>();
383 assert!(result.is_err());
384
385 let result = "1/2/3/4".parse::<GroupAddress>();
387 assert!(result.is_err());
388
389 let result = "a/b/c".parse::<GroupAddress>();
391 assert!(result.is_err());
392
393 let result = "".parse::<GroupAddress>();
395 assert!(result.is_err());
396
397 let result = "1/2048".parse::<GroupAddress>();
399 assert!(result.is_err());
400 }
401}