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