fakecloud_cloudfront/
xml_io.rs1use quick_xml::de::from_str;
8use quick_xml::se::to_string_with_root;
9use serde::de::DeserializeOwned;
10use serde::Serialize;
11
12use crate::NAMESPACE;
13
14const XML_DECL: &str = r#"<?xml version="1.0" encoding="UTF-8"?>"#;
15
16pub fn to_xml_root<T: Serialize>(root: &str, value: &T) -> Result<String, quick_xml::DeError> {
19 let inner =
20 to_string_with_root(root, value).map_err(|e| quick_xml::DeError::Custom(e.to_string()))?;
21 Ok(inject_namespace(&inner, root))
22}
23
24fn inject_namespace(inner: &str, root: &str) -> String {
25 let open_tag = format!("<{root}>");
26 let open_with_ns = format!("<{root} xmlns=\"{NAMESPACE}\">");
27 let stamped = if let Some(rest) = inner.strip_prefix(&open_tag) {
28 format!("{open_with_ns}{rest}")
29 } else {
30 let self_close = format!("<{root}/>");
31 if let Some(rest) = inner.strip_prefix(&self_close) {
32 format!("<{root} xmlns=\"{NAMESPACE}\"/>{rest}")
33 } else {
34 inner.to_string()
35 }
36 };
37 format!("{XML_DECL}{stamped}")
38}
39
40pub fn from_xml_root<T: DeserializeOwned>(body: &[u8]) -> Result<T, quick_xml::DeError> {
42 let s = std::str::from_utf8(body)
43 .map_err(|e| quick_xml::DeError::Custom(format!("invalid utf-8 in body: {e}")))?;
44 from_str(s)
45}
46
47#[cfg(test)]
48mod tests {
49 use super::*;
50 use serde::{Deserialize, Serialize};
51
52 #[derive(Serialize, Deserialize, PartialEq, Debug)]
53 #[serde(rename_all = "PascalCase")]
54 struct Wrap {
55 inner: String,
56 }
57
58 #[test]
59 fn roundtrip_root_with_namespace() {
60 let value = Wrap {
61 inner: "hello".into(),
62 };
63 let xml = to_xml_root("Wrap", &value).unwrap();
64 assert!(xml.contains("xmlns=\"http://cloudfront.amazonaws.com/doc/2020-05-31/\""));
65 assert!(xml.contains("<Inner>hello</Inner>"));
66 let parsed: Wrap = from_xml_root(xml.as_bytes()).unwrap();
67 assert_eq!(parsed, value);
68 }
69
70 #[test]
71 fn empty_struct_renders_self_closing() {
72 #[derive(Serialize, Deserialize)]
73 #[serde(rename_all = "PascalCase")]
74 struct Empty {}
75 let xml = to_xml_root("Empty", &Empty {}).unwrap();
76 assert!(xml.contains("<Empty xmlns=\"http://cloudfront.amazonaws.com/doc/2020-05-31/\"/>"));
77 }
78}