1use std::{fmt, num::ParseIntError, str::FromStr};
7
8pub type SlaveId = u8;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
13pub struct Slave(pub SlaveId);
14
15impl Slave {
16 #[must_use]
24 pub const fn broadcast() -> Self {
25 Slave(0)
26 }
27
28 #[must_use]
30 pub const fn min_device() -> Self {
31 Slave(1)
32 }
33
34 #[must_use]
36 pub const fn max_device() -> Self {
37 Slave(247)
38 }
39
40 #[must_use]
48 pub const fn tcp_device() -> Self {
49 Slave(255)
50 }
51
52 #[must_use]
54 pub fn is_broadcast(self) -> bool {
55 self == Self::broadcast()
56 }
57
58 #[must_use]
60 pub fn is_single_device(self) -> bool {
61 self >= Self::min_device() && self <= Self::max_device()
62 }
63
64 #[must_use]
66 pub fn is_reserved(self) -> bool {
67 self > Self::max_device()
68 }
69}
70
71impl From<SlaveId> for Slave {
72 fn from(from: SlaveId) -> Self {
73 Slave(from)
74 }
75}
76
77impl From<Slave> for SlaveId {
78 fn from(from: Slave) -> Self {
79 from.0
80 }
81}
82
83impl FromStr for Slave {
84 type Err = ParseIntError;
85
86 fn from_str(s: &str) -> Result<Self, Self::Err> {
87 let slave_id = match s.parse::<u8>() {
88 Ok(slave_id) => Ok(slave_id),
89 Err(err) => {
90 if let Some(stripped) = s.strip_prefix("0x") {
91 u8::from_str_radix(stripped, 16)
92 } else {
93 Err(err)
94 }
95 }
96 }?;
97 Ok(Slave(slave_id))
98 }
99}
100
101impl fmt::Display for Slave {
102 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103 write!(f, "{} (0x{:0>2X})", self.0, self.0)
104 }
105}
106
107pub trait SlaveContext {
119 fn set_slave(&mut self, slave: Slave);
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126
127 #[test]
128 fn parse_dec() {
129 assert_eq!(Slave(0), Slave::from_str("0").unwrap());
130 assert_eq!(Slave(123), Slave::from_str("123").unwrap());
131 assert_eq!(Slave(255), Slave::from_str("255").unwrap());
132 assert!(Slave::from_str("-1").is_err());
133 assert!(Slave::from_str("256").is_err());
134 }
135
136 #[test]
137 fn parse_hex() {
138 assert_eq!(Slave(0), Slave::from_str("0x00").unwrap());
139 assert_eq!(Slave(123), Slave::from_str("0x7b").unwrap());
140 assert_eq!(Slave(123), Slave::from_str("0x7B").unwrap());
141 assert_eq!(Slave(255), Slave::from_str("0xff").unwrap());
142 assert_eq!(Slave(255), Slave::from_str("0xFF").unwrap());
143 assert!(Slave::from_str("0X00").is_err());
144 assert!(Slave::from_str("0x100").is_err());
145 assert!(Slave::from_str("0xfff").is_err());
146 assert!(Slave::from_str("0xFFF").is_err());
147 }
148
149 #[test]
150 fn format() {
151 assert!(format!("{}", Slave(123)).contains("123"));
152 assert!(format!("{}", Slave(0x7B)).contains("0x7B"));
153 assert!(!format!("{}", Slave(0x7B)).contains("0x7b"));
154 }
155}