1use std::io::Write;
2
3use base64::prelude::*;
4use chrono::DateTime;
5use uuid::Uuid;
6use xml::{EventReader, EventWriter};
7
8use crate::Uri;
9
10use super::Llsd;
11
12pub fn from_parser<R: std::io::Read>(parser: EventReader<R>) -> Result<Llsd, anyhow::Error> {
13 use xml::reader::XmlEvent;
14 let mut stack: Vec<Llsd> = Vec::new();
15 let mut name_stack: Vec<String> = Vec::new();
16 let mut key_stack: Vec<Option<String>> = Vec::new();
17 let mut start = false;
18 let mut end = false;
19
20 for event in parser {
21 match event {
22 Ok(XmlEvent::StartElement { name, .. }) => {
23 name_stack.push(name.local_name.clone());
24 if !start {
25 if name.local_name.as_str() != "llsd" {
26 return Err(anyhow::anyhow!(
27 "Error parsing LLSD: expected <llsd> root element, got {}",
28 name.local_name
29 ));
30 }
31 start = true;
32 continue;
33 }
34 match name.local_name.as_str() {
35 "llsd" => {
36 return Err(anyhow::anyhow!(
37 "Error parsing LLSD: unexpected <llsd> element"
38 ));
39 }
40 "undef" => stack.push(Llsd::Undefined),
41 "boolean" => stack.push(Llsd::Boolean(false)),
42 "string" => stack.push(Llsd::String(String::new())),
43 "uuid" => stack.push(Llsd::Uuid(Default::default())),
44 "uri" => stack.push(Llsd::Uri(Uri::Empty)),
45 "date" => stack.push(Llsd::Date(Default::default())),
46 "binary" => stack.push(Llsd::Binary(Vec::new())),
47 "integer" => stack.push(Llsd::Integer(0)),
48 "real" => stack.push(Llsd::Real(0.0)),
49 "array" => stack.push(Llsd::Array(Vec::new())),
50 "map" => stack.push(Llsd::Map(Default::default())),
51 "key" => {
52 key_stack.push(None);
53 }
54 _ => {
55 return Err(anyhow::anyhow!(
56 "Error parsing LLSD: unexpected element {}",
57 name.local_name
58 ));
59 }
60 }
61 }
62 Ok(XmlEvent::Characters(data)) => {
63 if key_stack.last() == Some(&None) {
64 key_stack.pop();
65 key_stack.push(Some(data.clone()));
66 } else if let Some(llsd) = stack.last_mut() {
67 match llsd {
68 Llsd::Boolean(_) => match data.as_str() {
69 "true" => *llsd = Llsd::Boolean(true),
70 "false" => *llsd = Llsd::Boolean(false),
71 "1" => *llsd = Llsd::Boolean(true),
72 "0" => *llsd = Llsd::Boolean(false),
73 _ => {
74 return Err(anyhow::anyhow!(
75 "Error parsing LLSD: expected boolean, got {}",
76 data
77 ));
78 }
79 },
80 &mut Llsd::String(ref mut s) => s.push_str(data.as_str()),
81 &mut Llsd::Uuid(ref mut u) => *u = Uuid::parse_str(data.as_str())?,
82 &mut Llsd::Uri(ref mut u) => *u = Uri::parse(data.as_str()),
83 &mut Llsd::Date(ref mut d) => {
84 *d = DateTime::parse_from_rfc3339(data.as_str())?.into()
85 }
86 &mut Llsd::Binary(ref mut b) => {
87 *b = BASE64_STANDARD.decode(data.as_bytes())?
88 }
89 &mut Llsd::Integer(ref mut i) => *i = data.parse()?,
90 &mut Llsd::Real(ref mut r) => match data.as_str() {
91 "nan" => *r = f64::NAN,
92 "inf" => *r = f64::INFINITY,
93 "-inf" => *r = f64::NEG_INFINITY,
94 _ => *r = data.parse()?,
95 },
96 _ => {
97 return Err(anyhow::anyhow!(
98 "Error parsing LLSD: unexpected characters {}",
99 data
100 ));
101 }
102 }
103 }
104 }
105 Ok(XmlEvent::EndElement { name }) => {
106 if name_stack.pop().as_ref() != Some(&name.local_name) {
107 return Err(anyhow::anyhow!(
108 "Error parsing LLSD: unexpected end element {}",
109 name.local_name
110 ));
111 }
112 if name.local_name.as_str() == "key" {
113 if key_stack.last().is_none() {
114 return Err(anyhow::anyhow!("Error parsing LLSD: missing key"));
115 }
116 } else if name.local_name.as_str() == "llsd" {
117 end = true;
118 break;
119 } else if let Some(last) = stack.pop() {
120 match stack.last_mut() {
121 Some(Llsd::Array(parent)) => parent.push(last),
122 Some(Llsd::Map(parent)) => {
123 if let Some(Some(key)) = key_stack.pop() {
124 parent.insert(key.to_string(), last);
125 } else {
126 return Err(anyhow::anyhow!("Error parsing LLSD: missing key"));
127 }
128 }
129 _ => stack.push(last),
130 }
131 } else {
132 return Err(anyhow::anyhow!(
133 "Error parsing LLSD: unexpected end element {}",
134 name.local_name
135 ));
136 }
137 }
138 Err(e) => return Err(anyhow::anyhow!("Error parsing LLSD: {}", e)),
139 _ => {}
140 }
141 }
142 if !end {
143 Err(anyhow::anyhow!(
144 "Error parsing LLSD: unexpected end of input"
145 ))
146 } else if !key_stack.is_empty() {
147 Err(anyhow::anyhow!("Error parsing LLSD: missing key"))
148 } else if stack.len() > 1 {
149 Err(anyhow::anyhow!(
150 "Error parsing LLSD: expected 1 value, got {}",
151 stack.len()
152 ))
153 } else {
154 Ok(stack.pop().unwrap_or(Llsd::Undefined))
155 }
156}
157
158pub fn from_str(data: &str) -> Result<Llsd, anyhow::Error> {
159 from_parser(EventReader::from_str(data))
160}
161
162pub fn from_reader<R: std::io::Read>(reader: R) -> Result<Llsd, anyhow::Error> {
163 from_parser(EventReader::new(reader))
164}
165
166pub fn from_slice(data: &[u8]) -> Result<Llsd, anyhow::Error> {
167 from_parser(EventReader::new(std::io::Cursor::new(data)))
168}
169
170fn write_inner<W: Write>(llsd: &Llsd, w: &mut EventWriter<W>) -> Result<(), anyhow::Error> {
171 use xml::writer::XmlEvent;
172 let tag = |w: &mut EventWriter<W>, tag, text: &str| -> Result<(), anyhow::Error> {
173 w.write(XmlEvent::start_element(tag))?;
174 if !text.is_empty() {
175 w.write(XmlEvent::characters(text))?;
176 }
177 w.write(XmlEvent::end_element())?;
178 Ok(())
179 };
180 fn f64_to_xml(v: f64) -> String {
181 let ss = v.to_string();
182 if ss == "NaN" { "nan".to_string() } else { ss }
183 }
184 match llsd {
185 Llsd::Undefined => tag(w, "undef", "")?,
186 Llsd::Boolean(b) => tag(w, "boolean", if *b { "1" } else { "0" })?,
187 Llsd::String(s) => tag(w, "string", s)?,
188 Llsd::Uuid(u) => tag(w, "uuid", u.to_string().as_str())?,
189 Llsd::Uri(u) => tag(w, "uri", u.as_str())?,
190 Llsd::Date(d) => tag(w, "date", d.to_rfc3339().as_str())?,
191 Llsd::Binary(b) => {
192 if b.is_empty() {
193 tag(w, "binary", "")?;
194 } else {
195 w.write(XmlEvent::start_element("binary").attr("encoding", "base64"))?;
196 w.write(XmlEvent::characters(&BASE64_STANDARD.encode(b)))?;
197 w.write(XmlEvent::end_element())?;
198 }
199 }
200 Llsd::Integer(i) => tag(w, "integer", &i.to_string())?,
201 Llsd::Real(r) => tag(w, "real", f64_to_xml(*r).as_str())?,
202 Llsd::Array(a) => {
203 w.write(XmlEvent::start_element("array"))?;
204 for v in a {
205 write_inner(v, w)?;
206 }
207 w.write(XmlEvent::end_element())?;
208 }
209 Llsd::Map(m) => {
210 w.write(XmlEvent::start_element("map"))?;
211 for (k, v) in m {
212 tag(w, "key", k)?;
213 write_inner(v, w)?;
214 }
215 w.write(XmlEvent::end_element())?;
216 }
217 }
218 Ok(())
219}
220
221pub fn write<W: Write>(llsd: &Llsd, w: &mut EventWriter<W>) -> Result<(), anyhow::Error> {
222 use xml::writer::XmlEvent;
223 w.write(XmlEvent::start_element("llsd"))?;
224 write_inner(llsd, w)?;
225 w.write(XmlEvent::end_element())?;
226 Ok(())
227}
228
229pub fn to_pretty_string(llsd: &Llsd) -> Result<String, anyhow::Error> {
230 let mut buf = Vec::new();
231 write(
232 llsd,
233 &mut EventWriter::new_with_config(
234 &mut buf,
235 xml::writer::EmitterConfig::new().perform_indent(true),
236 ),
237 )?;
238 Ok(String::from_utf8(buf)?)
239}
240
241pub fn to_string(llsd: &Llsd) -> Result<String, anyhow::Error> {
242 let mut buf = Vec::new();
243 write(llsd, &mut EventWriter::new(&mut buf))?;
244 Ok(String::from_utf8(buf)?)
245}
246
247pub fn to_request(llsd: &Llsd) -> Result<Vec<u8>, anyhow::Error> {
248 let mut buf = Vec::new();
249 write(
250 llsd,
251 &mut EventWriter::new_with_config(
252 &mut buf,
253 xml::writer::EmitterConfig::new().write_document_declaration(false),
254 ),
255 )?;
256 Ok(buf)
257}
258
259#[cfg(test)]
260mod tests {
261 use super::*;
262 use chrono::{TimeZone, Utc};
263 use std::collections::HashMap;
264 use url::Url;
265
266 fn round_trip(llsd: Llsd) {
267 let encoded = to_string(&llsd).expect("Failed to encode");
268 let decoded = from_str(&encoded).expect("Failed to decode");
269 assert_eq!(llsd, decoded);
270 }
271
272 #[test]
273 fn undefined() {
274 round_trip(Llsd::Undefined);
275 }
276
277 #[test]
278 fn boolean() {
279 round_trip(Llsd::Boolean(true));
280 round_trip(Llsd::Boolean(false));
281 }
282
283 #[test]
284 fn integer() {
285 round_trip(Llsd::Integer(42));
286 }
287
288 #[test]
289 fn real() {
290 round_trip(Llsd::Real(13.1415));
291 }
292
293 #[test]
294 fn string() {
295 round_trip(Llsd::String("Hello, LLSD!".to_owned()));
296 }
297
298 #[test]
299 fn uri() {
300 let url = Url::parse("https://example.com/").unwrap();
301 round_trip(Llsd::Uri(url.into()));
302 }
303
304 #[test]
305 fn uuid() {
306 let uuid = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
307 round_trip(Llsd::Uuid(uuid));
308 }
309
310 #[test]
311 fn date() {
312 let dt = Utc.timestamp_opt(1_620_000_000, 0).unwrap();
313 round_trip(Llsd::Date(dt));
314 }
315
316 #[test]
317 fn binary() {
318 round_trip(Llsd::Binary(vec![0xde, 0xad, 0xbe, 0xef]));
319 }
320
321 #[test]
322 fn array() {
323 let arr = vec![
324 Llsd::Integer(1),
325 Llsd::String("two".into()),
326 Llsd::Boolean(false),
327 ];
328 round_trip(Llsd::Array(arr));
329 }
330
331 #[test]
332 fn map() {
333 let mut map = HashMap::new();
334 map.insert("answer".into(), Llsd::Integer(42));
335 map.insert("pi".into(), Llsd::Real(13.14));
336 map.insert("greeting".into(), Llsd::String("hello".into()));
337 round_trip(Llsd::Map(map));
338 }
339}