rupnp/scpd/
state_variable.rs

1use crate::{find_in_xml, utils, Error, Result};
2use roxmltree::Node;
3use std::fmt;
4use std::fmt::Formatter;
5
6/// A `StateVariable` is the type of every [Argument](struct.Argument.html) in UPnP Actions.
7/// It is either a single value, an enumeration of strings or an integer range: see
8/// [StateVariableKind](enum.StateVariableKind.html).
9#[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/// The range of a StateVariable
20#[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/// The type of a state variable.
53#[derive(Debug)]
54pub enum StateVariableKind {
55    /// Just a value of some datatype
56    Simple(DataType),
57    /// An enumeration of possible strings. Can have a default value.
58    Enum(Vec<String>),
59    /// A Range of values.
60    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}