azure_core/
xml.rs

1use crate::error::{ErrorKind, ResultExt};
2use bytes::Bytes;
3pub use quick_xml::serde_helpers::text_content;
4use quick_xml::{
5    de::{from_reader, from_str},
6    se::{to_string, to_string_with_root},
7};
8use serde::de::DeserializeOwned;
9
10/// The UTF8 [byte order marker](https://en.wikipedia.org/wiki/Byte_order_mark)
11const UTF8_BOM: [u8; 3] = [0xEF, 0xBB, 0xBF];
12
13/// Reads the XML from bytes.
14pub fn read_xml_str<T>(body: &str) -> crate::Result<T>
15where
16    T: DeserializeOwned,
17{
18    from_str(body).with_context(ErrorKind::DataConversion, || {
19        let t = core::any::type_name::<T>();
20        format!("failed to deserialize the following xml into a {t}\n{body}")
21    })
22}
23
24/// Reads the XML from bytes.
25pub fn read_xml<T>(body: &[u8]) -> crate::Result<T>
26where
27    T: DeserializeOwned,
28{
29    from_reader(slice_bom(body)).with_context(ErrorKind::DataConversion, || {
30        let t = core::any::type_name::<T>();
31        let xml = std::str::from_utf8(body).unwrap_or("<XML IS NOT UTF-8>");
32        format!("failed to deserialize the following xml into a {t}\n{xml}")
33    })
34}
35
36pub fn to_xml<T>(value: &T) -> crate::Result<Bytes>
37where
38    T: serde::Serialize,
39{
40    let value = to_string(value).with_context(ErrorKind::DataConversion, || {
41        let t = core::any::type_name::<T>();
42        format!("failed to serialize {t} into xml")
43    })?;
44    Ok(Bytes::from(value))
45}
46
47pub fn to_xml_with_root<T>(root_tag: &str, value: &T) -> crate::Result<Bytes>
48where
49    T: serde::Serialize,
50{
51    let value =
52        to_string_with_root(root_tag, value).with_context(ErrorKind::DataConversion, || {
53            let t = core::any::type_name::<T>();
54            format!("failed to serialize {t} into xml")
55        })?;
56    Ok(Bytes::from(value))
57}
58
59/// Returns bytes without the UTF-8 BOM.
60fn slice_bom(bytes: &[u8]) -> &[u8] {
61    if bytes.len() > 3 && bytes[0..3] == UTF8_BOM {
62        &bytes[3..]
63    } else {
64        bytes
65    }
66}
67
68#[cfg(test)]
69mod test {
70    use super::*;
71    use serde::Deserialize;
72
73    #[test]
74    fn test_slice_bom() {
75        let bytes = &[0xEF, 0xBB, 0xBF, 7];
76        assert_eq!(&[7], slice_bom(bytes));
77
78        let bytes = &[8];
79        assert_eq!(&[8], slice_bom(bytes));
80    }
81
82    #[test]
83    fn reading_xml() -> crate::Result<()> {
84        #[derive(Deserialize, PartialEq, Debug)]
85        #[serde(rename = "Foo")]
86        struct Test {
87            x: String,
88        }
89        let test = Test {
90            x: "Hello, world!".into(),
91        };
92        let xml = br#"<?xml version="1.0" encoding="utf-8"?><Foo><x>Hello, world!</x></Foo>"#;
93        assert_eq!(test, read_xml(xml)?);
94
95        let error = read_xml::<Test>(&xml[..xml.len() - 2]).unwrap_err();
96        assert!(format!("{error}").contains("reading_xml::Test"));
97
98        let xml = r#"<?xml version="1.0" encoding="utf-8"?><Foo><x>Hello, world!</x></Foo>"#;
99        assert_eq!(test, read_xml_str(xml)?);
100
101        let error = read_xml_str::<Test>(&xml[..xml.len() - 2]).unwrap_err();
102        assert!(format!("{error}").contains("reading_xml::Test"));
103        Ok(())
104    }
105}