1use std::fmt;
4
5use super::operation_id::OperationId;
6use super::params::{XfsParam, XfsParams};
7use super::xfs_struct::XfsStruct;
8use crate::{Error, Result};
9
10#[repr(C)]
12#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
13#[serde(rename = "methodCall")]
14pub struct XfsMethodCall {
15 #[serde(rename = "methodName")]
16 name: String,
17 params: XfsParams,
18}
19
20impl XfsMethodCall {
21 pub const fn new() -> Self {
23 Self {
24 name: String::new(),
25 params: XfsParams::new(),
26 }
27 }
28
29 pub fn create<P: Into<Vec<XfsParam>>>(name: XfsMethodName, params: P) -> Self {
31 Self {
32 name: <&str>::from(name).into(),
33 params: XfsParams::create(params.into()),
34 }
35 }
36
37 pub fn name(&self) -> Result<XfsMethodName> {
44 self.name.as_str().try_into()
45 }
46
47 pub fn name_str(&self) -> &str {
49 self.name.as_str()
50 }
51
52 pub fn set_name(&mut self, name: XfsMethodName) {
54 self.name = <&str>::from(name).into();
55 }
56
57 pub fn with_name(mut self, name: XfsMethodName) -> Self {
59 self.set_name(name);
60 self
61 }
62
63 pub const fn params(&self) -> &XfsParams {
65 &self.params
66 }
67
68 pub fn set_params(&mut self, params: XfsParams) {
70 self.params = params;
71 }
72
73 pub fn with_params(mut self, params: XfsParams) -> Self {
75 self.set_params(params);
76 self
77 }
78
79 pub fn is_async(&self) -> bool {
83 if let Ok(name) = self.name() {
84 matches!(
85 name,
86 XfsMethodName::CashIn
87 | XfsMethodName::CashInStart
88 | XfsMethodName::CashInRollback
89 | XfsMethodName::CashInEnd
90 | XfsMethodName::Empty
91 | XfsMethodName::Eject
92 | XfsMethodName::Reset
93 | XfsMethodName::Park
94 | XfsMethodName::Denominate
95 | XfsMethodName::Dispense
96 | XfsMethodName::Present
97 | XfsMethodName::Retract
98 )
99 } else {
100 false
101 }
102 }
103
104 pub fn call_id(&self) -> Result<i32> {
108 self.params()
109 .params()
110 .iter()
111 .find(|&p| p.inner().value().i4().is_some())
112 .ok_or(Error::Xfs("missing callback ID".into()))?
113 .inner()
114 .value()
115 .i4()
116 .cloned()
117 .ok_or(Error::Xfs("missing callback ID".into()))
118 }
119
120 pub fn operation_id(&self) -> Result<OperationId> {
124 Ok(OperationId::create(
125 self.params()
126 .params()
127 .iter()
128 .filter(|&p| p.inner().value().i4().is_some())
129 .nth(1)
130 .ok_or(Error::Xfs("missing operation ID".into()))?
131 .inner()
132 .value()
133 .i4()
134 .cloned()
135 .ok_or(Error::Xfs("missing operation ID".into()))? as u32,
136 ))
137 }
138
139 pub fn result(&self) -> Result<i32> {
143 self.params()
144 .params()
145 .iter()
146 .filter(|&p| p.inner().value().i4().is_some())
147 .nth(2)
148 .ok_or(Error::Xfs("missing result".into()))?
149 .inner()
150 .value()
151 .i4()
152 .cloned()
153 .ok_or(Error::Xfs("missing result".into()))
154 }
155
156 pub fn ext_result(&self) -> Result<i32> {
160 self.params()
161 .params()
162 .iter()
163 .filter(|&p| p.inner().value().i4().is_some())
164 .nth(3)
165 .ok_or(Error::Xfs("missing extended result".into()))?
166 .inner()
167 .value()
168 .i4()
169 .cloned()
170 .ok_or(Error::Xfs("missing extended result".into()))
171 }
172
173 pub fn xfs_struct(&self) -> Result<XfsStruct> {
177 self.params()
178 .params()
179 .iter()
180 .find(|&p| p.inner().value().xfs_struct().is_some())
181 .ok_or(Error::Xfs("missing XFS struct parameter".into()))?
182 .inner()
183 .value()
184 .xfs_struct()
185 .cloned()
186 .ok_or(Error::Xfs("missing XFS struct parameter".into()))
187 }
188}
189
190impl From<&XfsMethodName> for XfsMethodCall {
191 fn from(val: &XfsMethodName) -> Self {
192 Self {
193 name: <&str>::from(val).into(),
194 params: XfsParams::new(),
195 }
196 }
197}
198
199impl From<XfsMethodName> for XfsMethodCall {
200 fn from(val: XfsMethodName) -> Self {
201 (&val).into()
202 }
203}
204
205impl fmt::Display for XfsMethodCall {
206 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
207 write!(f, "{{")?;
208 write!(f, r#""name": {}", "#, self.name)?;
209 write!(f, r#""params": ["#)?;
210 for (i, param) in self.params.params().iter().enumerate() {
211 if i != 0 {
212 write!(f, ", ")?;
213 }
214 write!(f, "{param}")?;
215 }
216 write!(f, "]}}")
217 }
218}
219
220#[repr(C)]
222#[derive(Clone, Copy, Debug, Default, PartialEq, serde::Deserialize, serde::Serialize)]
223pub enum XfsMethodName {
224 #[serde(rename = "bnr.getdatetime")]
225 GetDateTime,
226 #[serde(rename = "bnr.setdatetime")]
227 SetDateTime,
228 #[serde(rename = "bnr.cashinstart")]
229 CashInStart,
230 #[serde(rename = "bnr.cashin")]
231 CashIn,
232 #[serde(rename = "bnr.cashinrollback")]
233 CashInRollback,
234 #[serde(rename = "bnr.cashinend")]
235 CashInEnd,
236 #[serde(rename = "module.getidentification")]
237 #[default]
238 GetIdentification,
239 #[serde(rename = "bnr.getstatus")]
240 GetStatus,
241 #[serde(rename = "bnr.stopsession")]
242 StopSession,
243 #[serde(rename = "bnr.reset")]
244 Reset,
245 #[serde(rename = "bnr.reboot")]
246 Reboot,
247 #[serde(rename = "bnr.cancel")]
248 Cancel,
249 #[serde(rename = "bnr.park")]
250 Park,
251 #[serde(rename = "bnr.empty")]
252 Empty,
253 #[serde(rename = "bnr.eject")]
254 Eject,
255 #[serde(rename = "bnr.querycashunit")]
256 QueryCashUnit,
257 #[serde(rename = "bnr.configurecashunit")]
258 ConfigureCashUnit,
259 #[serde(rename = "bnr.updatecashunit")]
260 UpdateCashUnit,
261 #[serde(rename = "bnr.denominate")]
262 Denominate,
263 #[serde(rename = "bnr.dispense")]
264 Dispense,
265 #[serde(rename = "bnr.present")]
266 Present,
267 #[serde(rename = "bnr.cancelwaitingcashtaken")]
268 CancelWaitingCashTaken,
269 #[serde(rename = "bnr.retract")]
270 Retract,
271 #[serde(rename = "bnr.getcapabilities")]
272 GetCapabilities,
273 #[serde(rename = "bnr.setcapabilities")]
274 SetCapabilities,
275 #[serde(rename = "bnr.querydenominations")]
276 QueryDenominations,
277 #[serde(rename = "bnr.updatedenominations")]
278 UpdateDenominations,
279 #[serde(rename = "bnr.querybillsetids")]
280 QueryBillsetIds,
281 #[serde(rename = "bnr.getbillacceptancehistory")]
282 GetBillAcceptanceHistory,
283 #[serde(rename = "bnr.getbilldispensehistory")]
284 GetBillDispenseHistory,
285 #[serde(rename = "bnr.getfailurehistory")]
286 GetFailureHistory,
287 #[serde(rename = "bnr.getrestarthistory")]
288 GetRestartHistory,
289 #[serde(rename = "bnr.getusehistory")]
290 GetUseHistory,
291 #[serde(rename = "BnrListener.operationCompleteOccured")]
293 OperationCompleteOccurred,
294 #[serde(rename = "BnrListener.intermediateOccured")]
296 IntermediateOccurred,
297 #[serde(rename = "BnrListener.statusOccured")]
299 StatusOccurred,
300}
301
302impl XfsMethodName {
303 pub const fn new() -> Self {
305 Self::GetIdentification
306 }
307}
308
309impl From<&XfsMethodName> for &'static str {
310 fn from(val: &XfsMethodName) -> Self {
311 match val {
312 XfsMethodName::GetDateTime => "bnr.getdatetime",
313 XfsMethodName::SetDateTime => "bnr.setdatetime",
314 XfsMethodName::CashInStart => "bnr.cashinstart",
315 XfsMethodName::CashIn => "bnr.cashin",
316 XfsMethodName::CashInRollback => "bnr.cashinrollback",
317 XfsMethodName::CashInEnd => "bnr.cashinend",
318 XfsMethodName::GetIdentification => "module.getidentification",
319 XfsMethodName::GetStatus => "bnr.getstatus",
320 XfsMethodName::StopSession => "bnr.stopsession",
321 XfsMethodName::Reset => "bnr.reset",
322 XfsMethodName::Reboot => "bnr.reboot",
323 XfsMethodName::Cancel => "bnr.cancel",
324 XfsMethodName::Park => "bnr.park",
325 XfsMethodName::Empty => "bnr.empty",
326 XfsMethodName::Eject => "bnr.eject",
327 XfsMethodName::QueryCashUnit => "bnr.querycashunit",
328 XfsMethodName::ConfigureCashUnit => "bnr.configurecashunit",
329 XfsMethodName::UpdateCashUnit => "bnr.updatecashunit",
330 XfsMethodName::Denominate => "bnr.denominate",
331 XfsMethodName::Dispense => "bnr.dispense",
332 XfsMethodName::Present => "bnr.present",
333 XfsMethodName::CancelWaitingCashTaken => "bnr.cancelwaitingcashtaken",
334 XfsMethodName::Retract => "bnr.retract",
335 XfsMethodName::GetCapabilities => "bnr.getcapabilities",
336 XfsMethodName::SetCapabilities => "bnr.setcapabilities",
337 XfsMethodName::QueryDenominations => "bnr.querydenominations",
338 XfsMethodName::UpdateDenominations => "bnr.updatedenominations",
339 XfsMethodName::QueryBillsetIds => "bnr.querybillsetids",
340 XfsMethodName::GetBillAcceptanceHistory => "bnr.getbillacceptancehistory",
341 XfsMethodName::GetBillDispenseHistory => "bnr.getbilldispensehistory",
342 XfsMethodName::GetFailureHistory => "bnr.getfailurehistory",
343 XfsMethodName::GetRestartHistory => "bnr.getrestarthistory",
344 XfsMethodName::GetUseHistory => "bnr.getusehistory",
345 XfsMethodName::OperationCompleteOccurred => "BnrListener.operationCompleteOccured",
346 XfsMethodName::IntermediateOccurred => "BnrListener.intermediateOccured",
347 XfsMethodName::StatusOccurred => "BnrListener.statusOccured",
348 }
349 }
350}
351
352impl From<XfsMethodName> for &'static str {
353 fn from(val: XfsMethodName) -> Self {
354 (&val).into()
355 }
356}
357
358impl TryFrom<&str> for XfsMethodName {
359 type Error = Error;
360
361 fn try_from(val: &str) -> Result<Self> {
362 match val.to_lowercase().as_str() {
363 "bnr.getdatetime" => Ok(Self::GetDateTime),
364 "bnr.setdatetime" => Ok(Self::SetDateTime),
365 "bnr.cashinstart" => Ok(Self::CashInStart),
366 "bnr.cashin" => Ok(Self::CashIn),
367 "bnr.cashinrollback" => Ok(Self::CashInRollback),
368 "bnr.cashinend" => Ok(Self::CashInEnd),
369 "module.getidentification" => Ok(Self::GetIdentification),
370 "bnr.getstatus" => Ok(Self::GetStatus),
371 "bnr.stopsession" => Ok(Self::StopSession),
372 "bnr.reset" => Ok(Self::Reset),
373 "bnr.reboot" => Ok(Self::Reboot),
374 "bnr.cancel" => Ok(Self::Cancel),
375 "bnr.park" => Ok(Self::Park),
376 "bnr.empty" => Ok(Self::Empty),
377 "bnr.eject" => Ok(Self::Eject),
378 "bnr.querycashunit" => Ok(Self::QueryCashUnit),
379 "bnr.configurecashunit" => Ok(Self::ConfigureCashUnit),
380 "bnr.updatecashunit" => Ok(Self::UpdateCashUnit),
381 "bnr.denominate" => Ok(Self::Denominate),
382 "bnr.dispense" => Ok(Self::Dispense),
383 "bnr.present" => Ok(Self::Present),
384 "bnr.cancelwaitingcashtaken" => Ok(Self::CancelWaitingCashTaken),
385 "bnr.retract" => Ok(Self::Retract),
386 "bnr.getcapabilities" => Ok(Self::GetCapabilities),
387 "bnr.setcapabilities" => Ok(Self::SetCapabilities),
388 "bnr.querydenominations" => Ok(Self::QueryDenominations),
389 "bnr.updatedenominations" => Ok(Self::UpdateDenominations),
390 "bnr.querybillsetids" => Ok(Self::QueryBillsetIds),
391 "bnr.getbillacceptancehistory" => Ok(Self::GetBillAcceptanceHistory),
392 "bnr.getbilldispensehistory" => Ok(Self::GetBillDispenseHistory),
393 "bnr.getfailurehistory" => Ok(Self::GetFailureHistory),
394 "bnr.getrestarthistory" => Ok(Self::GetRestartHistory),
395 "bnr.getusehistory" => Ok(Self::GetUseHistory),
396 "bnrlistener.operationcompleteoccured" => Ok(Self::OperationCompleteOccurred),
397 "bnrlistener.intermediateoccured" => Ok(Self::IntermediateOccurred),
398 "bnrlistener.statusoccured" => Ok(Self::StatusOccurred),
399 _ => Err(Error::Parsing(format!("unknown method name: {val}"))),
400 }
401 }
402}
403
404impl fmt::Display for XfsMethodName {
405 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
406 write!(f, r#""{}""#, <&str>::from(self))
407 }
408}
409
410#[cfg(test)]
411mod tests {
412 use super::*;
413 use crate::{xfs, Result};
414
415 #[test]
416 fn test_method_call_serde() -> Result<()> {
417 let exp_xml = r#"<?xml version="1.0" encoding="UTF-8"?><methodCall><methodName></methodName><params/></methodCall>"#;
418 let xml_str = xfs::to_string(&XfsMethodCall::new())?;
419
420 assert_eq!(xml_str.as_str(), exp_xml);
421
422 let exp_xml = r#"<?xml version="1.0" encoding="UTF-8"?><methodCall><methodName>module.getidentification</methodName><params/></methodCall>"#;
423 let xml_str = xfs::to_string(&XfsMethodCall::from(XfsMethodName::GetIdentification))?;
424
425 assert_eq!(xml_str.as_str(), exp_xml);
426
427 Ok(())
428 }
429}