musicxml/datatypes/
ending_number.rs

1use alloc::{string::String, string::ToString, vec::Vec};
2use core::ops::Deref;
3use musicxml_internal::{DatatypeDeserializer, DatatypeSerializer};
4use musicxml_macros::DatatypeSerialize;
5use regex::Regex;
6
7/// Used to specify either a comma-separated list of positive integers without leading zeros, or a string of zero or more spaces.
8///
9/// It is used for the `number` attribute of the [Ending][crate::elements::Ending] element.
10///
11/// The "zero or more spaces" version is used when software knows that an ending is present, but cannot determine the type of the ending.
12///
13/// The value of an instance of this type may be accessed by dereferencing the struct: `*datatype_val`.
14#[derive(Debug, PartialEq, Eq, DatatypeSerialize)]
15pub struct EndingNumber(pub String);
16
17impl Deref for EndingNumber {
18  type Target = String;
19  fn deref(&self) -> &Self::Target {
20    &self.0
21  }
22}
23
24impl DatatypeDeserializer for EndingNumber {
25  fn deserialize(value: &str) -> Result<Self, String> {
26    let space_regex = Regex::new(r"^[ ]*$").unwrap();
27    let integer_regex = Regex::new(r"^[1-9][0-9]*(, ?[1-9][0-9]*)*$").unwrap();
28    if space_regex.is_match(value) {
29      Ok(EndingNumber(String::from(value)))
30    } else if integer_regex.is_match(value) {
31      Ok(EndingNumber(value.split(' ').collect::<Vec<_>>().join("")))
32    } else {
33      Err(format!("Value {value} is invalid for the <ending-number> data type"))
34    }
35  }
36}
37
38#[cfg(test)]
39mod ending_number_tests {
40  use super::*;
41
42  #[test]
43  fn deserialize_valid1() {
44    let result = EndingNumber::deserialize("1");
45    assert!(result.is_ok());
46    assert_eq!(result.unwrap(), EndingNumber(String::from("1")));
47  }
48
49  #[test]
50  fn deserialize_valid2() {
51    let result = EndingNumber::deserialize("33");
52    assert!(result.is_ok());
53    assert_eq!(result.unwrap(), EndingNumber(String::from("33")));
54  }
55
56  #[test]
57  fn deserialize_valid3() {
58    let result = EndingNumber::deserialize("348,21");
59    assert!(result.is_ok());
60    assert_eq!(result.unwrap(), EndingNumber(String::from("348,21")));
61  }
62
63  #[test]
64  fn deserialize_valid4() {
65    let result = EndingNumber::deserialize("9, 23, 34,34");
66    assert!(result.is_ok());
67    assert_eq!(result.unwrap(), EndingNumber(String::from("9,23,34,34")));
68  }
69
70  #[test]
71  fn deserialize_invalid1() {
72    let result = EndingNumber::deserialize("02");
73    assert!(result.is_err());
74  }
75
76  #[test]
77  fn deserialize_invalid2() {
78    let result = EndingNumber::deserialize("4,");
79    assert!(result.is_err());
80  }
81
82  #[test]
83  fn deserialize_invalid3() {
84    let result = EndingNumber::deserialize("a");
85    assert!(result.is_err());
86  }
87
88  #[test]
89  fn deserialize_invalid4() {
90    let result = EndingNumber::deserialize(",33");
91    assert!(result.is_err());
92  }
93
94  #[test]
95  fn deserialize_invalid5() {
96    let result = EndingNumber::deserialize("23,33,  2");
97    assert!(result.is_err());
98  }
99}