xrust 0.3.0

Support for XPath, and XSLT
Documentation
/// Driver for roxmltree

use crate::item::{Item, Document, Node};
use crate::xdmerror::*;
use roxmltree::{Document as RoxDoc, Node as RoxNode};

impl<'a> Document for RoxDoc<'a> {
  fn to_string(&self) -> String {
    self.root().to_string()
  }
  fn to_xml(&self) -> String {
    self.root().to_xml()
  }
  fn to_json(&self) -> String {
    self.root().to_json()
  }
  fn children(&self) -> Vec<Box<dyn Node>> {
    vec![Box::new(self.root_element())]
  }
  fn get_root_element(&self) -> Option<Box<dyn Node>> {
    Some(Box::new(self.root_element()))
  }

  fn to_int(&self) -> Result<i64, Error> {
    self.get_root_element().to_int()
  }
  fn to_double(&self) -> f64 {
    f64::NAN
  }
}

impl<'a, 'input> Node for RoxNode<'a, 'input> {
  fn to_name(&self) -> String {
    self.tag_name().name()
  }
  fn to_string(&self) -> String {
    match self.node_type() {
      roxmltree::NodeType::Root => {
        if self.has_children() {
	  self.first_child().to_string()
	} else {
	  String::new()
	}
      }
      roxmltree::NodeType::Element => {
        if self.has_children() {
	  self
	    .children()
	    .fold(String::new(), |s,c| s + xnode_sv_helper(c).as_str())
	} else {
	  "".to_string()
	}
      }
      roxmltree::NodeType::Text => {
        self.text().unwrap_or("").to_string()
      }
      roxmltree::NodeType::Comment => {
        self.text().unwrap_or("").to_string()
      }
      roxmltree::NodeType::PI => {
        self.text().unwrap_or("").to_string()
      }
    }
  }
  fn to_xml(&self) -> String {
    match self.node_type() {
      roxmltree::NodeType::Root => {
        if self.has_children() {
	  self.first_child().unwrap().to_xml()
	} else {
	  String::new()
	}
      }
      roxmltree::NodeType::Element => {
        if self.has_children() {
	  // TODO: attributes
	  format!("<{}>{}</{}>", self.tag_name().name(), self.children().fold(String::new(), |s,c| s + c.to_xml()), self.tag_name().name())
	} else {
	  format!("<{}/>", self.tag_name().name())
	}
      }
      roxmltree::NodeType::Text => {
        String::from(self.text().unwrap_or(""))
      }
      roxmltree::NodeType::Comment => {
        let mut r = String::new();
        r.push_str("<!--");
        r.push_str(self.text().unwrap_or(""));
        r.push_str("-->");
	r
      }
      roxmltree::NodeType::PI => {
        let mut r = String::new();
        r.push_str("<?");
        r.push_str(self.tag_name().name());
        r.push_str(" ");
        r.push_str(self.text().unwrap_or(""));
        r.push_str("?>");
	r
      }
    }
  }
  fn to_json(&self) -> String {
    match self.node_type() {
      roxmltree::NodeType::Root => {
        if self.has_children() {
	  self.first_child().unwrap().to_json()
	} else {
	  "{}".to_string()
	}
      }
      roxmltree::NodeType::Element => {
        if self.has_children() {
	  // TODO: attributes
	  format!("\"{}\": {}", self.tag_name().name(), self.children().fold(String::new(), |s,c| s + c.to_json()))
	} else {
	  format!("\"{}\": \"\"", self.tag_name().name())
	}
      }
      roxmltree::NodeType::Text => {
        format!("\"{}\"", String::from(self.text().unwrap_or("")))
      }
      roxmltree::NodeType::Comment |
      roxmltree::NodeType::PI => {
        "".to_string()
      }
    }
  }

  // TODO: is there a way of retrieving the Document from the Node?
  fn doc(&self) -> Option<Box<dyn Document>> {
    None
  }

  fn parent(&self) -> Option<Box<dyn Node>> {
    match self.parent() {
      Some(p) => Some(Box::new(p)),
      None => None,
    }
  }

  fn children(&self) -> Vec<Box<dyn Node>> {
    let mut ret: Vec<Box<dyn Node>> = Vec::new();
    for c in self.children() {
      ret.push(Box::new(c));
    }
    ret
  }

  fn to_int(&self) -> Result<i64, Error> {
    match self.to_string().parse::<i64>() {
      Ok(i) => Ok(i),
      Result::Err(e) => Result::Err(Error{kind: ErrorKind::Unknown, message: e.to_string()}),
    }
  }
  fn to_double(&self) -> f64 {
    match self.to_string().parse::<f64>() {
      Ok(f) => f,
      Result::Err(_) => f64::NAN
    }
  }

  fn is_element(&self) -> bool {
    self.is_element()
  }
}

fn xnode_sv_helper(c: RoxNode) -> String {
  let mut s: String = String::new();
  for e in c.descendants()
    .filter(|d| d.node_type() == roxmltree::NodeType::Text) {
    s.push_str(e.text().unwrap_or(""));
  }
  s
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn node_stringvalue_1() {
      let d = roxmltree::Document::parse("<Test><Level2>test text</Level2></Test>").expect("unable to parse XML");
      let i = Item::Node(&d.root().first_child().unwrap().first_child().unwrap());
      assert_eq!(i.to_string(), "test text")
    }
    #[test]
    fn node_stringvalue_2() {
      let d = roxmltree::Document::parse("<Test><Level2>test</Level2><Level3>text</Level3></Test>").expect("unable to parse XML");
      let i = Item::Node(&d.root().first_child().unwrap());
      assert_eq!(i.to_string(), "testtext")
    }

    // Documents and Nodes using roxmltree
    #[test]
    fn xnode_doc() {
      let d = roxmltree::Document::parse("<Test/>").expect("unable to parse XML");
      let i = Item::Document(&d);
      assert_eq!(i.to_xml(), "<Test/>")
    }
    #[test]
    fn node_xml() {
      let d = roxmltree::Document::parse("<Test><Level2>test text</Level2></Test>").expect("unable to parse XML");
      let i = Item::Node(&d.root().first_child().unwrap().first_child().unwrap());
      assert_eq!(i.to_string(), "test text");
      assert_eq!(i.to_xml(), "<Level2>test text</Level2>")
    }

}