Skip to main content

llsd_rs/
xml.rs

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}