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