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