1use crate::{find_in_xml, utils, Error, Result};
2use roxmltree::Node;
3use std::fmt;
4use std::fmt::Formatter;
5
6#[derive(Debug)]
10pub struct StateVariable {
11 name: String,
12 default: Option<String>,
13 kind: StateVariableKind,
14 send_events: bool,
15 multicast: bool,
16 optional: bool,
17}
18
19#[derive(Debug)]
21pub struct StateVariableRange {
22 minimum: String,
23 maximum: String,
24 step: Option<String>,
25}
26
27impl fmt::Display for StateVariableRange {
28 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
29 write!(f, "{}..={}", self.minimum(), self.maximum())?;
30 if let Some(step) = self.step() {
31 write!(f, ":{step}")?;
32 }
33
34 Ok(())
35 }
36}
37
38impl StateVariableRange {
39 pub fn minimum(&self) -> &str {
40 &self.minimum
41 }
42
43 pub fn maximum(&self) -> &str {
44 &self.maximum
45 }
46
47 pub fn step(&self) -> Option<&str> {
48 self.step.as_deref()
49 }
50}
51
52#[derive(Debug)]
54pub enum StateVariableKind {
55 Simple(DataType),
57 Enum(Vec<String>),
59 Range(StateVariableRange),
61}
62
63impl fmt::Display for StateVariable {
64 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65 write!(f, "{}", self.name())
66 }
67}
68
69impl StateVariable {
70 pub(crate) fn from_xml(node: Node<'_, '_>) -> Result<Self> {
71 #[allow(non_snake_case)]
72 let (name, datatype, default, variants, range, optional) = find_in_xml! { node => name, dataType, ?defaultValue, ?allowedValueList, ?allowedValueRange, ?optional };
73
74 let variants = variants
75 .map(|a| {
76 a.children()
77 .filter(Node::is_element)
78 .map(utils::parse_node_text)
79 .collect()
80 })
81 .transpose()?;
82
83 let default = default.map(utils::parse_node_text).transpose()?;
84 let range = range.map(range_from_xml).transpose()?;
85
86 let name = utils::parse_node_text(name)?;
87 let datatype = utils::parse_node_text(datatype)?;
88 let optional = optional.is_some();
89
90 let send_events = utils::find_node_attribute(node, "sendEvents")
91 .map(|val| val == "yes")
92 .unwrap_or(true);
93 let multicast = utils::find_node_attribute(node, "multicast")
94 .map(|val| val == "yes")
95 .unwrap_or(false);
96
97 let kind = match (variants, range) {
98 (None, None) => Ok(StateVariableKind::Simple(datatype)),
99 (Some(variants), None) => Ok(StateVariableKind::Enum(variants)),
100 (None, Some(range)) => Ok(StateVariableKind::Range(range)),
101 (Some(_), Some(_)) => Err(Error::ParseError(
102 "both `AllowedValues` and `AllowedValueRange` is set",
103 )),
104 }?;
105
106 Ok(StateVariable {
107 name,
108 kind,
109 default,
110 optional,
111 send_events,
112 multicast,
113 })
114 }
115
116 pub fn name(&self) -> &str {
117 &self.name
118 }
119
120 pub fn default(&self) -> Option<&str> {
121 self.default.as_deref()
122 }
123
124 pub fn optional(&self) -> bool {
125 self.optional
126 }
127
128 pub fn sends_events(&self) -> bool {
129 self.send_events
130 }
131
132 pub fn is_multicast(&self) -> bool {
133 self.multicast
134 }
135
136 pub fn kind(&self) -> &StateVariableKind {
137 &self.kind
138 }
139}
140
141#[derive(Debug, Copy, Clone, Eq, PartialEq)]
142#[allow(non_camel_case_types)]
143pub enum DataType {
144 ui1,
145 ui2,
146 ui4,
147 ui8,
148 i1,
149 i2,
150 i4,
151 int,
152 r4,
153 r8,
154 Number,
155 Float,
156 Fixed14_4,
157 Char,
158 String,
159 Date,
160 DateTime,
161 DateTimeTz,
162 Time,
163 TimeTz,
164 Boolean,
165 BinBase64,
166 BinHex,
167 Uri,
168}
169
170impl fmt::Display for DataType {
171 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
172 fmt::Debug::fmt(self, f)
173 }
174}
175
176#[derive(Debug)]
177pub struct ParseDataTypeErr(String);
178impl fmt::Display for ParseDataTypeErr {
179 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
180 write!(f, "invalid data type: {}", self.0)
181 }
182}
183impl std::error::Error for ParseDataTypeErr {}
184impl std::str::FromStr for DataType {
185 type Err = ParseDataTypeErr;
186
187 fn from_str(s: &str) -> Result<Self, Self::Err> {
188 match s {
189 "ui1" => Ok(DataType::ui1),
190 "ui2" => Ok(DataType::ui2),
191 "ui4" => Ok(DataType::ui4),
192 "ui8" => Ok(DataType::ui8),
193 "i1" => Ok(DataType::i1),
194 "i2" => Ok(DataType::i2),
195 "i4" => Ok(DataType::i4),
196 "int" => Ok(DataType::int),
197 "r4" => Ok(DataType::r4),
198 "r8" => Ok(DataType::r8),
199 "number" => Ok(DataType::Number),
200 "float" => Ok(DataType::Float),
201 "fixed14_4" => Ok(DataType::Fixed14_4),
202 "char" => Ok(DataType::Char),
203 "string" => Ok(DataType::String),
204 "date" => Ok(DataType::Date),
205 "dateTime" => Ok(DataType::DateTime),
206 "dateTimeTz" => Ok(DataType::DateTimeTz),
207 "time" => Ok(DataType::Time),
208 "timeTz" => Ok(DataType::TimeTz),
209 "boolean" => Ok(DataType::Boolean),
210 "bin.base64" => Ok(DataType::BinBase64),
211 "bin.hex" => Ok(DataType::BinHex),
212 "uri" => Ok(DataType::Uri),
213 _ => Err(ParseDataTypeErr(s.to_string())),
214 }
215 }
216}
217
218fn range_from_xml(node: Node<'_, '_>) -> Result<StateVariableRange> {
219 #[allow(non_snake_case)]
220 let (minimum, maximum, step) = find_in_xml! { node => minimum, maximum, ?step };
221
222 let step = step.map(utils::parse_node_text).transpose()?;
223 let minimum = utils::parse_node_text(minimum)?;
224 let maximum = utils::parse_node_text(maximum)?;
225
226 Ok(StateVariableRange {
227 minimum,
228 maximum,
229 step,
230 })
231}