use bytes::Bytes;
use quick_xml::events::Event;
use quick_xml::NsReader;
use soap_server::SoapFault;
fn resolve_predefined_entity(name: &str) -> Option<char> {
match name {
"amp" => Some('&'),
"lt" => Some('<'),
"gt" => Some('>'),
"apos" => Some('\''),
"quot" => Some('"'),
_ => None,
}
}
pub fn extract_text_ns(
body: &Bytes,
element_name: &str,
accepted_namespaces: &[&[u8]],
) -> Result<String, SoapFault> {
let mut reader = NsReader::from_reader(body.as_ref());
reader.config_mut().trim_text(true);
let mut inside_target = false;
let mut accumulated = String::new();
loop {
match reader
.read_resolved_event()
.map_err(|e| SoapFault::sender(format!("{e}")))?
{
(ns, Event::Start(e)) => {
let local_name = e.local_name();
let local = std::str::from_utf8(local_name.as_ref())
.map_err(|e| SoapFault::sender(format!("{e}")))?;
if local == element_name {
let ns_matches = matches!(
ns,
quick_xml::name::ResolveResult::Bound(ref n)
if accepted_namespaces.iter().any(|&a| n.as_ref() == a)
) || matches!(ns, quick_xml::name::ResolveResult::Unbound);
if ns_matches {
inside_target = true;
accumulated.clear();
}
} else if inside_target {
inside_target = false;
}
}
(_, Event::End(_)) => {
if inside_target {
return Ok(accumulated);
}
}
(_, Event::Text(t)) if inside_target => {
let chunk = t
.decode()
.map_err(|e| SoapFault::sender(format!("text decode error: {e}")))?;
accumulated.push_str(&chunk);
}
(_, Event::GeneralRef(r)) if inside_target => {
let ref_name = r
.decode()
.map_err(|e| SoapFault::sender(format!("ref decode error: {e}")))?;
if let Some(ch) = r
.resolve_char_ref()
.map_err(|e| SoapFault::sender(format!("char ref error: {e}")))?
{
accumulated.push(ch);
} else if let Some(ch) = resolve_predefined_entity(&ref_name) {
accumulated.push(ch);
} else {
accumulated.push('&');
accumulated.push_str(&ref_name);
accumulated.push(';');
}
}
(_, Event::Eof) => {
return Err(SoapFault::sender(format!(
"Element {element_name} not found in body"
)))
}
_ => {}
}
}
}