use serde::de::DeserializeOwned;
use crate::{envelope::detect_message_type, ParseError};
pub fn from_str<T: DeserializeOwned>(xml: &str) -> Result<T, ParseError> {
quick_xml::de::from_str(xml).map_err(ParseError::Deserialize)
}
pub fn from_reader<R: std::io::BufRead, T: DeserializeOwned>(reader: R) -> Result<T, ParseError> {
quick_xml::de::from_reader(reader).map_err(ParseError::Deserialize)
}
pub fn from_str_in_envelope<T: DeserializeOwned>(xml: &str) -> Result<T, ParseError> {
let detected = detect_message_type(xml).ok();
quick_xml::de::from_str(xml).map_err(|e| match detected {
Some(id) => ParseError::DeserializeIn {
context: id.dotted(),
source: e,
},
None => ParseError::Deserialize(e),
})
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(serde::Deserialize, Debug)]
struct Foo {
#[allow(dead_code)]
x: u32,
}
#[test]
fn from_str_invalid_xml_returns_error() {
let result: Result<Foo, _> = from_str("<<<garbage>>>");
assert!(result.is_err(), "malformed XML must return an error");
}
#[test]
fn from_reader_invalid_xml_returns_error() {
let xml = b"<<<garbage>>>";
let result: Result<Foo, _> = from_reader(xml.as_ref());
assert!(result.is_err(), "malformed XML must return an error");
}
#[test]
fn from_str_in_envelope_includes_detected_message_id_on_failure() {
let xml = r#"<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pacs.008.001.13"><Other/></Document>"#;
let err = from_str_in_envelope::<Foo>(xml).unwrap_err();
match err {
ParseError::DeserializeIn { ref context, .. } => {
assert_eq!(context, "pacs.008.001.13");
let rendered = err.to_string();
assert!(
rendered.contains("pacs.008.001.13"),
"Display should mention the detected envelope, got: {rendered}"
);
}
other => panic!("expected DeserializeIn, got: {other:?}"),
}
}
#[test]
fn from_str_in_envelope_falls_back_to_plain_deserialize_when_envelope_unknown() {
let xml = r#"<Document><Other/></Document>"#;
let err = from_str_in_envelope::<Foo>(xml).unwrap_err();
assert!(
matches!(err, ParseError::Deserialize(_)),
"expected Deserialize without context, got: {err:?}"
);
}
#[test]
fn from_str_in_envelope_succeeds_passes_through() {
let xml = r#"<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pacs.008.001.13"><x>42</x></Document>"#;
let v: Foo = from_str_in_envelope(xml).expect("body matches Foo");
assert_eq!(v.x, 42);
}
}