bnr_xfs/xfs/
method_response.rs

1//! XFS `method response` types.
2
3use std::fmt;
4
5use super::fault::XfsFault;
6use super::params::{XfsParam, XfsParams};
7use crate::{Error, Result};
8
9/// Represents an XFS method response containing (one of):
10///
11/// - a list of [params](XfsParams).
12/// - an XFS [fault](XfsFault).
13#[repr(C)]
14#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
15#[serde(rename = "methodResponse")]
16pub enum XfsMethodResponse {
17    #[serde(rename = "params")]
18    Params(XfsParams),
19    #[serde(rename = "fault")]
20    Fault(XfsFault),
21}
22
23impl XfsMethodResponse {
24    /// Creates a new [XfsMethodResponse::Params] variant with the provided [XfsParams].
25    pub fn new_params<P: Into<Vec<XfsParam>>>(params: P) -> Self {
26        Self::Params(XfsParams::create(params.into()))
27    }
28
29    /// Creates a new [XfsMethodResponse::Fault] variant with the provided fault code.
30    pub fn new_fault<S: Into<String>>(code: i32, string: S) -> Self {
31        Self::Fault(XfsFault::create(code, string))
32    }
33
34    /// Gets the async callback ID from the [XfsMethodResponse].
35    ///
36    /// Returns: `Ok(i32)` on success, `Err(Error)` on failure
37    pub fn call_id(&self) -> Result<i32> {
38        self.as_params()?
39            .params()
40            .iter()
41            .find(|&p| p.inner().value().i4().is_some())
42            .ok_or(Error::Xfs("missing response ID".into()))?
43            .inner()
44            .value()
45            .i4()
46            .cloned()
47            .ok_or(Error::Xfs("missing response ID".into()))
48    }
49}
50
51inner_enum!(XfsMethodResponse, Params, XfsParams);
52inner_enum!(XfsMethodResponse, Fault, XfsFault);
53
54impl fmt::Display for XfsMethodResponse {
55    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56        match self {
57            Self::Params(res) => write!(f, "{res}"),
58            Self::Fault(res) => write!(f, "{res}"),
59        }
60    }
61}
62
63/// Represents an XFS method response wrapper around [XfsMethodResponse].
64///
65/// This is an implementation detail for correct serialization.
66#[repr(C)]
67#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
68#[serde(rename = "methodResponse")]
69pub struct XfsMethodResponseStruct {
70    #[serde(rename = "$value")]
71    response: XfsMethodResponse,
72}
73
74impl XfsMethodResponseStruct {
75    /// Creates a new [XfsMethodResponseStruct] wrapper.
76    pub const fn new(response: XfsMethodResponse) -> Self {
77        Self { response }
78    }
79
80    /// Gets a reference to the [XfsMethodResponseStruct] inner representation.
81    pub const fn inner(&self) -> &XfsMethodResponse {
82        &self.response
83    }
84
85    /// Gets a mutable reference to the [XfsMethodResponseStruct] inner representation.
86    pub fn inner_mut(&mut self) -> &mut XfsMethodResponse {
87        &mut self.response
88    }
89
90    /// Converts the [XfsMethodResponseStruct] to its inner representation.
91    pub fn into_inner(self) -> XfsMethodResponse {
92        self.response
93    }
94}
95
96impl From<XfsMethodResponse> for XfsMethodResponseStruct {
97    fn from(val: XfsMethodResponse) -> Self {
98        Self::new(val)
99    }
100}
101
102impl From<&XfsMethodResponse> for XfsMethodResponseStruct {
103    fn from(val: &XfsMethodResponse) -> Self {
104        Self::new(val.clone())
105    }
106}
107
108impl From<XfsMethodResponseStruct> for XfsMethodResponse {
109    fn from(val: XfsMethodResponseStruct) -> Self {
110        val.into_inner()
111    }
112}
113
114impl From<&XfsMethodResponseStruct> for XfsMethodResponse {
115    fn from(val: &XfsMethodResponseStruct) -> Self {
116        val.clone().into()
117    }
118}
119
120impl fmt::Display for XfsMethodResponseStruct {
121    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122        let s = self.inner();
123        write!(f, "{s}")
124    }
125}
126
127#[cfg(test)]
128mod tests {
129    use super::*;
130    use crate::{
131        xfs::{self, value::XfsValue},
132        Result,
133    };
134
135    #[test]
136    fn test_method_response_serde() -> Result<()> {
137        let exp_xml = r#"<?xml version="1.0" encoding="UTF-8"?><methodResponse><fault><value><struct><member><name>faultCode</name><value><i4>6072</i4></value></member><member><name>faultString</name><value><string></string></value></member></struct></value></fault></methodResponse>"#;
138        let res = XfsMethodResponseStruct::from(XfsMethodResponse::new_fault(6072, ""));
139
140        assert_eq!(xfs::to_string(&res)?.as_str(), exp_xml);
141        assert_eq!(xfs::from_str::<XfsMethodResponseStruct>(exp_xml)?, res);
142
143        let exp_xml = r#"<?xml version="1.0" encoding="UTF-8"?><methodResponse><params><param><value><i4>32</i4></value></param></params></methodResponse>"#;
144        let res = XfsMethodResponseStruct::from(XfsMethodResponse::new_params([XfsParam::create(
145            XfsValue::new().with_i4(32),
146        )]));
147
148        assert_eq!(xfs::to_string(&res)?.as_str(), exp_xml);
149        assert_eq!(xfs::from_str::<XfsMethodResponseStruct>(exp_xml)?, res);
150
151        Ok(())
152    }
153
154    #[test]
155    fn test_method_response_accessors() -> Result<()> {
156        let fault = XfsMethodResponse::new_fault(1010, "");
157        let exp_fault = XfsFault::create(1010, "");
158
159        assert!(fault.is_fault());
160        assert_eq!(fault.as_fault()?, &exp_fault);
161        assert_eq!(fault.into_fault()?, exp_fault);
162
163        let params = XfsMethodResponse::new_params([XfsParam::create(XfsValue::new().with_i4(32))]);
164        let exp_params = XfsParams::create([XfsParam::create(XfsValue::new().with_i4(32))]);
165
166        assert!(params.is_params());
167        assert_eq!(params.as_params()?, &exp_params);
168        assert_eq!(params.into_params()?, exp_params);
169
170        Ok(())
171    }
172
173    #[test]
174    fn test_nested_response() -> Result<()> {
175        let fuck_xml = r#"<?xml version="1.0"?>
176        <methodResponse>
177          <params>
178            <param>
179              <value>
180                <struct>
181                  <member>
182                    <name>
183                      outerStruct
184                    </name>
185                    <value>
186                      <array>
187                        <data>
188                          <value>
189                            <struct>
190                              <member>
191                                <name>
192                                  innerStruct
193                                </name>
194                                <value>
195                                  <array>
196                                    <data>
197                                      <value>
198                                        <i4>0</i4>
199                                      </value>
200                                      <value>
201                                        <i4>1</i4>
202                                      </value>
203                                    </data>
204                                  </array>
205                                </value>
206                              </member>
207                            </struct>
208                          </value>
209                        </data>
210                      </array>
211                    </value>
212                  </member>
213                </struct>
214              </value>
215            </param>
216          </params>
217        </methodResponse>
218        "#;
219
220        xfs::from_str::<XfsMethodResponseStruct>(fuck_xml)?;
221
222        Ok(())
223    }
224
225    #[test]
226    fn test_null_param_value_serde() -> Result<()> {
227        let exp_xml = r#"<?xml version="1.0" encoding="UTF-8"?><methodResponse><params><param><value/></param></params></methodResponse>"#;
228        let exp_res =
229            XfsMethodResponseStruct::from(XfsMethodResponse::Params(XfsParams::create([
230                XfsParam::new(),
231            ])));
232
233        assert_eq!(xfs::to_string(&exp_res)?.as_str(), exp_xml);
234        assert_eq!(xfs::from_str::<XfsMethodResponseStruct>(exp_xml)?, exp_res);
235
236        Ok(())
237    }
238}