lorawan/
string.rs

1use crate::keys::*;
2use crate::parser::*;
3
4#[cfg(feature = "with-to-string")]
5pub extern crate std;
6
7pub use hex::FromHexError;
8
9macro_rules! fixed_len_struct_impl_to_string_msb {
10    (
11        $type:ident,$size:expr;
12    ) => {
13        impl core::str::FromStr for $type {
14            type Err = FromHexError;
15
16            fn from_str(s: &str) -> Result<Self, Self::Err> {
17                let mut res = [0; $size];
18                hex::decode_to_slice(s.as_bytes(), &mut res)?;
19                Ok(Self::from(res))
20            }
21        }
22
23        #[cfg(feature = "with-to-string")]
24        impl core::fmt::Display for $type {
25            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
26                let mut res = std::string::String::with_capacity($size * 2);
27                res.extend(std::iter::repeat('-').take($size * 2));
28                let slice = unsafe { &mut res.as_bytes_mut() };
29                hex::encode_to_slice(self.as_ref(), slice).unwrap();
30                write!(f, "{}", res)
31            }
32        }
33    };
34    (
35        $type:ident[$size:expr];
36    ) => {
37        impl core::str::FromStr for $type<[u8; $size]> {
38            type Err = FromHexError;
39
40            fn from_str(s: &str) -> Result<Self, Self::Err> {
41                let mut res = [0; $size];
42                hex::decode_to_slice(s.as_bytes(), &mut res)?;
43                Ok(Self::from(res))
44            }
45        }
46
47        #[cfg(feature = "with-to-string")]
48        impl<T: AsRef<[u8]>> core::fmt::Display for $type<T> {
49            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
50                // initialize the return string with the correct size and fill it with '-'
51                let mut res = std::string::String::with_capacity($size * 2);
52                res.extend(std::iter::repeat('-').take($size * 2));
53
54                // get the string as bytes so that we can call hex::encode_to_slice directly
55                let slice = unsafe { &mut res.as_bytes_mut() };
56                hex::encode_to_slice(self.as_ref(), slice).unwrap();
57                write!(f, "{}", res)
58            }
59        }
60    };
61}
62
63macro_rules! fixed_len_struct_impl_string_lsb {
64    (
65        $type:ident,$size:expr;
66    ) => {
67        impl core::str::FromStr for $type {
68            type Err = FromHexError;
69
70            fn from_str(s: &str) -> Result<Self, Self::Err> {
71                let mut res = [0; $size];
72                hex::decode_to_slice(s.as_bytes(), &mut res)?;
73                res.reverse();
74                Ok(Self::from(res))
75            }
76        }
77
78        #[cfg(feature = "with-to-string")]
79        impl core::fmt::Display for $type {
80            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
81                let mut res = std::string::String::with_capacity($size * 2);
82                res.extend(std::iter::repeat('0').take($size * 2));
83                let slice = unsafe { &mut res.as_bytes_mut() };
84                self.as_ref().iter().rev().enumerate().for_each(|(i, b)| {
85                    hex::encode_to_slice(&[*b], &mut slice[i * 2..i * 2 + 2]).unwrap();
86                });
87                write!(f, "{}", res)
88            }
89        }
90    };
91}
92
93fixed_len_struct_impl_to_string_msb! {
94    EUI64[8];
95}
96
97fixed_len_struct_impl_to_string_msb! {
98    DevNonce[2];
99}
100
101fixed_len_struct_impl_to_string_msb! {
102    AppNonce[3];
103}
104
105fixed_len_struct_impl_to_string_msb! {
106    DevAddr[4];
107}
108
109fixed_len_struct_impl_to_string_msb! {
110    NwkAddr[3];
111}
112
113fixed_len_struct_impl_to_string_msb! {
114    AppKey, 16;
115}
116
117fixed_len_struct_impl_to_string_msb! {
118    NewSKey, 16;
119}
120
121fixed_len_struct_impl_to_string_msb! {
122    AppSKey, 16;
123}
124
125fixed_len_struct_impl_string_lsb! {
126    DevEui, 8;
127}
128
129fixed_len_struct_impl_string_lsb! {
130    AppEui, 8;
131}
132
133#[cfg(test)]
134mod test {
135    use super::*;
136    use crate::extra::std::string::ToString;
137    use core::str::FromStr;
138
139    #[test]
140    fn test_appskey_to_string() {
141        let appskey = AppSKey::from([
142            0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfd, 0xb9, 0x75, 0x31, 0x24, 0x68,
143            0xac, 0xed,
144        ]);
145        assert_eq!(appskey.to_string(), "0123456789abcdeffdb975312468aced");
146    }
147
148    #[test]
149    fn test_appskey_from_str() {
150        let appskey = AppSKey::from_str("00112233445566778899aabbccddeeff").unwrap();
151        assert_eq!(
152            appskey,
153            AppSKey::from([
154                0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD,
155                0xEE, 0xFF
156            ])
157        );
158    }
159
160    #[test]
161    fn test_deveui_to_string() {
162        let deveui = DevEui::from([0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12]);
163        assert_eq!(deveui.to_string(), "123456789abcdef0");
164    }
165
166    #[test]
167    fn test_deveui_from_str() {
168        let deveui = DevEui::from_str("123456789abcdef0").unwrap();
169        assert_eq!(deveui, DevEui::from([0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12]));
170    }
171
172    #[test]
173    fn test_deveui_from_small_str() {
174        let result = DevEui::from_str("123456789abcd");
175        assert_eq!(result, Err(FromHexError::OddLength));
176    }
177
178    #[test]
179    fn test_deveui_from_large_str() {
180        let result = DevEui::from_str("123456789abcdef000");
181        assert_eq!(result, Err(FromHexError::InvalidStringLength));
182    }
183}