1use core::fmt;
5
6use crate::view::{DESTINATION, EXPECTING_REPLY, NETWORK_MESSAGE, SOURCE};
7use crate::{Address, GLOBAL_NETWORK, MAX_MAC_LEN, Npdu, VERSION};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14#[non_exhaustive]
15pub enum EncodeError {
16 NoRoom {
18 needed: usize,
20 available: usize,
22 },
23 ReservedNetwork,
25 EmptySource,
27 AddressTooLong,
29}
30
31impl fmt::Display for EncodeError {
32 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33 match self {
34 Self::NoRoom { needed, available } => {
35 write!(f, "an NPDU of {needed} octets doesn't fit in {available}")
36 }
37 Self::ReservedNetwork => f.write_str("NPDU names a reserved network number"),
38 Self::EmptySource => f.write_str("NPDU source SLEN=0 is invalid"),
39 Self::AddressTooLong => write!(f, "NPDU address exceeds the {MAX_MAC_LEN}-octet limit"),
40 }
41 }
42}
43
44impl core::error::Error for EncodeError {}
45
46impl Npdu<'_> {
47 #[must_use]
50 pub fn encoded_len(&self) -> usize {
51 let address = |address: Option<Address<'_>>| address.map_or(0, |a| 3 + a.mac.len());
52 let message = match self.message_type {
53 Some(kind) if kind.is_proprietary() => 3,
54 Some(_) => 1,
55 None => 0,
56 };
57 2 + address(self.destination)
58 + address(self.source)
59 + usize::from(self.destination.is_some())
60 + message
61 + self.payload.len()
62 }
63
64 pub fn encode_into(&self, out: &mut [u8]) -> Result<usize, EncodeError> {
71 if let Some(destination) = self.destination {
72 if destination.network == 0 {
73 return Err(EncodeError::ReservedNetwork);
74 }
75 if destination.mac.len() > MAX_MAC_LEN {
76 return Err(EncodeError::AddressTooLong);
77 }
78 }
79 if let Some(source) = self.source {
80 if source.network == 0 || source.network == GLOBAL_NETWORK {
81 return Err(EncodeError::ReservedNetwork);
82 }
83 if source.mac.is_empty() {
84 return Err(EncodeError::EmptySource);
85 }
86 if source.mac.len() > MAX_MAC_LEN {
87 return Err(EncodeError::AddressTooLong);
88 }
89 }
90 let needed = self.encoded_len();
91 let available = out.len();
92 let out = out
93 .get_mut(..needed)
94 .ok_or(EncodeError::NoRoom { needed, available })?;
95
96 let mut control = self.priority.to_raw();
97 if self.message_type.is_some() {
98 control |= NETWORK_MESSAGE;
99 }
100 if self.destination.is_some() {
101 control |= DESTINATION;
102 }
103 if self.source.is_some() {
104 control |= SOURCE;
105 }
106 if self.expecting_reply {
107 control |= EXPECTING_REPLY;
108 }
109 let mut cursor = Cursor { out, at: 0 };
110 cursor.put(&[VERSION, control]);
111 if let Some(destination) = self.destination {
112 cursor.put_address(destination);
113 }
114 if let Some(source) = self.source {
115 cursor.put_address(source);
116 }
117 if self.destination.is_some() {
118 cursor.put(&[self.hop_count]);
119 }
120 if let Some(kind) = self.message_type {
121 cursor.put(&[kind.to_raw()]);
122 if kind.is_proprietary() {
123 cursor.put(&self.vendor_id.unwrap_or(0).to_be_bytes());
124 }
125 }
126 cursor.put(self.payload);
127 debug_assert_eq!(cursor.at, needed);
128 Ok(needed)
129 }
130}
131
132struct Cursor<'o> {
134 out: &'o mut [u8],
135 at: usize,
136}
137
138impl Cursor<'_> {
139 fn put(&mut self, octets: &[u8]) {
140 self.out[self.at..self.at + octets.len()].copy_from_slice(octets);
141 self.at += octets.len();
142 }
143
144 fn put_address(&mut self, address: Address<'_>) {
145 self.put(&address.network.to_be_bytes());
146 self.put(&[address.mac.len() as u8]);
148 self.put(address.mac);
149 }
150}