bnr_xfs/xfs/
value.rs

1//! XFS `value` types.
2
3use std::fmt;
4
5use super::{array::XfsArray, xfs_struct::XfsStruct};
6
7/// Represents an XFS parameter `value`.
8#[derive(Clone, Debug, PartialEq, serde::Deserialize)]
9#[serde(rename = "value")]
10pub struct XfsValue {
11    /// Represents a 64-bit signed integer value.
12    #[serde(default)]
13    pub(crate) int: Option<i64>,
14    /// Represents a 32-bit signed integer value.
15    #[serde(default)]
16    pub(crate) i4: Option<i32>,
17    /// Represents a Base-64 encoded string value.
18    #[serde(default)]
19    pub(crate) base64: Option<String>,
20    /// Represents a date-time entry value, encoded according to ISO-8601: <https://en.wikipedia.org/wiki/ISO_8601>.
21    #[serde(rename = "dateTime.iso8601", default)]
22    pub(crate) date_time: Option<String>,
23    /// Boolean integer type:
24    ///
25    /// - `0` is false
26    /// - `1` is true
27    #[serde(default)]
28    pub(crate) boolean: Option<u8>,
29    /// String value.
30    #[serde(default)]
31    pub(crate) string: Option<String>,
32    /// XFS `struct` value.
33    #[serde(rename = "struct", default)]
34    pub(crate) xfs_struct: Option<XfsStruct>,
35    /// XFS `array` value.
36    #[serde(default)]
37    pub(crate) array: Option<XfsArray>,
38}
39
40impl XfsValue {
41    /// Creates a new [XfsValue].
42    pub const fn new() -> Self {
43        Self {
44            int: None,
45            i4: None,
46            base64: None,
47            date_time: None,
48            boolean: None,
49            string: None,
50            xfs_struct: None,
51            array: None,
52        }
53    }
54
55    /// Gets whether all fields are empty.
56    pub const fn is_empty(&self) -> bool {
57        self.int.is_none()
58            && self.i4.is_none()
59            && self.base64.is_none()
60            && self.date_time.is_none()
61            && self.boolean.is_none()
62            && self.string.is_none()
63            && self.xfs_struct.is_none()
64            && self.array.is_none()
65    }
66
67    /// Gets an optional reference to the `int` field.
68    pub const fn int(&self) -> Option<&i64> {
69        self.int.as_ref()
70    }
71
72    /// Sets the `int` field.
73    pub fn set_int(&mut self, int: i64) {
74        self.int = Some(int);
75    }
76
77    /// Unsets the `int` field.
78    pub fn unset_int(&mut self) -> Option<i64> {
79        self.int.take()
80    }
81
82    /// Builder function that sets the `int` field.
83    pub fn with_int(mut self, int: i64) -> Self {
84        self.set_int(int);
85        self
86    }
87
88    /// Gets an optional reference to the `i4` field.
89    pub const fn i4(&self) -> Option<&i32> {
90        self.i4.as_ref()
91    }
92
93    /// Sets the `i4` field.
94    pub fn set_i4(&mut self, i4: i32) {
95        self.i4 = Some(i4);
96    }
97
98    /// Unsets the `i4` field.
99    pub fn unset_i4(&mut self) -> Option<i32> {
100        self.i4.take()
101    }
102
103    /// Builder function that sets the `i4` field.
104    pub fn with_i4(mut self, i4: i32) -> Self {
105        self.set_i4(i4);
106        self
107    }
108
109    /// Gets an optional reference to the `base64` field.
110    pub fn base64(&self) -> Option<&str> {
111        self.base64.as_deref()
112    }
113
114    /// Sets the `base64` field.
115    pub fn set_base64<S: Into<String>>(&mut self, base64: S) {
116        self.base64 = Some(base64.into());
117    }
118
119    /// Unsets the `base64` field.
120    pub fn unset_base64(&mut self) -> Option<String> {
121        self.base64.take()
122    }
123
124    /// Builder function that sets the `base64` field.
125    pub fn with_base64<S: Into<String>>(mut self, base64: S) -> Self {
126        self.set_base64(base64);
127        self
128    }
129
130    /// Gets an optional reference to the `date_time` field.
131    pub fn date_time(&self) -> Option<&str> {
132        self.date_time.as_deref()
133    }
134
135    /// Sets the `date_time` field.
136    pub fn set_date_time<S: Into<String>>(&mut self, date_time: S) {
137        self.date_time = Some(date_time.into());
138    }
139
140    /// Unsets the `date_time` field.
141    pub fn unset_date_time(&mut self) -> Option<String> {
142        self.date_time.take()
143    }
144
145    /// Builder function that sets the `date_time` field.
146    pub fn with_date_time<S: Into<String>>(mut self, date_time: S) -> Self {
147        self.set_date_time(date_time);
148        self
149    }
150
151    /// Gets an optional reference to the `string` field.
152    pub fn string(&self) -> Option<&str> {
153        self.string.as_deref()
154    }
155
156    /// Sets the `string` field.
157    pub fn set_string<S: Into<String>>(&mut self, string: S) {
158        self.string = Some(string.into());
159    }
160
161    /// Unsets the `string` field.
162    pub fn unset_string(&mut self) -> Option<String> {
163        self.string.take()
164    }
165
166    /// Builder function that sets the `string` field.
167    pub fn with_string<S: Into<String>>(mut self, string: S) -> Self {
168        self.set_string(string);
169        self
170    }
171
172    /// Gets an optional reference to the `struct` field.
173    pub const fn xfs_struct(&self) -> Option<&XfsStruct> {
174        self.xfs_struct.as_ref()
175    }
176
177    /// Sets the `struct` field.
178    pub fn set_xfs_struct(&mut self, xfs_struct: XfsStruct) {
179        self.xfs_struct = Some(xfs_struct);
180    }
181
182    /// Unsets the `struct` field.
183    pub fn unset_xfs_struct(&mut self) -> Option<XfsStruct> {
184        self.xfs_struct.take()
185    }
186
187    /// Builder function that sets the `struct` field.
188    pub fn with_xfs_struct(mut self, xfs_struct: XfsStruct) -> Self {
189        self.set_xfs_struct(xfs_struct);
190        self
191    }
192
193    /// Gets an optional reference to the `boolean` field.
194    pub const fn boolean(&self) -> Option<&u8> {
195        self.boolean.as_ref()
196    }
197
198    /// Sets the `boolean` field.
199    pub fn set_boolean(&mut self, boolean: u8) {
200        self.boolean = Some(boolean);
201    }
202
203    /// Unsets the `boolean` field.
204    pub fn unset_boolean(&mut self) -> Option<u8> {
205        self.boolean.take()
206    }
207
208    /// Builder function that sets the `boolean` field.
209    pub fn with_boolean(mut self, boolean: u8) -> Self {
210        self.set_boolean(boolean);
211        self
212    }
213
214    /// Gets an optional reference to the `array` field.
215    pub const fn array(&self) -> Option<&XfsArray> {
216        self.array.as_ref()
217    }
218
219    /// Sets the `array` field.
220    pub fn set_array(&mut self, xfs_array: XfsArray) {
221        self.array = Some(xfs_array);
222    }
223
224    /// Unsets the `array` field.
225    pub fn unset_array(&mut self) -> Option<XfsArray> {
226        self.array.take()
227    }
228
229    /// Builder function that sets the `array` field.
230    pub fn with_array(mut self, xfs_array: XfsArray) -> Self {
231        self.set_array(xfs_array);
232        self
233    }
234}
235
236impl serde::Serialize for XfsValue {
237    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
238    where
239        S: serde::Serializer,
240    {
241        use serde::ser::SerializeStruct;
242
243        let mut ser = serializer.serialize_struct("value", 8)?;
244
245        if let Some(v) = self.int() {
246            ser.serialize_field("int", v)?;
247        }
248
249        if let Some(v) = self.i4() {
250            ser.serialize_field("i4", v)?;
251        }
252
253        if let Some(v) = self.base64() {
254            ser.serialize_field("base64", v)?;
255        }
256
257        if let Some(v) = self.date_time() {
258            ser.serialize_field("dateTime.iso8601", v)?;
259        }
260
261        if let Some(v) = self.boolean() {
262            ser.serialize_field("boolean", v)?;
263        }
264
265        if let Some(v) = self.string() {
266            ser.serialize_field("string", v)?;
267        }
268
269        if let Some(v) = self.xfs_struct() {
270            ser.serialize_field("struct", v)?;
271        }
272
273        if let Some(v) = self.array() {
274            ser.serialize_field("array", v)?;
275        }
276
277        ser.end()
278    }
279}
280
281impl_default!(XfsValue);
282
283impl fmt::Display for XfsValue {
284    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
285        write!(f, "{{")?;
286        let mut has_field = false;
287        if let Some(v) = self.int() {
288            write!(f, r#""int": {v}"#)?;
289            has_field = true;
290        }
291        if let Some(v) = self.i4() {
292            if has_field {
293                write!(f, ", ")?;
294            }
295            write!(f, r#""i4": {v}"#)?;
296            has_field = true;
297        }
298        if let Some(v) = self.base64() {
299            if has_field {
300                write!(f, ", ")?;
301            }
302            write!(f, r#""base64": {v}"#)?;
303            has_field = true;
304        }
305        if let Some(v) = self.date_time() {
306            if has_field {
307                write!(f, ", ")?;
308            }
309            write!(f, r#""date_time": {v}"#)?;
310            has_field = true;
311        }
312        if let Some(v) = self.boolean() {
313            if has_field {
314                write!(f, ", ")?;
315            }
316            write!(f, r#""boolean": {v}"#)?;
317            has_field = true;
318        }
319        if let Some(v) = self.string() {
320            if has_field {
321                write!(f, ", ")?;
322            }
323            write!(f, r#""string": "{v}""#)?;
324            has_field = true;
325        }
326        if let Some(v) = self.xfs_struct() {
327            if has_field {
328                write!(f, ", ")?;
329            }
330            write!(f, r#""struct": {v}"#)?;
331            has_field = true;
332        }
333        if let Some(v) = self.array() {
334            if has_field {
335                write!(f, ", ")?;
336            }
337            write!(f, r#""array": {v}"#)?;
338        }
339        write!(f, "}}")
340    }
341}
342
343/// Wrapper type for `serde-xml-rs` to properly encode [XfsValue] in collections.
344#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
345pub enum ListValue {
346    #[serde(rename = "value")]
347    Value(XfsValue),
348}
349
350impl ListValue {
351    /// Creates a new [ListValue].
352    pub const fn new() -> Self {
353        Self::Value(XfsValue::new())
354    }
355
356    /// Creates a new [ListValue] from the provided [XfsValue].
357    pub const fn create(value: XfsValue) -> Self {
358        Self::Value(value)
359    }
360
361    /// Gets a reference to the inner [XfsValue].
362    pub const fn inner(&self) -> &XfsValue {
363        match self {
364            Self::Value(v) => v,
365        }
366    }
367
368    /// Converts into the inner [XfsValue].
369    pub fn into_inner(self) -> XfsValue {
370        match self {
371            Self::Value(v) => v,
372        }
373    }
374}
375
376impl From<XfsValue> for ListValue {
377    fn from(val: XfsValue) -> Self {
378        Self::create(val)
379    }
380}
381
382impl From<&XfsValue> for ListValue {
383    fn from(val: &XfsValue) -> Self {
384        val.clone().into()
385    }
386}
387
388impl From<ListValue> for XfsValue {
389    fn from(val: ListValue) -> Self {
390        val.into_inner()
391    }
392}
393
394impl From<&ListValue> for XfsValue {
395    fn from(val: &ListValue) -> Self {
396        val.clone().into()
397    }
398}
399
400impl fmt::Display for ListValue {
401    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
402        let v = self.inner();
403        write!(f, "{v}")
404    }
405}
406
407#[cfg(test)]
408mod tests {
409    use super::*;
410    use crate::{
411        xfs::{self, xfs_struct::XfsMember},
412        Result,
413    };
414
415    #[test]
416    fn test_value_serde() -> Result<()> {
417        let exp_xml = r#"<?xml version="1.0" encoding="UTF-8"?><value/>"#;
418        let exp_val = XfsValue::new();
419
420        assert_eq!(xfs::to_string(&exp_val)?.as_str(), exp_xml);
421        assert_eq!(xfs::from_str::<XfsValue>(exp_xml)?, exp_val);
422
423        let exp_xml = r#"<?xml version="1.0" encoding="UTF-8"?><value><i4>-1</i4></value>"#;
424        let exp_val = XfsValue::new().with_i4(-1);
425
426        assert_eq!(xfs::to_string(&exp_val)?.as_str(), exp_xml);
427
428        Ok(())
429    }
430
431    #[test]
432    fn test_value_list_serde() -> Result<()> {
433        #[derive(Debug, PartialEq, serde::Deserialize, serde::Serialize)]
434        #[serde(rename = "list")]
435        struct List {
436            #[serde(rename = "$value")]
437            value: Vec<Value>,
438        }
439
440        #[derive(Debug, PartialEq, serde::Deserialize, serde::Serialize)]
441        enum Value {
442            #[serde(rename = "value")]
443            Value(XfsValue),
444        }
445
446        let exp_xml = r#"<?xml version="1.0" encoding="UTF-8"?><list><value/></list>"#;
447        let exp_list = List {
448            value: vec![Value::Value(XfsValue::new())],
449        };
450
451        assert_eq!(xfs::to_string(&exp_list)?.as_str(), exp_xml);
452        assert_eq!(xfs::from_str::<List>(exp_xml)?, exp_list);
453
454        Ok(())
455    }
456
457    #[test]
458    fn test_value_struct_serde() -> Result<()> {
459        let exp_xml = r#"<?xml version="1.0" encoding="UTF-8"?><value><struct><member><name>moduleType</name><value><i4>0</i4></value></member></struct></value>"#;
460        let exp_val = ListValue::create(XfsValue::new().with_xfs_struct(XfsStruct::create([
461            XfsMember::create("moduleType", XfsValue::new().with_i4(0)),
462        ])));
463
464        assert_eq!(xfs::from_str::<ListValue>(exp_xml)?, exp_val);
465
466        Ok(())
467    }
468
469    #[test]
470    fn test_array_serde() -> Result<()> {
471        let exp_xml = r#"<?xml version="1.0" encoding="UTF-8"?><array><data><value><struct><member><name>moduleType</name><value><i4>0</i4></value></member></struct></value></data></array>"#;
472        let exp_arr = XfsArray::create([XfsValue::new().with_xfs_struct(XfsStruct::create([
473            XfsMember::create("moduleType", XfsValue::new().with_i4(0)),
474        ]))]);
475
476        assert_eq!(xfs::from_str::<XfsArray>(exp_xml)?, exp_arr);
477        assert_eq!(xfs::to_string(exp_arr)?.as_str(), exp_xml);
478
479        let exp_xml = r#"<?xml version="1.0" encoding="UTF-8"?><value><array><data><value><struct><member><name>moduleType</name><value><i4>0</i4></value></member></struct></value></data></array></value>"#;
480        let exp_val = ListValue::Value(XfsValue::new().with_array(XfsArray::create([
481            XfsValue::new().with_xfs_struct(XfsStruct::create([XfsMember::create(
482                "moduleType",
483                XfsValue::new().with_i4(0),
484            )])),
485        ])));
486
487        assert_eq!(xfs::from_str::<ListValue>(exp_xml)?, exp_val);
488        assert_eq!(xfs::to_string(exp_val)?.as_str(), exp_xml);
489
490        Ok(())
491    }
492
493    #[test]
494    fn test_nested_array_serde() -> Result<()> {
495        let exp_xml = r#"<?xml version="1.0"?><value><array><data><value><struct><member><name>moduleType</name><value><array><data><value><struct><member><name>innerModule</name><value><i4>0</i4></value></member></struct></value></data></array></value></member></struct></value></data></array></value>"#;
496        let exp_val =
497            ListValue::Value(
498                XfsValue::new().with_array(XfsArray::create([XfsValue::new().with_xfs_struct(
499                    XfsStruct::create([XfsMember::create(
500                        "moduleType",
501                        XfsValue::new().with_array(XfsArray::create(
502                            [XfsValue::new().with_xfs_struct(XfsStruct::create([
503                                XfsMember::create("innerModule", XfsValue::new().with_i4(0)),
504                            ]))]
505                            .into_iter(),
506                        )),
507                    )]),
508                )])),
509            );
510
511        assert_eq!(xfs::from_str::<ListValue>(exp_xml)?, exp_val);
512
513        Ok(())
514    }
515
516    #[test]
517    fn test_nested_struct_serde() -> Result<()> {
518        let exp_xml = r#"<?xml version="1.0"?>
519        <value>
520          <struct>
521            <member>
522              <name>outerStruct</name>
523              <value>
524                <array>
525                  <data>
526                    <value>
527                      <struct>
528                        <member>
529                          <name>innerStruct</name>
530                          <value>
531                            <array>
532                              <data>
533                                <value>
534                                  <i4>0</i4>
535                                </value>
536                                <value>
537                                  <i4>1</i4>
538                                </value>
539                              </data>
540                            </array>
541                          </value>
542                        </member>
543                      </struct>
544                    </value>
545                  </data>
546                </array>
547              </value>
548            </member>
549          </struct>
550        </value>
551        "#;
552
553        xfs::from_str::<ListValue>(exp_xml)?;
554
555        Ok(())
556    }
557}