1pub mod fs;
3pub mod image;
5pub mod os;
7pub mod shell;
9pub mod stats;
11pub mod zephyr;
13
14mod macros;
15use macros::impl_mcumgr_command;
16
17use serde::{Deserialize, Serialize};
18
19#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
21pub struct ErrResponseV2 {
22 pub group: u16,
24 pub rc: i32,
26}
27
28#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
30pub struct ErrResponse {
31 pub rc: Option<i32>,
33 pub rsn: Option<String>,
35 pub err: Option<ErrResponseV2>,
37}
38
39pub trait McuMgrCommand {
41 type Payload: Serialize;
43 type Response: for<'a> Deserialize<'a>;
45 fn is_write_operation(&self) -> bool;
47 fn group_id(&self) -> u16;
49 fn command_id(&self) -> u8;
51 fn data(&self) -> &Self::Payload;
53}
54
55fn is_default<T: Default + PartialEq>(val: &T) -> bool {
57 val == &T::default()
58}
59
60fn data_too_large_error() -> std::io::Error {
61 std::io::Error::other(Box::<dyn std::error::Error + Send + Sync>::from(
62 "Serialized data too large".to_string(),
63 ))
64}
65
66struct CountingWriter {
68 bytes_written: usize,
69}
70impl CountingWriter {
71 fn new() -> Self {
72 Self { bytes_written: 0 }
73 }
74}
75impl std::io::Write for CountingWriter {
76 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
77 let len = buf.len();
78 self.bytes_written = self
79 .bytes_written
80 .checked_add(len)
81 .ok_or_else(data_too_large_error)?;
82 Ok(len)
83 }
84
85 fn flush(&mut self) -> std::io::Result<()> {
86 Ok(())
87 }
88}
89
90impl_mcumgr_command!((read, MGMT_GROUP_ID_OS, 0): os::Echo<'_> => os::EchoResponse);
91impl_mcumgr_command!((read, MGMT_GROUP_ID_OS, 2): os::TaskStatistics => os::TaskStatisticsResponse);
92impl_mcumgr_command!((read, MGMT_GROUP_ID_OS, 4): os::DateTimeGet => os::DateTimeGetResponse);
93impl_mcumgr_command!((write, MGMT_GROUP_ID_OS, 4): os::DateTimeSet => os::DateTimeSetResponse);
94impl_mcumgr_command!((write, MGMT_GROUP_ID_OS, 5): os::SystemReset => os::SystemResetResponse);
95impl_mcumgr_command!((read, MGMT_GROUP_ID_OS, 6): os::MCUmgrParameters => os::MCUmgrParametersResponse);
96impl_mcumgr_command!((read, MGMT_GROUP_ID_OS, 7): os::ApplicationInfo<'_> => os::ApplicationInfoResponse);
97impl_mcumgr_command!((read, MGMT_GROUP_ID_OS, 8): os::BootloaderInfo => os::BootloaderInfoResponse);
98impl_mcumgr_command!((read, MGMT_GROUP_ID_OS, 8): os::BootloaderInfoMcubootMode => os::BootloaderInfoMcubootModeResponse);
99
100impl_mcumgr_command!((read, MGMT_GROUP_ID_IMAGE, 0): image::GetImageState => image::ImageStateResponse);
101impl_mcumgr_command!((write, MGMT_GROUP_ID_IMAGE, 0): image::SetImageState<'_> => image::ImageStateResponse);
102impl_mcumgr_command!((write, MGMT_GROUP_ID_IMAGE, 1): image::ImageUpload<'_, '_> => image::ImageUploadResponse);
103impl_mcumgr_command!((write, MGMT_GROUP_ID_IMAGE, 5): image::ImageErase => image::ImageEraseResponse);
104impl_mcumgr_command!((read, MGMT_GROUP_ID_IMAGE, 6): image::SlotInfo => image::SlotInfoResponse);
105
106impl_mcumgr_command!((read, MGMT_GROUP_ID_STAT, 0): stats::GroupData<'_> => stats::GroupDataResponse);
107impl_mcumgr_command!((read, MGMT_GROUP_ID_STAT, 1): stats::ListGroups => stats::ListGroupsResponse);
108
109impl_mcumgr_command!((write, MGMT_GROUP_ID_FS, 0): fs::FileUpload<'_, '_> => fs::FileUploadResponse);
110impl_mcumgr_command!((read, MGMT_GROUP_ID_FS, 0): fs::FileDownload<'_> => fs::FileDownloadResponse);
111impl_mcumgr_command!((read, MGMT_GROUP_ID_FS, 1): fs::FileStatus<'_> => fs::FileStatusResponse);
112impl_mcumgr_command!((read, MGMT_GROUP_ID_FS, 2): fs::FileChecksum<'_, '_> => fs::FileChecksumResponse);
113impl_mcumgr_command!((read, MGMT_GROUP_ID_FS, 3): fs::SupportedFileChecksumTypes => fs::SupportedFileChecksumTypesResponse);
114impl_mcumgr_command!((write, MGMT_GROUP_ID_FS, 4): fs::FileClose => fs::FileCloseResponse);
115
116impl_mcumgr_command!((write, MGMT_GROUP_ID_SHELL, 0): shell::ShellCommandLineExecute<'_> => shell::ShellCommandLineExecuteResponse);
117
118impl_mcumgr_command!((write, ZEPHYR_MGMT_GRP_BASIC, 0): zephyr::EraseStorage => zephyr::EraseStorageResponse);
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123 use ciborium::cbor;
124
125 #[test]
126 fn decode_error_none() {
127 let mut cbor_data = vec![];
128 ciborium::into_writer(
129 &cbor!({
130 "foo" => 42,
131 })
132 .unwrap(),
133 &mut cbor_data,
134 )
135 .unwrap();
136 let err: ErrResponse = ciborium::from_reader(cbor_data.as_slice()).unwrap();
137 assert_eq!(
138 err,
139 ErrResponse {
140 rc: None,
141 rsn: None,
142 err: None,
143 }
144 );
145 }
146
147 #[test]
148 fn decode_error_v1() {
149 let mut cbor_data = vec![];
150 ciborium::into_writer(
151 &cbor!({
152 "rc" => 10,
153 })
154 .unwrap(),
155 &mut cbor_data,
156 )
157 .unwrap();
158 let err: ErrResponse = ciborium::from_reader(cbor_data.as_slice()).unwrap();
159 assert_eq!(
160 err,
161 ErrResponse {
162 rc: Some(10),
163 rsn: None,
164 err: None,
165 }
166 );
167 }
168
169 #[test]
170 fn decode_error_v1_with_msg() {
171 let mut cbor_data = vec![];
172 ciborium::into_writer(
173 &cbor!({
174 "rc" => 1,
175 "rsn" => "Test Reason!",
176 })
177 .unwrap(),
178 &mut cbor_data,
179 )
180 .unwrap();
181 let err: ErrResponse = ciborium::from_reader(cbor_data.as_slice()).unwrap();
182 assert_eq!(
183 err,
184 ErrResponse {
185 rc: Some(1),
186 rsn: Some("Test Reason!".to_string()),
187 err: None,
188 }
189 );
190 }
191
192 #[test]
193 fn decode_error_v2() {
194 let mut cbor_data = vec![];
195 ciborium::into_writer(
196 &cbor!({
197 "err" => {
198 "group" => 4,
199 "rc" => 20,
200 }
201 })
202 .unwrap(),
203 &mut cbor_data,
204 )
205 .unwrap();
206 let err: ErrResponse = ciborium::from_reader(cbor_data.as_slice()).unwrap();
207 assert_eq!(
208 err,
209 ErrResponse {
210 rc: None,
211 rsn: None,
212 err: Some(ErrResponseV2 { group: 4, rc: 20 })
213 }
214 );
215 }
216
217 #[test]
218 fn is_default() {
219 assert!(super::is_default(&0));
220 assert!(!super::is_default(&5));
221 }
222}