1use core::fmt;
2use core::str::FromStr;
3
4use crate::{KnxError, Result};
5
6const IA_AREA_SHIFT: u16 = 12;
8const IA_LINE_SHIFT: u16 = 8;
9const IA_FIELD_MAX: u8 = 0x0f;
10const IA_NIBBLE_MASK: u16 = 0x0f;
11const IA_DEVICE_MASK: u16 = 0xff;
12
13const GA_MAIN_SHIFT: u16 = 11;
16const GA_MAIN_MAX: u8 = 0x1f;
17const GA_MAIN_MASK: u16 = 0x1f;
18const GA_MIDDLE_SHIFT: u16 = 8;
19const GA_MIDDLE_MAX: u8 = 0x07;
20const GA_MIDDLE_MASK: u16 = 0x07;
21const GA_SUB_MASK: u16 = 0xff;
22const GA_TWO_LEVEL_SUB_MAX: u16 = 0x07ff;
23const GA_TWO_LEVEL_SUB_MASK: u16 = 0x07ff;
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
32pub struct IndividualAddress(u16);
33
34impl IndividualAddress {
35 pub const fn new(area: u8, line: u8, device: u8) -> Result<Self> {
38 if area > IA_FIELD_MAX {
39 return Err(KnxError::InvalidAddress("individual area out of range"));
40 }
41 if line > IA_FIELD_MAX {
42 return Err(KnxError::InvalidAddress("individual line out of range"));
43 }
44
45 Ok(Self(
46 ((area as u16) << IA_AREA_SHIFT) | ((line as u16) << IA_LINE_SHIFT) | device as u16,
47 ))
48 }
49
50 pub const fn from_raw(raw: u16) -> Self {
56 Self(raw)
57 }
58
59 pub const fn raw(self) -> u16 {
62 self.0
63 }
64
65 pub const fn area(self) -> u8 {
67 ((self.0 >> IA_AREA_SHIFT) & IA_NIBBLE_MASK) as u8
68 }
69
70 pub const fn line(self) -> u8 {
72 ((self.0 >> IA_LINE_SHIFT) & IA_NIBBLE_MASK) as u8
73 }
74
75 pub const fn device(self) -> u8 {
77 (self.0 & IA_DEVICE_MASK) as u8
78 }
79}
80
81impl fmt::Display for IndividualAddress {
82 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83 write!(f, "{}.{}.{}", self.area(), self.line(), self.device())
84 }
85}
86
87impl FromStr for IndividualAddress {
88 type Err = KnxError;
89
90 fn from_str(value: &str) -> Result<Self> {
91 let mut parts = value.split('.');
92 let area = parse_part(parts.next(), "missing individual area")?;
93 let line = parse_part(parts.next(), "missing individual line")?;
94 let device = parse_part(parts.next(), "missing individual device")?;
95
96 if parts.next().is_some() {
97 return Err(KnxError::InvalidAddress("too many individual parts"));
98 }
99
100 Self::new(area, line, device)
101 }
102}
103
104#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
114pub struct GroupAddress(u16);
115
116impl GroupAddress {
117 pub const fn new_three_level(main: u8, middle: u8, sub: u8) -> Result<Self> {
120 if main > GA_MAIN_MAX {
121 return Err(KnxError::InvalidAddress("group main out of range"));
122 }
123 if middle > GA_MIDDLE_MAX {
124 return Err(KnxError::InvalidAddress("group middle out of range"));
125 }
126
127 Ok(Self(
128 ((main as u16) << GA_MAIN_SHIFT) | ((middle as u16) << GA_MIDDLE_SHIFT) | sub as u16,
129 ))
130 }
131
132 pub const fn new_two_level(main: u8, sub: u16) -> Result<Self> {
141 if main > GA_MAIN_MAX {
142 return Err(KnxError::InvalidAddress("group main out of range"));
143 }
144 if sub > GA_TWO_LEVEL_SUB_MAX {
145 return Err(KnxError::InvalidAddress("group sub out of range"));
146 }
147
148 Ok(Self(((main as u16) << GA_MAIN_SHIFT) | sub))
149 }
150
151 pub const fn from_raw(raw: u16) -> Self {
158 Self(raw)
159 }
160
161 pub const fn raw(self) -> u16 {
164 self.0
165 }
166
167 pub const fn main(self) -> u8 {
170 ((self.0 >> GA_MAIN_SHIFT) & GA_MAIN_MASK) as u8
171 }
172
173 pub const fn middle(self) -> u8 {
175 ((self.0 >> GA_MIDDLE_SHIFT) & GA_MIDDLE_MASK) as u8
176 }
177
178 pub const fn sub(self) -> u8 {
180 (self.0 & GA_SUB_MASK) as u8
181 }
182
183 pub const fn two_level_sub(self) -> u16 {
186 self.0 & GA_TWO_LEVEL_SUB_MASK
187 }
188
189 pub fn parse_two_level(value: &str) -> Result<Self> {
196 let mut parts = value.split('/');
197 let main = parse_part(parts.next(), "missing group main")?;
198 let sub = parse_part(parts.next(), "missing group sub")?;
199
200 if parts.next().is_some() {
201 return Err(KnxError::InvalidAddress("too many group parts"));
202 }
203
204 Self::new_two_level(main, sub)
205 }
206
207 pub fn to_two_level_display(self) -> TwoLevelGroupAddressDisplay {
213 TwoLevelGroupAddressDisplay(self)
214 }
215}
216
217impl fmt::Display for GroupAddress {
218 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
219 write!(f, "{}/{}/{}", self.main(), self.middle(), self.sub())
220 }
221}
222
223impl FromStr for GroupAddress {
224 type Err = KnxError;
225
226 fn from_str(value: &str) -> Result<Self> {
227 let mut parts = value.split('/');
228 let main = parse_part(parts.next(), "missing group main")?;
229 let middle = parse_part(parts.next(), "missing group middle")?;
230 let sub = parse_part(parts.next(), "missing group sub")?;
231
232 if parts.next().is_some() {
233 return Err(KnxError::InvalidAddress("too many group parts"));
234 }
235
236 Self::new_three_level(main, middle, sub)
237 }
238}
239
240#[derive(Debug, Clone, Copy, PartialEq, Eq)]
247pub struct TwoLevelGroupAddressDisplay(GroupAddress);
248
249impl fmt::Display for TwoLevelGroupAddressDisplay {
250 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
251 write!(f, "{}/{}", self.0.main(), self.0.two_level_sub())
252 }
253}
254
255fn parse_part<T: core::str::FromStr>(value: Option<&str>, missing: &'static str) -> Result<T> {
256 let value = value.ok_or(KnxError::InvalidAddress(missing))?;
257 value.parse().map_err(|_| KnxError::InvalidAddress("invalid numeric address part"))
258}