1#[cfg(test)]
2use hex_literal::hex;
3
4pub mod action;
6pub mod dash7;
9pub mod operand;
10pub use crate::codec::{Codec, WithOffset, WithSize};
12pub use action::Action;
13
14#[derive(Clone, Debug, PartialEq, Default)]
19pub struct Command {
20 pub actions: Vec<Action>,
22}
23
24impl std::fmt::Display for Command {
25 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
26 write!(f, "[")?;
27 let end = self.actions.len() - 1;
28 for (i, action) in self.actions.iter().enumerate() {
29 write!(f, "{}", action)?;
30 if i != end {
31 write!(f, "; ")?;
32 }
33 }
34 write!(f, "]")
35 }
36}
37
38#[derive(Debug, Clone, PartialEq)]
39pub struct CommandParseFail {
40 pub actions: Vec<Action>,
41 pub error: action::ActionDecodingError,
42}
43
44impl Command {
45 pub fn encoded_size(&self) -> usize {
46 self.actions.iter().map(|act| act.encoded_size()).sum()
47 }
48 pub unsafe fn encode_in(&self, out: &mut [u8]) -> usize {
55 let mut offset = 0;
56 for action in self.actions.iter() {
57 offset += action.encode_in(&mut out[offset..]);
58 }
59 offset
60 }
61 pub fn encode(&self) -> Box<[u8]> {
62 let mut data = vec![0; self.encoded_size()].into_boxed_slice();
63 unsafe { self.encode_in(&mut data) };
64 data
65 }
66 pub fn decode(out: &[u8]) -> Result<Self, WithOffset<CommandParseFail>> {
67 let mut actions = vec![];
68 let mut offset = 0;
69 loop {
70 if offset == out.len() {
71 break;
72 }
73 match Action::decode(&out[offset..]) {
74 Ok(WithSize { value, size }) => {
75 actions.push(value);
76 offset += size;
77 }
78 Err(error) => {
79 let WithOffset { offset: off, value } = error;
80 return Err(WithOffset {
81 offset: offset + off,
82 value: CommandParseFail {
83 actions,
84 error: value,
85 },
86 });
87 }
88 }
89 }
90 Ok(Self { actions })
91 }
92
93 pub fn request_id(&self) -> Option<u8> {
94 for action in self.actions.iter() {
95 if let Action::RequestTag(action::RequestTag { id, .. }) = action {
96 return Some(*id);
97 }
98 }
99 None
100 }
101
102 pub fn response_id(&self) -> Option<u8> {
103 for action in self.actions.iter() {
104 if let Action::ResponseTag(action::ResponseTag { id, .. }) = action {
105 return Some(*id);
106 }
107 }
108 None
109 }
110
111 pub fn is_last_response(&self) -> bool {
112 for action in self.actions.iter() {
113 if let Action::ResponseTag(action::ResponseTag { eop, .. }) = action {
114 return *eop;
115 }
116 }
117 false
118 }
119}
120#[test]
121fn test_command() {
122 let cmd = Command {
123 actions: vec![
124 Action::RequestTag(action::RequestTag { id: 66, eop: true }),
125 Action::ReadFileData(action::ReadFileData {
126 resp: true,
127 group: false,
128 file_id: 0,
129 offset: 0,
130 size: 8,
131 }),
132 Action::ReadFileData(action::ReadFileData {
133 resp: false,
134 group: true,
135 file_id: 4,
136 offset: 2,
137 size: 3,
138 }),
139 Action::Nop(action::Nop {
140 resp: true,
141 group: true,
142 }),
143 ],
144 };
145 let data = &hex!("B4 42 41 00 00 08 81 04 02 03 C0") as &[u8];
146
147 assert_eq!(&cmd.encode()[..], data);
148 assert_eq!(
149 Command::decode(data).expect("should be parsed without error"),
150 cmd,
151 );
152}
153#[test]
154fn test_command_display() {
155 assert_eq!(
156 Command {
157 actions: vec![
158 Action::RequestTag(action::RequestTag { id: 66, eop: true }),
159 Action::Nop(action::Nop {
160 resp: true,
161 group: true,
162 }),
163 ]
164 }
165 .to_string(),
166 "[RTAG[E](66); NOP[GR]]"
167 );
168}
169
170#[test]
171fn test_command_request_id() {
172 assert_eq!(
173 Command {
174 actions: vec![Action::request_tag(true, 66), Action::nop(true, true)]
175 }
176 .request_id(),
177 Some(66)
178 );
179 assert_eq!(
180 Command {
181 actions: vec![Action::nop(true, false), Action::request_tag(true, 44)]
182 }
183 .request_id(),
184 Some(44)
185 );
186 assert_eq!(
187 Command {
188 actions: vec![Action::nop(true, false), Action::nop(true, false)]
189 }
190 .request_id(),
191 None
192 );
193}
194
195#[test]
196fn test_comman_response_id() {
197 assert_eq!(
198 Command {
199 actions: vec![
200 Action::response_tag(true, true, 66),
201 Action::nop(true, true)
202 ]
203 }
204 .response_id(),
205 Some(66)
206 );
207 assert_eq!(
208 Command {
209 actions: vec![
210 Action::nop(true, false),
211 Action::response_tag(true, true, 44)
212 ]
213 }
214 .response_id(),
215 Some(44)
216 );
217 assert_eq!(
218 Command {
219 actions: vec![Action::nop(true, false), Action::nop(true, false)]
220 }
221 .response_id(),
222 None
223 );
224}
225
226#[test]
227fn test_command_is_last_response() {
228 assert!(Command {
229 actions: vec![
230 Action::response_tag(true, true, 66),
231 Action::nop(true, true)
232 ]
233 }
234 .is_last_response());
235 assert!(!Command {
236 actions: vec![
237 Action::response_tag(false, false, 66),
238 Action::nop(true, true)
239 ]
240 }
241 .is_last_response());
242 assert!(!Command {
243 actions: vec![
244 Action::response_tag(false, true, 44),
245 Action::response_tag(true, true, 44)
246 ]
247 }
248 .is_last_response());
249 assert!(!Command {
250 actions: vec![Action::nop(true, false), Action::nop(true, false)]
251 }
252 .is_last_response());
253}