1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
//! Raw response data types.
use serde::{Deserialize, Deserializer};
use serde_json::Value;
use std::collections::VecDeque;
use crate::command::CommandError;
fn deserialize_result<'de, D: Deserializer<'de>>(d: D) -> Result<Option<bool>, D::Error> {
let result_str = Option::<String>::deserialize(d)?;
match result_str {
Some(result_str) => {
match result_str.as_str() {
"success" => Ok(Some(true)),
"fail" => Ok(Some(false)),
other => Err(serde::de::Error::custom(format!("unknown result str: '{other}'"))),
}
},
None => Ok(None),
}
}
enum RecursiveJsonStringIterInner<'a> {
Exhausted,
//String(&'a mut String),
Single(&'a mut Value),
Many(VecDeque<RecursiveJsonStringIter<'a>>),
}
struct RecursiveJsonStringIter<'a> {
inner: RecursiveJsonStringIterInner<'a>,
}
impl<'a> RecursiveJsonStringIter<'a> {
fn new(value: &'a mut Value) -> Self {
Self {
inner: RecursiveJsonStringIterInner::Single(value),
}
}
}
fn find_first_in_many<'a>(many: &mut VecDeque<RecursiveJsonStringIter<'a>>) -> Option<&'a mut String> {
let mut output = None;
while output.is_none() && !many.is_empty() {
output = many.front_mut().unwrap().next();
if output.is_none() {
many.pop_front();
}
}
output
}
impl<'a> Iterator for RecursiveJsonStringIter<'a> {
type Item = &'a mut String;
fn next(&mut self) -> Option<Self::Item> {
let mut inner = RecursiveJsonStringIterInner::Exhausted;
std::mem::swap(&mut inner, &mut self.inner);
let mut handle_many = |mut many: VecDeque<RecursiveJsonStringIter<'a>>| {
let output = find_first_in_many(&mut many);
if !many.is_empty() {
self.inner = RecursiveJsonStringIterInner::Many(many);
}
output
};
match inner {
RecursiveJsonStringIterInner::Exhausted => None,
RecursiveJsonStringIterInner::Single(value) => {
match value {
Value::Array(values) => {
let many = values.iter_mut()
.map(|value| Self::new(value))
.collect::<VecDeque<_>>();
handle_many(many)
},
Value::Object(values) => {
let many = values.values_mut()
.map(|value| Self::new(value))
.collect::<VecDeque<_>>();
handle_many(many)
}
Value::String(value) => Some(value),
// The other values don't represent a string, so consider this iter exhausted
_ => None,
}
},
RecursiveJsonStringIterInner::Many(many) => {
handle_many(many)
}
}
}
}
/// Raw response HEOS metadata.
#[derive(Deserialize, Debug)]
pub struct RawResponseHeos {
/// The full command that produced this response.
///
/// For commands, this will be of the format "\<group\>/\<command\>".
///
/// For events, this will be of the format "event/\<event\>".
pub command: String,
/// Whether this command was successful (`true`) or not (`false`).
#[serde(default, deserialize_with = "deserialize_result")]
pub result: Option<bool>,
/// The "message" part of the response.
///
/// This value is a query-string that contains all the parameters sent to the HEOS system as
/// part of the original command. It also sometime contains _additional_ values that are yielded
/// as part of the response.
#[serde(default)]
pub message: String,
}
/// The data for a raw response received from the HEOS system.
///
/// Note that this raw response can also represent [change events](super::event). Many data types
/// can be parsed from a raw response using [TryFrom].
#[derive(Deserialize, Debug)]
pub struct RawResponse {
/// HEOS metadata of the response.
pub heos: RawResponseHeos,
/// Optional raw JSON payload.
///
/// For commands that yield large amounts of data, that data is usually encoded in the JSON
/// payload. The top-level JSON value can be either a list or a map.
pub payload: Option<Value>,
/// Optional "option" JSON.
///
/// For commands that can retrieve service option values, they will be yielded in this JSON.
pub options: Option<Value>,
}
impl RawResponse {
/// Validate that a response represents a successful command execution.
///
/// # Errors
///
/// If the command did not successfully execute, this will parse out a [CommandError] from the
/// raw response.
pub fn validate_command(&self) -> Result<(), CommandError> {
let result = self.heos.result
.ok_or(CommandError::response_missing_field("heos.result"))?;
if !result {
Err(CommandError::from_message(&self.heos.message))
} else {
Ok(())
}
}
/// Try to parse the message SEQUENCE from the response.
///
/// Commands can specify a SEQUENCE parameter that is duplicated in the response, in order to
/// easily associate responses to their original commands. This attempts to parse out that
/// SEQUENCE value, if it exists in the response.
///
/// # Errors
///
/// Errors if the SEQUENCE parameter does not exist in the response.
pub fn try_msg_id(&self) -> Result<Option<u64>, CommandError> {
let qs = qstring::QString::from(self.heos.message.as_str());
let val = match qs.get("SEQUENCE") {
Some(val) => val,
None => return Ok(None),
};
let msg_id = val.parse().map_err(|error| {
CommandError::MalformedResponse(format!("could not parse 'SEQUENCE': {error}"))
})?;
Ok(Some(msg_id))
}
pub fn percent_decode(&mut self) {
if let Some(payload) = &mut self.payload {
for str_value in RecursiveJsonStringIter::new(payload) {
let bytes = urlencoding::decode_binary(str_value.as_bytes());
*str_value = String::from_utf8_lossy(&bytes).into_owned();
}
}
if let Some(options) = &mut self.options {
for str_value in RecursiveJsonStringIter::new(options) {
let bytes = urlencoding::decode_binary(str_value.as_bytes());
*str_value = String::from_utf8_lossy(&bytes).into_owned();
}
}
}
}
impl TryFrom<RawResponse> for () {
type Error = CommandError;
#[inline]
fn try_from(_: RawResponse) -> Result<Self, Self::Error> {
Ok(())
}
}