use super::escape::unescape_text;
use super::response::{elements, first_element, has_element};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Element<'a> {
name: &'a str,
inner: &'a str,
}
impl<'a> Element<'a> {
pub(crate) const fn new(name: &'a str, inner: &'a str) -> Self {
Self { name, inner }
}
pub const fn name(&self) -> &'a str {
self.name
}
pub const fn inner(&self) -> &'a str {
self.inner
}
pub fn text(&self) -> String {
unescape_text(self.inner).trim().to_owned()
}
pub fn child(&self, name: &'a str) -> Option<Self> {
first_element(self.inner, name)
}
pub fn children(&self, name: &'a str) -> Vec<Self> {
elements(self.inner, name)
}
pub fn has(&self, name: &'a str) -> bool {
has_element(self.inner, name)
}
}
#[cfg(test)]
mod tests {
use crate::xml::response::first_element;
const BODY: &str = "<d:response><d:href>/a</d:href><d:propstat><d:prop>\
<oc:fileid>7</oc:fileid></d:prop></d:propstat></d:response>";
fn response() -> super::Element<'static> {
first_element(BODY, "d:response").expect("response is present")
}
#[test]
fn chained_lookups_walk_into_nested_elements() {
let propstat = response().child("d:propstat").expect("propstat present");
let fileid = propstat
.child("d:prop")
.and_then(|prop| prop.child("oc:fileid"))
.map(|element| element.text());
assert_eq!(fileid.as_deref(), Some("7"));
}
#[test]
fn name_and_inner_expose_the_located_slice() {
let element = response();
assert_eq!(element.name(), "d:response");
assert!(element.inner().starts_with("<d:href>"));
}
#[test]
fn has_reports_absence_rather_than_guessing() {
assert!(response().has("d:propstat"));
assert!(!response().has("d:collection"));
}
}