1use crate::error::{ErrorKind, Result, ResultExt};
6use bytes::Bytes;
7pub use quick_xml::serde_helpers::text_content as content;
8use quick_xml::{
9 de::from_reader,
10 se::{to_string, to_string_with_root},
11};
12use serde::de::DeserializeOwned;
13
14const UTF8_BOM: [u8; 3] = [0xEF, 0xBB, 0xBF];
16
17const DECLARATION: &[u8; 38] = br#"<?xml version="1.0" encoding="utf-8"?>"#;
19
20pub fn from_xml<S, T>(body: S) -> Result<T>
22where
23 S: AsRef<[u8]>,
24 T: DeserializeOwned,
25{
26 let body = body.as_ref();
27 from_reader(slice_bom(body)).with_context_fn(ErrorKind::DataConversion, || {
28 let t = core::any::type_name::<T>();
29 let xml = std::str::from_utf8(body).unwrap_or("(XML is not UTF8-encoded)");
30 format!("failed to deserialize the following xml into a {t}\n{xml}")
31 })
32}
33
34pub fn to_xml<T>(value: &T) -> Result<Bytes>
38where
39 T: serde::Serialize,
40{
41 let value = to_string(value).with_context_fn(ErrorKind::DataConversion, || {
42 let t = core::any::type_name::<T>();
43 format!("failed to serialize {t} into xml")
44 })?;
45 let mut buf = bytes::BytesMut::with_capacity(DECLARATION.len() + value.len());
46 buf.extend_from_slice(DECLARATION);
47 buf.extend_from_slice(value.as_bytes());
48 Ok(buf.into())
49}
50
51pub fn to_xml_with_root<T>(root_tag: &str, value: &T) -> Result<Bytes>
55where
56 T: serde::Serialize,
57{
58 let value =
59 to_string_with_root(root_tag, value).with_context_fn(ErrorKind::DataConversion, || {
60 let t = core::any::type_name::<T>();
61 format!("failed to serialize {t} into xml")
62 })?;
63 let mut buf = bytes::BytesMut::with_capacity(DECLARATION.len() + value.len());
64 buf.extend_from_slice(DECLARATION);
65 buf.extend_from_slice(value.as_bytes());
66 Ok(buf.into())
67}
68
69fn slice_bom(bytes: &[u8]) -> &[u8] {
71 if bytes.len() > 3 && bytes[0..3] == UTF8_BOM {
72 &bytes[3..]
73 } else {
74 bytes
75 }
76}
77
78#[cfg(test)]
79mod test {
80 use super::*;
81 use serde::{Deserialize, Serialize};
82
83 #[test]
84 fn test_slice_bom() {
85 let bytes = &[0xEF, 0xBB, 0xBF, 7];
86 assert_eq!(&[7], slice_bom(bytes));
87
88 let bytes = &[8];
89 assert_eq!(&[8], slice_bom(bytes));
90 }
91
92 #[derive(Deserialize, Serialize, PartialEq, Debug)]
93 #[serde(rename = "Foo")]
94 struct Test {
95 x: String,
96 }
97
98 #[test]
99 fn reading_xml() -> Result<()> {
100 let test = Test {
101 x: "Hello, world!".into(),
102 };
103 let xml = br#"<?xml version="1.0" encoding="utf-8"?><Foo><x>Hello, world!</x></Foo>"#;
104 assert_eq!(test, from_xml(xml)?);
105
106 let error = from_xml::<_, Test>(&xml[..xml.len() - 2]).unwrap_err();
107 assert!(format!("{error}").contains("typespec::xml::test::Test"));
108 Ok(())
109 }
110
111 #[test]
112 fn writing_xml() -> Result<()> {
113 assert_eq!(
114 br#"<?xml version="1.0" encoding="utf-8"?><Foo><x>Hello, world!</x></Foo>"#,
115 to_xml(&Test {
116 x: "Hello, world!".to_string()
117 })?
118 .to_vec()
119 .as_slice()
120 );
121
122 assert_eq!(
123 br#"<?xml version="1.0" encoding="utf-8"?><Bob><x>Hello, world!</x></Bob>"#,
124 to_xml_with_root(
125 "Bob",
126 &Test {
127 x: "Hello, world!".to_string()
128 }
129 )?
130 .to_vec()
131 .as_slice()
132 );
133 Ok(())
134 }
135}