1use std::{cmp, fmt};
2
3use crate::{create_xfs_bool, create_xfs_i4, impl_xfs_array, impl_xfs_struct, ShutterCmd, Size};
4
5use super::CdrPosition;
6
7pub const CDR_POS_CAP_LIST_LEN: usize = 2;
8
9create_xfs_bool!(
10 ShutterStatusSupported,
11 "shutterStatusSupported",
12 "Specifies whether shutter status reporting is supported."
13);
14create_xfs_bool!(
15 ContentStatusSupported,
16 "contentStatusSupported",
17 "Specifies whether there is a sensor to detect if the position is empty."
18);
19create_xfs_i4!(
20 MaxItems,
21 "maxItems",
22 "Maximum number of items which this position can hold."
23);
24create_xfs_bool!(
25 Input,
26 "input",
27 "Specifies whether this position can be used as source for an accept command."
28);
29create_xfs_bool!(
30 Output,
31 "output",
32 "Specifies whether this position can be used as target for a dispense command."
33);
34create_xfs_bool!(Rollback, "rollback", "Specifies whether this position can be used as target for[cash_in_rollback](crate::cash::cash_in_rollback) command.");
35create_xfs_bool!(Refusal, "refusal", "Specifies whether refused notes can be moved to this position during [cash_in](crate::cash::cash_in) command.");
36
37#[repr(C)]
39#[derive(Clone, Copy, Debug, Default, PartialEq, serde::Deserialize, serde::Serialize)]
40pub struct CdrPositionCapabilities {
41 pub position: CdrPosition,
43 pub content_status_supported: ContentStatusSupported,
44 pub shutter_status_supported: ShutterStatusSupported,
45 pub shutter_cmd: ShutterCmd,
47 pub max_items: MaxItems,
48 pub input: Input,
49 pub output: Output,
50 pub rollback: Rollback,
51 pub refusal: Refusal,
52}
53
54impl CdrPositionCapabilities {
55 pub const fn new() -> Self {
57 Self {
58 position: CdrPosition::new(),
59 shutter_status_supported: ShutterStatusSupported::new(),
60 shutter_cmd: ShutterCmd::new(),
61 content_status_supported: ContentStatusSupported::new(),
62 max_items: MaxItems::new(),
63 input: Input::new(),
64 output: Output::new(),
65 rollback: Rollback::new(),
66 refusal: Refusal::new(),
67 }
68 }
69}
70
71impl fmt::Display for CdrPositionCapabilities {
72 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73 write!(f, "{{")?;
74 write!(f, r#""position":{},"#, self.position)?;
75 write!(
76 f,
77 r#""shutter_status_supported":{},"#,
78 self.shutter_status_supported
79 )?;
80 write!(f, r#""shutter_cmd":{},"#, self.shutter_cmd)?;
81 write!(
82 f,
83 r#""content_status_supported":{},"#,
84 self.content_status_supported
85 )?;
86 write!(f, r#""max_items":{},"#, self.max_items)?;
87 write!(f, r#""input":{},"#, self.input)?;
88 write!(f, r#""output":{},"#, self.output)?;
89 write!(f, r#""rollback":{},"#, self.rollback)?;
90 write!(f, r#""refusal":{}"#, self.refusal)?;
91 write!(f, "}}")
92 }
93}
94
95impl_xfs_struct!(
96 CdrPositionCapabilities,
97 "positionCapabilities",
98 [
99 position: CdrPosition,
100 shutter_status_supported: ShutterStatusSupported,
101 shutter_cmd: ShutterCmd,
102 content_status_supported: ContentStatusSupported,
103 max_items: MaxItems,
104 input: Input,
105 output: Output,
106 rollback: Rollback,
107 refusal: Refusal
108 ]
109);
110
111#[repr(C)]
113#[derive(Clone, Copy, Debug, Default, PartialEq, serde::Deserialize, serde::Serialize)]
114pub struct CdrPositionCapabilitiesList {
115 size: Size,
116 items: [CdrPositionCapabilities; CDR_POS_CAP_LIST_LEN],
117}
118
119impl CdrPositionCapabilitiesList {
120 pub const fn new() -> Self {
122 Self {
123 size: Size::new(),
124 items: [CdrPositionCapabilities::new(); CDR_POS_CAP_LIST_LEN],
125 }
126 }
127
128 pub const fn create(items: [CdrPositionCapabilities; CDR_POS_CAP_LIST_LEN]) -> Self {
130 Self {
131 size: Size::create(CDR_POS_CAP_LIST_LEN as u32),
132 items,
133 }
134 }
135
136 pub const fn max_size() -> usize {
138 CDR_POS_CAP_LIST_LEN
139 }
140
141 pub const fn size(&self) -> u32 {
143 self.size.inner()
144 }
145
146 pub fn set_size(&mut self, size: u32) {
148 if size as usize <= Self::max_size() {
149 self.size.set_inner(size);
150 }
151 }
152
153 pub fn items(&self) -> &[CdrPositionCapabilities] {
155 let len = self.size.inner() as usize;
156 if len <= Self::max_size() {
157 self.items[..len].as_ref()
158 } else {
159 self.items.as_ref()
160 }
161 }
162
163 pub fn items_mut(&mut self) -> &mut [CdrPositionCapabilities] {
165 let len = self.size.inner() as usize;
166 if len <= Self::max_size() {
167 self.items[..len].as_mut()
168 } else {
169 self.items.as_mut()
170 }
171 }
172
173 pub fn set_items(&mut self, items: &[CdrPositionCapabilities]) {
175 let len = cmp::min(items.len(), Self::max_size());
176 self.items[..len].copy_from_slice(&items[..len]);
177 self.size.set_inner(len as u32);
178 }
179
180 pub fn with_items(mut self, items: &[CdrPositionCapabilities]) -> Self {
182 self.set_items(items);
183 self
184 }
185}
186
187impl fmt::Display for CdrPositionCapabilitiesList {
188 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
189 write!(f, "[")?;
190
191 let items = self.items();
192 for (i, item) in items.iter().enumerate() {
193 if i != 0 {
194 write!(f, ",")?;
195 }
196 write!(f, "{item}")?;
197 }
198
199 write!(f, "]")
200 }
201}
202
203impl_xfs_array!(CdrPositionCapabilitiesList, "positionCapabilitiesList");
204
205#[cfg(test)]
206mod tests {
207 use super::*;
208 use crate::xfs::{
209 array::XfsArray,
210 value::XfsValue,
211 xfs_struct::{XfsMember, XfsStruct},
212 };
213 use crate::Result;
214
215 #[test]
216 fn test_cdr_position_capabitilities_xfs() -> Result<()> {
217 let exp_xfs = XfsMember::create(
218 CdrPositionCapabilitiesList::xfs_name(),
219 XfsValue::new().with_array(XfsArray::create([XfsValue::new().with_xfs_struct(
220 XfsStruct::create([
221 XfsMember::create("position", XfsValue::new().with_i4(1)),
222 XfsMember::create("contentStatusSupported", XfsValue::new().with_boolean(1)),
223 XfsMember::create("shutterStatusSupported", XfsValue::new().with_boolean(0)),
224 XfsMember::create("shutterCmd", XfsValue::new().with_boolean(0)),
225 XfsMember::create("maxItems", XfsValue::new().with_i4(1)),
226 XfsMember::create("input", XfsValue::new().with_boolean(1)),
227 XfsMember::create("output", XfsValue::new().with_boolean(0)),
228 XfsMember::create("rollback", XfsValue::new().with_boolean(0)),
229 XfsMember::create("refusal", XfsValue::new().with_boolean(1)),
230 ]),
231 )])),
232 );
233 let exp_cap_list = CdrPositionCapabilitiesList {
234 size: Size::create(1),
235 items: [
236 CdrPositionCapabilities {
237 position: CdrPosition::Top,
238 shutter_status_supported: false.into(),
239 shutter_cmd: false.into(),
240 content_status_supported: true.into(),
241 max_items: 1u32.into(),
242 input: true.into(),
243 output: false.into(),
244 rollback: false.into(),
245 refusal: true.into(),
246 },
247 CdrPositionCapabilities::new(),
248 ],
249 };
250
251 assert_eq!(
252 CdrPositionCapabilitiesList::try_from(&exp_xfs)?,
253 exp_cap_list
254 );
255
256 let xfs_list = XfsMember::from(exp_cap_list);
257 for (exp, item) in exp_xfs
258 .value()
259 .array()
260 .unwrap()
261 .data()
262 .iter()
263 .map(|m| m.inner().xfs_struct().unwrap())
264 .zip(
265 xfs_list
266 .value()
267 .array()
268 .unwrap()
269 .data()
270 .iter()
271 .map(|m| m.inner().xfs_struct().unwrap()),
272 )
273 {
274 assert_eq!(
275 exp.find_member(CdrPosition::xfs_name())?,
276 item.find_member(CdrPosition::xfs_name())?
277 );
278 assert_eq!(
279 exp.find_member(ShutterStatusSupported::xfs_name())?,
280 item.find_member(ShutterStatusSupported::xfs_name())?
281 );
282 assert_eq!(
283 exp.find_member(ShutterCmd::xfs_name())?,
284 item.find_member(ShutterCmd::xfs_name())?
285 );
286 assert_eq!(
287 exp.find_member(ContentStatusSupported::xfs_name())?,
288 item.find_member(ContentStatusSupported::xfs_name())?
289 );
290 assert_eq!(
291 exp.find_member(MaxItems::xfs_name())?,
292 item.find_member(MaxItems::xfs_name())?
293 );
294 assert_eq!(
295 exp.find_member(Input::xfs_name())?,
296 item.find_member(Input::xfs_name())?
297 );
298 assert_eq!(
299 exp.find_member(Output::xfs_name())?,
300 item.find_member(Output::xfs_name())?
301 );
302 assert_eq!(
303 exp.find_member(Rollback::xfs_name())?,
304 item.find_member(Rollback::xfs_name())?
305 );
306 assert_eq!(
307 exp.find_member(Refusal::xfs_name())?,
308 item.find_member(Refusal::xfs_name())?
309 );
310 }
311
312 Ok(())
313 }
314
315 #[test]
316 fn test_cdr_position_capabitilities_xfs_full() -> Result<()> {
317 let exp_xfs = XfsMember::create(
318 CdrPositionCapabilitiesList::xfs_name(),
319 XfsValue::new().with_array(XfsArray::create([
320 XfsValue::new().with_xfs_struct(XfsStruct::create([
321 XfsMember::create("position", XfsValue::new().with_i4(1)),
322 XfsMember::create("contentStatusSupported", XfsValue::new().with_boolean(1)),
323 XfsMember::create("shutterStatusSupported", XfsValue::new().with_boolean(0)),
324 XfsMember::create("shutterCmd", XfsValue::new().with_boolean(0)),
325 XfsMember::create("maxItems", XfsValue::new().with_i4(1)),
326 XfsMember::create("input", XfsValue::new().with_boolean(1)),
327 XfsMember::create("output", XfsValue::new().with_boolean(0)),
328 XfsMember::create("rollback", XfsValue::new().with_boolean(0)),
329 XfsMember::create("refusal", XfsValue::new().with_boolean(1)),
330 ])),
331 XfsValue::new().with_xfs_struct(XfsStruct::create([
332 XfsMember::create("position", XfsValue::new().with_i4(2)),
333 XfsMember::create("contentStatusSupported", XfsValue::new().with_boolean(1)),
334 XfsMember::create("shutterStatusSupported", XfsValue::new().with_boolean(1)),
335 XfsMember::create("shutterCmd", XfsValue::new().with_boolean(1)),
336 XfsMember::create("maxItems", XfsValue::new().with_i4(15)),
337 XfsMember::create("input", XfsValue::new().with_boolean(0)),
338 XfsMember::create("output", XfsValue::new().with_boolean(1)),
339 XfsMember::create("rollback", XfsValue::new().with_boolean(1)),
340 XfsMember::create("refusal", XfsValue::new().with_boolean(0)),
341 ])),
342 ])),
343 );
344
345 let exp_cap_list = CdrPositionCapabilitiesList {
346 size: Size::create(2),
347 items: [
348 CdrPositionCapabilities {
349 position: CdrPosition::Top,
350 content_status_supported: true.into(),
351 shutter_status_supported: false.into(),
352 shutter_cmd: false.into(),
353 max_items: 1u32.into(),
354 input: true.into(),
355 output: false.into(),
356 rollback: false.into(),
357 refusal: true.into(),
358 },
359 CdrPositionCapabilities {
360 position: CdrPosition::Bottom,
361 content_status_supported: true.into(),
362 shutter_status_supported: true.into(),
363 shutter_cmd: true.into(),
364 max_items: 15u32.into(),
365 input: false.into(),
366 output: true.into(),
367 rollback: true.into(),
368 refusal: false.into(),
369 },
370 ],
371 };
372
373 assert_eq!(
374 CdrPositionCapabilitiesList::try_from(&exp_xfs)?,
375 exp_cap_list
376 );
377
378 let xfs_list = XfsMember::from(exp_cap_list);
379 for (exp, item) in exp_xfs
380 .value()
381 .array()
382 .unwrap()
383 .data()
384 .iter()
385 .map(|m| m.inner().xfs_struct().unwrap())
386 .zip(
387 xfs_list
388 .value()
389 .array()
390 .unwrap()
391 .data()
392 .iter()
393 .map(|m| m.inner().xfs_struct().unwrap()),
394 )
395 {
396 assert_eq!(
397 exp.find_member(CdrPosition::xfs_name())?,
398 item.find_member(CdrPosition::xfs_name())?
399 );
400 assert_eq!(
401 exp.find_member(ShutterStatusSupported::xfs_name())?,
402 item.find_member(ShutterStatusSupported::xfs_name())?
403 );
404 assert_eq!(
405 exp.find_member(ShutterCmd::xfs_name())?,
406 item.find_member(ShutterCmd::xfs_name())?
407 );
408 assert_eq!(
409 exp.find_member(ContentStatusSupported::xfs_name())?,
410 item.find_member(ContentStatusSupported::xfs_name())?
411 );
412 assert_eq!(
413 exp.find_member(MaxItems::xfs_name())?,
414 item.find_member(MaxItems::xfs_name())?
415 );
416 assert_eq!(
417 exp.find_member(Input::xfs_name())?,
418 item.find_member(Input::xfs_name())?
419 );
420 assert_eq!(
421 exp.find_member(Output::xfs_name())?,
422 item.find_member(Output::xfs_name())?
423 );
424 assert_eq!(
425 exp.find_member(Rollback::xfs_name())?,
426 item.find_member(Rollback::xfs_name())?
427 );
428 assert_eq!(
429 exp.find_member(Refusal::xfs_name())?,
430 item.find_member(Refusal::xfs_name())?
431 );
432 }
433
434 Ok(())
435 }
436}