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#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
26#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
27pub struct IndividualAddress(u16);
28
29impl IndividualAddress {
30 pub const fn new(area: u8, line: u8, device: u8) -> Result<Self> {
31 if area > IA_FIELD_MAX {
32 return Err(KnxError::InvalidAddress("individual area out of range"));
33 }
34 if line > IA_FIELD_MAX {
35 return Err(KnxError::InvalidAddress("individual line out of range"));
36 }
37
38 Ok(Self(
39 ((area as u16) << IA_AREA_SHIFT) | ((line as u16) << IA_LINE_SHIFT) | device as u16,
40 ))
41 }
42
43 pub const fn from_raw(raw: u16) -> Self {
47 Self(raw)
48 }
49
50 pub const fn raw(self) -> u16 {
51 self.0
52 }
53
54 pub const fn area(self) -> u8 {
55 ((self.0 >> IA_AREA_SHIFT) & IA_NIBBLE_MASK) as u8
56 }
57
58 pub const fn line(self) -> u8 {
59 ((self.0 >> IA_LINE_SHIFT) & IA_NIBBLE_MASK) as u8
60 }
61
62 pub const fn device(self) -> u8 {
63 (self.0 & IA_DEVICE_MASK) as u8
64 }
65}
66
67impl fmt::Display for IndividualAddress {
68 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69 write!(f, "{}.{}.{}", self.area(), self.line(), self.device())
70 }
71}
72
73impl FromStr for IndividualAddress {
74 type Err = KnxError;
75
76 fn from_str(value: &str) -> Result<Self> {
77 let mut parts = value.split('.');
78 let area = parse_part(parts.next(), "missing individual area")?;
79 let line = parse_part(parts.next(), "missing individual line")?;
80 let device = parse_part(parts.next(), "missing individual device")?;
81
82 if parts.next().is_some() {
83 return Err(KnxError::InvalidAddress("too many individual parts"));
84 }
85
86 Self::new(area, line, device)
87 }
88}
89
90#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
91#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
92pub struct GroupAddress(u16);
93
94impl GroupAddress {
95 pub const fn new_three_level(main: u8, middle: u8, sub: u8) -> Result<Self> {
96 if main > GA_MAIN_MAX {
97 return Err(KnxError::InvalidAddress("group main out of range"));
98 }
99 if middle > GA_MIDDLE_MAX {
100 return Err(KnxError::InvalidAddress("group middle out of range"));
101 }
102
103 Ok(Self(
104 ((main as u16) << GA_MAIN_SHIFT) | ((middle as u16) << GA_MIDDLE_SHIFT) | sub as u16,
105 ))
106 }
107
108 pub const fn new_two_level(main: u8, sub: u16) -> Result<Self> {
109 if main > GA_MAIN_MAX {
110 return Err(KnxError::InvalidAddress("group main out of range"));
111 }
112 if sub > GA_TWO_LEVEL_SUB_MAX {
113 return Err(KnxError::InvalidAddress("group sub out of range"));
114 }
115
116 Ok(Self(((main as u16) << GA_MAIN_SHIFT) | sub))
117 }
118
119 pub const fn from_raw(raw: u16) -> Self {
124 Self(raw)
125 }
126
127 pub const fn raw(self) -> u16 {
128 self.0
129 }
130
131 pub const fn main(self) -> u8 {
132 ((self.0 >> GA_MAIN_SHIFT) & GA_MAIN_MASK) as u8
133 }
134
135 pub const fn middle(self) -> u8 {
136 ((self.0 >> GA_MIDDLE_SHIFT) & GA_MIDDLE_MASK) as u8
137 }
138
139 pub const fn sub(self) -> u8 {
140 (self.0 & GA_SUB_MASK) as u8
141 }
142
143 pub const fn two_level_sub(self) -> u16 {
144 self.0 & GA_TWO_LEVEL_SUB_MASK
145 }
146
147 pub fn parse_two_level(value: &str) -> Result<Self> {
148 let mut parts = value.split('/');
149 let main = parse_part(parts.next(), "missing group main")?;
150 let sub = parse_part(parts.next(), "missing group sub")?;
151
152 if parts.next().is_some() {
153 return Err(KnxError::InvalidAddress("too many group parts"));
154 }
155
156 Self::new_two_level(main, sub)
157 }
158
159 pub fn to_two_level_display(self) -> TwoLevelGroupAddressDisplay {
160 TwoLevelGroupAddressDisplay(self)
161 }
162
163 #[cfg(feature = "std")]
164 pub fn to_two_level_string(self) -> std::string::String {
165 use std::string::ToString;
166
167 self.to_two_level_display().to_string()
168 }
169}
170
171impl fmt::Display for GroupAddress {
172 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173 write!(f, "{}/{}/{}", self.main(), self.middle(), self.sub())
174 }
175}
176
177impl FromStr for GroupAddress {
178 type Err = KnxError;
179
180 fn from_str(value: &str) -> Result<Self> {
181 let mut parts = value.split('/');
182 let main = parse_part(parts.next(), "missing group main")?;
183 let middle = parse_part(parts.next(), "missing group middle")?;
184 let sub = parse_part(parts.next(), "missing group sub")?;
185
186 if parts.next().is_some() {
187 return Err(KnxError::InvalidAddress("too many group parts"));
188 }
189
190 Self::new_three_level(main, middle, sub)
191 }
192}
193
194#[derive(Debug, Clone, Copy, PartialEq, Eq)]
195pub struct TwoLevelGroupAddressDisplay(GroupAddress);
196
197impl fmt::Display for TwoLevelGroupAddressDisplay {
198 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
199 write!(f, "{}/{}", self.0.main(), self.0.two_level_sub())
200 }
201}
202
203fn parse_part<T: core::str::FromStr>(value: Option<&str>, missing: &'static str) -> Result<T> {
204 let value = value.ok_or(KnxError::InvalidAddress(missing))?;
205 value
206 .parse()
207 .map_err(|_| KnxError::InvalidAddress("invalid numeric address part"))
208}