nextcloud_dav/xml/
response.rs1use super::scanner;
26use super::{Element, MissingElement};
27
28pub fn first_element<'a>(xml: &'a str, name: &'a str) -> Option<Element<'a>> {
30 elements(xml, name).into_iter().next()
31}
32
33pub fn elements<'a>(xml: &'a str, name: &'a str) -> Vec<Element<'a>> {
38 let mut found = Vec::new();
39 let mut cursor = 0;
40 while let Some(span) = scanner::find(xml, cursor, name) {
41 found.push(Element::new(name, &xml[span.inner]));
42 cursor = span.resume;
43 }
44 found
45}
46
47pub fn has_element(xml: &str, name: &str) -> bool {
49 scanner::find(xml, 0, name).is_some()
50}
51
52pub fn text_of(xml: &str, name: &str) -> Option<String> {
54 first_element(xml, name).map(|element| element.text())
55}
56
57pub fn require_element<'a>(xml: &'a str, name: &'a str) -> Result<Element<'a>, MissingElement> {
59 first_element(xml, name).ok_or_else(|| MissingElement::new(name))
60}
61
62#[cfg(test)]
63mod tests {
64 use super::{elements, has_element, require_element, text_of};
65
66 const BODY: &str = "<d:multistatus><d:response><d:href>/a&b</d:href>\
67 <d:propstat><d:prop><oc:fileid>7</oc:fileid></d:prop></d:propstat>\
68 </d:response></d:multistatus>";
69
70 #[test]
71 fn reads_element_text_with_entities_decoded() {
72 assert_eq!(text_of(BODY, "d:href").as_deref(), Some("/a&b"));
73 }
74
75 #[test]
76 fn lists_every_matching_element_in_order() {
77 let xml = "<a><b>1</b><b>2</b></a>";
78 let texts: Vec<String> = elements(xml, "b").iter().map(|e| e.text()).collect();
79 assert_eq!(texts, ["1", "2"]);
80 }
81
82 #[test]
83 fn absent_elements_are_reported_not_invented() {
84 assert!(!has_element(BODY, "d:collection"));
85 assert!(text_of(BODY, "d:collection").is_none());
86 }
87
88 #[test]
89 fn require_element_names_the_missing_tag() {
90 assert!(require_element(BODY, "d:propstat").is_ok());
91 let error = require_element(BODY, "d:collection").expect_err("collection is absent");
92 assert_eq!(error.name(), "d:collection");
93 }
94}