nextcloud_dav/xml/
element.rs1use super::escape::unescape_text;
4use super::response::{elements, first_element, has_element};
5
6#[derive(Clone, Copy, Debug, PartialEq, Eq)]
12pub struct Element<'a> {
13 name: &'a str,
14 inner: &'a str,
15}
16
17impl<'a> Element<'a> {
18 pub(crate) const fn new(name: &'a str, inner: &'a str) -> Self {
22 Self { name, inner }
23 }
24
25 pub const fn name(&self) -> &'a str {
27 self.name
28 }
29
30 pub const fn inner(&self) -> &'a str {
32 self.inner
33 }
34
35 pub fn text(&self) -> String {
37 unescape_text(self.inner).trim().to_owned()
38 }
39
40 pub fn child(&self, name: &'a str) -> Option<Self> {
42 first_element(self.inner, name)
43 }
44
45 pub fn children(&self, name: &'a str) -> Vec<Self> {
50 elements(self.inner, name)
51 }
52
53 pub fn has(&self, name: &'a str) -> bool {
55 has_element(self.inner, name)
56 }
57}
58
59#[cfg(test)]
60mod tests {
61 use crate::xml::response::first_element;
62
63 const BODY: &str = "<d:response><d:href>/a</d:href><d:propstat><d:prop>\
64 <oc:fileid>7</oc:fileid></d:prop></d:propstat></d:response>";
65
66 fn response() -> super::Element<'static> {
67 first_element(BODY, "d:response").expect("response is present")
68 }
69
70 #[test]
71 fn chained_lookups_walk_into_nested_elements() {
72 let propstat = response().child("d:propstat").expect("propstat present");
73 let fileid = propstat
74 .child("d:prop")
75 .and_then(|prop| prop.child("oc:fileid"))
76 .map(|element| element.text());
77 assert_eq!(fileid.as_deref(), Some("7"));
78 }
79
80 #[test]
81 fn name_and_inner_expose_the_located_slice() {
82 let element = response();
83 assert_eq!(element.name(), "d:response");
84 assert!(element.inner().starts_with("<d:href>"));
85 }
86
87 #[test]
88 fn has_reports_absence_rather_than_guessing() {
89 assert!(response().has("d:propstat"));
90 assert!(!response().has("d:collection"));
91 }
92}