twine_codec/dataset/operational_dataset/
mod.rs1use core::str::FromStr;
9
10use twine_tlv::TlvCollection;
11
12use crate::{
13 dataset::{
14 ActiveTimestamp, DelayTimer, ExtendedPanId, MeshLocalPrefix, NetworkKey, NetworkName,
15 PendingTimestamp, Pskc, SecurityPolicy, Timestamp,
16 },
17 radio::{Channel, ChannelMask, PanId},
18 TwineCodecError,
19};
20
21mod iter;
22pub use iter::OperationalDatasetIter;
23
24const OPERATIONAL_DATASET_MAX_SIZE: usize = 256;
25
26macro_rules! decode_type {
27 ($name:ident, $decode_type:ty) => {
28 pub fn $name(&self) -> Option<$decode_type> {
29 self.collection.decode_type_unchecked::<$decode_type>()
30 }
31 };
32}
33
34#[derive(Debug)]
35pub struct OperationalDataset {
36 collection: TlvCollection<OPERATIONAL_DATASET_MAX_SIZE>,
37}
38
39impl OperationalDataset {
40 #[cfg(any(test, feature = "std"))]
42 pub fn random() -> Result<Self, TwineCodecError> {
43 let mut collection = TlvCollection::default();
44
45 use crate::dataset::timestamp::Authoritative;
46 let active_timestamp = Timestamp::now(Authoritative(false));
47 let _ = collection.push(ActiveTimestamp::from(active_timestamp))?;
48
49 let channel = Channel::random();
50 let _ = collection.push(channel)?;
51
52 let channel_mask = ChannelMask::default();
55 let _ = collection.push(channel_mask)?;
56
57 let xpan = ExtendedPanId::random();
58 let _ = collection.push(xpan)?;
59
60 let mesh_local_prefix = MeshLocalPrefix::random_ula();
61 let _ = collection.push(mesh_local_prefix)?;
62
63 let network_key = NetworkKey::random();
64 let _ = collection.push(network_key)?;
65
66 let pan_id = PanId::random();
67 let network_name = alloc::format!("Twine-{:x}", pan_id.get());
68 let _ = collection.push(NetworkName::from_str(&network_name)?)?;
69
70 let _ = collection.push(pan_id)?;
71
72 let pskc = Pskc::random();
73 let _ = collection.push(pskc)?;
74
75 let security_policy = SecurityPolicy::default();
76 let _ = collection.push(security_policy)?;
77
78 Ok(Self { collection })
79 }
80
81 pub fn active_timestamp(&self) -> Option<Timestamp> {
82 self.collection
83 .decode_type_unchecked::<ActiveTimestamp>()
84 .map(Timestamp::from)
85 }
86
87 pub fn set_active_timestamp(&mut self, timestamp: Timestamp) -> Result<(), TwineCodecError> {
88 let active_timestamp = ActiveTimestamp::from(timestamp);
89 self.collection.replace_or_push(active_timestamp)?;
90 Ok(())
91 }
92
93 pub fn pending_timestamp(&self) -> Option<Timestamp> {
94 self.collection
95 .decode_type_unchecked::<PendingTimestamp>()
96 .map(Timestamp::from)
97 }
98
99 decode_type!(delay_timer, DelayTimer);
100 decode_type!(channel, Channel);
101 decode_type!(pan_id, PanId);
103 decode_type!(channel_mask, ChannelMask);
104 decode_type!(extended_pan_id, ExtendedPanId);
105 decode_type!(network_name, NetworkName);
106 decode_type!(pskc, Pskc);
107 decode_type!(network_key, NetworkKey);
108 decode_type!(mesh_local_prefix, MeshLocalPrefix);
109 decode_type!(security_policy, SecurityPolicy);
110
111 #[cfg(any(test, feature = "std"))]
112 pub fn pretty_fmt(&self) {
113 std::println!("Operational Dataset: {:?}", self);
114 self.iter().for_each(|item| std::println!("{item:?}"));
115 }
116
117 pub fn iter(&self) -> OperationalDatasetIter<'_> {
118 OperationalDatasetIter {
119 inner: (&self.collection).into_iter(),
120 }
121 }
122
123 #[cfg(any(test, feature = "alloc"))]
124 pub fn as_hex_string(&self) -> alloc::string::String {
125 let mut hex_string = alloc::string::String::new();
126 for tlv in &self.collection {
127 hex_string.push_str(&hex::encode(tlv));
128 }
129 hex_string
130 }
131}
132
133impl FromStr for OperationalDataset {
134 type Err = TwineCodecError;
135
136 fn from_str(s: &str) -> Result<Self, Self::Err> {
137 if (s.len() & 1) != 0 {
139 return Err(TwineCodecError::HexDecodeError);
140 }
141
142 let n = s.len() / 2;
143 let mut buffer = [0_u8; OPERATIONAL_DATASET_MAX_SIZE];
144
145 if n > buffer.len() {
147 return Err(TwineCodecError::HexDecodeError);
148 }
149
150 hex::decode_to_slice(s, &mut buffer[..n]).map_err(|_| TwineCodecError::HexDecodeError)?;
151 let collection = TlvCollection::new_from_static(buffer);
152
153 Ok(Self { collection })
154 }
155}
156
157impl core::fmt::Display for OperationalDataset {
158 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
159 self.iter().try_fold((), |_, item| writeln!(f, "{item}"))
160 }
161}
162
163#[cfg(test)]
164mod tests {
165 use crate::{dataset::timestamp::Authoritative, SecurityPolicyBuilder};
166
167 use super::*;
168
169 #[test]
170 fn success_from_str() {
171 let dataset_str = "0e080000000000010000000300000c4a0300001335060004001fffe002081bb896bef533a5850708fd48b2e8c34e7dc70510e9b948988752752873570d09ada4d0be030f4f70656e5468726561642d623364650102b3de0410f9f07ed37fbb6828fb3b26b63bdea3c30c0402a0f7f8";
172 let dataset = OperationalDataset::from_str(dataset_str).unwrap();
173
174 let active_timestamp = dataset.active_timestamp().unwrap();
175 let channel = dataset.channel().unwrap();
176 let xpan = dataset.extended_pan_id().unwrap();
179 let mesh_local_prefix: MeshLocalPrefix = dataset.mesh_local_prefix().unwrap();
180 let network_key = dataset.network_key().unwrap();
181 let network_name = dataset.network_name().unwrap();
182 let pan_id = dataset.pan_id().unwrap();
183 let pskc = dataset.pskc().unwrap();
184 let security_policy = dataset.security_policy().unwrap();
185
186 assert_eq!(
187 active_timestamp,
188 Timestamp::from((1, 1, Authoritative(false)))
189 );
190 assert_eq!(channel, Channel::new(0, 12));
191 assert_eq!(
194 xpan,
195 ExtendedPanId::from([0x1b, 0xb8, 0x96, 0xbe, 0xf5, 0x33, 0xa5, 0x85])
196 );
197 assert_eq!(
198 mesh_local_prefix,
199 MeshLocalPrefix::from([0xfd, 0x48, 0xb2, 0xe8, 0xc3, 0x4e, 0x7d, 0xc7])
200 );
201 assert_eq!(
202 network_key,
203 NetworkKey::from(u128::from_be_bytes([
204 0xe9, 0xb9, 0x48, 0x98, 0x87, 0x52, 0x75, 0x28, 0x73, 0x57, 0x0d, 0x09, 0xad, 0xa4,
205 0xd0, 0xbe
206 ]))
207 );
208 assert_eq!(
209 network_name,
210 NetworkName::from_str("OpenThread-b3de").unwrap()
211 );
212 assert_eq!(pan_id, PanId::from(0xb3de));
213 assert_eq!(
214 pskc,
215 Pskc::from([
216 0xf9, 0xf0, 0x7e, 0xd3, 0x7f, 0xbb, 0x68, 0x28, 0xfb, 0x3b, 0x26, 0xb6, 0x3b, 0xde,
217 0xa3, 0xc3
218 ])
219 );
220 assert_eq!(
221 security_policy,
222 SecurityPolicyBuilder::with_default_policy()
223 .build()
224 .unwrap()
225 );
226 }
227}