added pom on top of dom on top of sax

This commit is contained in:
Shautvast 2025-07-23 22:48:18 +02:00
parent 427f24cd9f
commit a7d3a0b9e7
15 changed files with 527 additions and 360 deletions

View file

@ -1,12 +1,10 @@
use crate::maven::pom::{ArtifactId, GroupId, Version};
/// The Maven variant to parse poms
/// These structs is directly modelled after the XML because that is what strong-xml plugin requires
#[derive(PartialEq, Debug)]
pub struct Metadata {
pub group_id: GroupId,
pub artifact_id: ArtifactId,
pub version: Version,
pub group_id: String,
pub artifact_id: String,
pub version: String,
pub versioning: Versioning,
}

View file

@ -1,4 +1,4 @@
pub mod metadata;
pub mod pom;
pub mod pom_view;
mod pom_reader;
pub mod pom_reader;

View file

@ -2,122 +2,46 @@
/// These structs is directly modelled after the XML because that is what strong-xml plugin requires
#[derive(PartialEq, Debug)]
pub struct Pom {
pub(crate) model_version: ModelVersion,
pub(crate) parent: Option<Parent>,
pub(crate) group_id: Option<GroupId>,
pub(crate) artifact_id: ArtifactId,
pub(crate) version: Option<Version>,
pub(crate) name: Name,
pub(crate) packaging: Option<Packaging>,
pub(crate) url: Option<Url>,
pub(crate) description: Description,
pub(crate) licences: Option<Licenses>,
pub(crate) scm: Option<Scm>,
pub(crate) developers: Option<Developers>,
pub(crate) dependencies: Option<Dependencies>,
pub(crate) dependency_management: Option<DependencyManagement>,
pub(crate) group_id: Option<String>,
pub(crate) artifact_id: String,
pub(crate) version: Option<String>,
pub(crate) name: String,
pub(crate) packaging: Option<String>,
pub(crate) url: Option<String>,
pub(crate) dependencies: Vec<Dependency>,
pub(crate) dependency_management: Vec<Dependency>,
}
#[derive(PartialEq, Debug)]
pub struct ModelVersion {
pub value: String,
}
impl Pom {
#[derive(PartialEq, Debug, Clone)]
pub struct GroupId {
pub(crate) value: String,
}
#[derive(PartialEq, Debug, Clone)]
pub struct ArtifactId {
pub(crate) value: String,
}
#[derive(PartialEq, Debug, Clone)]
pub struct Version {
pub(crate) value: String,
}
#[derive(PartialEq, Debug)]
pub struct Name {
pub(crate) value: String,
}
#[derive(PartialEq, Debug)]
pub struct Id {
pub(crate) value: String,
}
#[derive(PartialEq, Debug)]
pub struct Packaging {
pub(crate) value: String,
}
#[derive(PartialEq, Debug)]
pub struct Url {
pub(crate) value: String,
}
#[derive(PartialEq, Debug)]
pub struct Description {
pub(crate) value: String,
}
#[derive(PartialEq, Debug)]
pub struct Licenses {
pub(crate) licenses: Vec<License>,
}
#[derive(PartialEq, Debug)]
pub struct Distribution {
pub(crate) value: String,
}
#[derive(PartialEq, Debug)]
pub struct License {
pub(crate) name: Name,
pub(crate) url: Url,
pub(crate) distribution: Option<Distribution>,
pub(crate) name: String,
pub(crate) url: String,
pub(crate) distribution: Option<String>,
}
#[derive(PartialEq, Debug)]
pub struct Parent {
pub(crate) group_id: GroupId,
pub(crate) artifact_id: ArtifactId,
pub(crate) version: Version,
pub(crate) group_id: String,
pub(crate) artifact_id: String,
pub(crate) version: String,
}
#[derive(PartialEq, Debug)]
pub struct Scm {
pub(crate) url: Url,
}
#[derive(PartialEq, Debug)]
pub struct Developers {
pub(crate) developers: Vec<Developer>,
}
#[derive(PartialEq, Debug)]
struct Developer {
pub(crate) id: Option<Id>,
pub(crate) name: Name,
}
#[derive(PartialEq, Debug, Clone)]
pub struct Dependencies {
pub(crate) value: Vec<Dependency>,
}
#[derive(PartialEq, Debug, Clone)]
pub struct DependencyManagement {
pub(crate) value: Dependencies,
pub struct Developer {
pub(crate) id: Option<String>,
pub(crate) name: String,
}
#[derive(PartialEq, Debug, Clone)]
pub struct Dependency {
pub(crate) group_id: GroupId,
pub(crate) artifact_id: ArtifactId,
pub(crate) version: Option<Version>,
pub(crate) group_id: String,
pub(crate) artifact_id: String,
pub(crate) version: Option<String>,
}
#[cfg(test)]
@ -126,7 +50,5 @@ mod test {
use crate::maven::pom::Pom;
#[test]
fn parse_should_not_fail() {
}
fn parse_should_not_fail() {}
}

View file

@ -1,39 +1,128 @@
use crate::maven::pom::Pom;
use crate::xml::{Attribute, SaxHandler};
use crate::maven::pom::{Dependency, Developer, Parent, Pom};
use crate::xml::SaxError;
use crate::xml::dom_parser::{Node, get_document};
fn read(xml: &str){
pub fn get_pom(xml: &str) -> Result<Pom, SaxError> {
let mut group_id = None;
let mut artefact_id = None;
let mut parent = None;
let mut version = None;
let mut name = None;
let mut packaging = None;
let mut url = None;
let mut dependencies = vec![];
let mut dependency_management = vec![];
for child in get_document(xml)?.root.children {
match child.name.as_str() {
"groupId" => group_id = child.text,
"artifactId" => artefact_id = child.text,
"parent" => parent = Some(get_parent(&child)),
"version" => version = child.text,
"name" => name = child.text,
"packaging" => packaging = child.text,
"url" => url = child.text,
"dependencies" => dependencies = get_dependencies(child),
"dependencyManagement" => dependency_management = get_dependency_mgmt(child),
_ => {}
}
}
Ok(Pom {
parent,
group_id,
artifact_id: artefact_id.unwrap(),
version,
name: name.unwrap(),
packaging,
url,
dependencies,
dependency_management
})
}
fn get_dependency_mgmt(element: Node) -> Vec<Dependency> {
if !element.children.is_empty(){
get_dependencies(element.children.first().unwrap().clone())
} else {
vec![]
}
}
fn get_dependencies(element: Node) -> Vec<Dependency> {
let mut dependencies = vec![];
for node in element.children {
if node.name == "dependency" {
dependencies.push(get_dependency(node))
}
}
dependencies
}
fn get_dependency(element: Node) -> Dependency {
let mut grouo_id = None;
let mut artefact_id = None;
let mut version = None;
for node in element.children {
match node.name.as_str() {
"groupId" => grouo_id = node.text,
"artifactId" => artefact_id = node.text,
"version" => version = node.text,
_ => {}
}
}
Dependency {
group_id: grouo_id.unwrap(),
artifact_id: artefact_id.unwrap(),
version,
}
}
fn get_developers(element: Node) -> Vec<Developer> {
let mut developers = vec![];
for node in element.children {
if node.name == "developer" {
developers.push(get_developer(node))
}
}
developers
}
fn get_developer(element: Node) -> Developer {
let mut id = None;
let mut name = None;
for node in element.children {
match node.name.as_str() {
"id" => id = node.text,
"name" => name = node.text,
_ => {}
}
}
Developer {
id,
name: name.unwrap(),
}
}
fn get_parent(element: &Node) -> Parent {
let mut group_id = None;
let mut artefact_id = None;
let mut version = None;
for child in &element.children {
match child.name.as_str() {
"groupId" => group_id = child.text.clone(),
"artefactId" => artefact_id = child.text.clone(),
"version" => version = child.text.clone(),
_ => {}
}
}
Parent {
group_id: group_id.unwrap(),
artifact_id: artefact_id.unwrap(),
version: version.unwrap(),
}
}
struct PomReader {
element_stack: Vec<String>,
pom: Pom,
}
impl SaxHandler for PomReader{
fn start_document(&mut self) {
}
fn end_document(&mut self) {
}
fn start_prefix_mapping(&mut self, prefix: &str, uri: &str) {
}
fn start_element(&mut self, uri: Option<String>, local_name: &str, qualified_name: &str, attributes: Vec<Attribute>) {
// match local_name{
// "modelVersion" => {self.pom=Pom{}}
// }
}
fn end_element(&mut self, uri: Option<String>, local_name: &str, qualified_name: &str) {
todo!()
}
fn characters(&mut self, chars: &[char]) {
todo!()
}
fn error(&mut self, error: &str) {
todo!()
}
}

View file

@ -1,7 +1,3 @@
use regex::Regex;
use crate::maven::pom::{Dependency, Parent, Pom};
/// offers a (non-mutable) view on the pom-as-xml-representation
/// the main use of this is that it resolves the parent information when needed
///

131
src/xml/dom_parser.rs Normal file
View file

@ -0,0 +1,131 @@
use crate::xml::sax_parser::parse_string;
use crate::xml::{Attribute, SaxError, SaxHandler};
pub fn get_document(xml: &str) -> Result<Document, SaxError> {
let mut dom_hax_handler = DomSaxHandler::new();
parse_string(xml, Box::new(&mut dom_hax_handler))?;
Ok(dom_hax_handler.into_doc())
}
#[derive(Debug)]
pub struct Document {
pub root: Node,
}
#[derive(Debug, Clone, PartialEq)]
struct BNode {
name: String,
namespace: Option<String>,
children: Vec<usize>,
attributes: Vec<Attribute>,
text: Option<String>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Node {
pub name: String,
pub namespace: Option<String>,
pub children: Vec<Node>,
pub attributes: Vec<Attribute>,
pub text: Option<String>,
}
impl From<&BNode> for Node {
fn from(bnode: &BNode) -> Self {
Self {
name: bnode.name.clone(),
namespace: bnode.namespace.clone(),
children: vec![],
attributes: bnode.attributes.to_vec(),
text: bnode.text.clone(),
}
}
}
impl BNode {
fn new(name: &str, namespace: Option<String>, attributes: Vec<Attribute>) -> Self {
Self {
name: name.to_string(),
namespace,
attributes,
children: vec![],
text: None,
}
}
}
struct DomSaxHandler {
node_stack: Vec<usize>,
nodes: Vec<BNode>,
name: String,
}
impl DomSaxHandler {
fn new() -> Self {
Self {
node_stack: vec![],
nodes: vec![],
name: "koe".to_string(),
}
}
fn into_doc(self) -> Document {
let bnode = &self.nodes[self.node_stack[0]];
let node = self.to_node(bnode);
Document { root: node }
}
fn to_node(&self, bnode: &BNode) -> Node {
let mut node: Node = bnode.into();
for child_index in &bnode.children {
let child = self.nodes.get(*child_index).unwrap();
node.children.push(self.to_node(child));
}
node
}
}
impl SaxHandler for DomSaxHandler {
fn start_document(&mut self) {}
fn end_document(&mut self) {}
fn start_prefix_mapping(&mut self, _prefix: &str, _uri: &str) {}
fn start_element(
&mut self,
uri: Option<String>,
local_name: &str,
_qualified_name: &str,
attributes: Vec<Attribute>,
) {
let id = self.nodes.iter().len();
let node = BNode::new(local_name, uri, attributes);
self.nodes.push(node);
if !self.node_stack.is_empty() {
let parent_index = *self.node_stack.last().unwrap();
self.nodes.get_mut(parent_index).unwrap().children.push(id);
}
self.node_stack.push(id);
}
fn end_element(&mut self, _uri: Option<String>, _local_name: &str, _qualified_name: &str) {
if self.node_stack.len() > 1 {
self.node_stack.pop();
}
}
fn characters(&mut self, chars: &[char]) {
if !self.node_stack.is_empty() {
let top = *self.node_stack.last().unwrap();
let parent = self.nodes.get_mut(top).unwrap();
parent.text = Some(chars.iter().collect::<String>());
}
}
fn error(&mut self, error: &str) {
panic!("{}", error)
}
}

View file

@ -1,13 +1,23 @@
pub mod sax_parser;
mod debug;
pub mod dom_parser;
#[derive(Debug)]
#[derive(Debug,Clone,PartialEq)]
pub struct Attribute {
pub name: String,
pub namespace: Option<String>,
pub value: String,
}
enum SaxEvent{
StartDocument,
EndDocument,
StartElement(Option<String>, String, String, Vec<Attribute>),
EndElement(Option<String>, String),
Characters(String),
Error(String)
}
pub trait SaxHandler {
fn start_document(&mut self);
fn end_document(&mut self);

View file

@ -1,7 +1,7 @@
use crate::xml::{Attribute, SaxError, SaxHandler};
use std::collections::HashMap;
pub fn parse_string(xml: String, handler: Box<&mut dyn SaxHandler>) -> Result<(), SaxError> {
pub fn parse_string(xml: &str, handler: Box<&mut dyn SaxHandler>) -> Result<(), SaxError> {
SAXParser::new(xml, handler).parse()
}
@ -16,7 +16,7 @@ pub struct SAXParser<'a> {
}
impl<'a> SAXParser<'a> {
pub fn new(xml: String, handler: Box<&'a mut dyn SaxHandler>) -> Self {
pub fn new(xml: &str, handler: Box<&'a mut dyn SaxHandler>) -> Self {
Self {
xml: xml.chars().collect(),
handler,

View file

@ -1 +1,2 @@
mod xml;
mod pom;

1
tests/pom/mod.rs Normal file
View file

@ -0,0 +1 @@
mod pom_parser_test;

View file

@ -0,0 +1,9 @@
use undeepend::maven::pom_reader::get_pom;
use undeepend::xml::dom_parser::get_document;
#[test]
fn test_pom_parser() {
let test_xml = include_str!("../pom/resources/pom.xml");
let pom = get_pom(test_xml).expect("failed to get document");
println!("{:?}", pom);
}

View file

@ -0,0 +1,8 @@
use undeepend::xml::dom_parser::get_document;
#[test]
fn test_dom_parser() {
let test_xml = include_str!("../pom/resources/pom.xml");
let doc = get_document(test_xml).expect("failed to get document");
println!("{:?}",doc);
}

View file

@ -1 +1,2 @@
mod sax_parser_test;
mod dom_parser_test;

View file

@ -1,212 +1,213 @@
use std::collections::HashMap;
use undeepend::xml::sax_parser::parse_string;
use undeepend::xml::{Attribute, SaxError, SaxHandler};
#[test]
fn test_xml_header() {
let test_xml = include_str!("resources/header.xml");
let mut testhandler = TestHandler::new();
parse_string(test_xml.to_string(), Box::new(&mut testhandler))
.expect("Failed to parse test xml");
println!("{:?}", testhandler);
assert_eq!(testhandler.start_document_called, 1);
assert_eq!(testhandler.end_document_called, 1);
}
#[test]
fn test_single_element_short() {
let test_xml = include_str!("resources/header.xml");
let mut testhandler = TestHandler::new();
parse_string(test_xml.to_string(), Box::new(&mut testhandler))
.expect("Failed to parse test xml");
assert_eq!(testhandler.start_document_called, 1);
assert_eq!(testhandler.start_element_called, 1);
assert!(!testhandler.elements.is_empty());
assert_eq!(testhandler.elements[0], "<xml>");
assert_eq!(testhandler.end_document_called, 1);
}
#[test]
fn test_single_element() {
let test_xml = include_str!("resources/element.xml");
let mut testhandler = TestHandler::new();
parse_string(test_xml.to_string(), Box::new(&mut testhandler))
.expect("Failed to parse test xml");
assert_eq!(testhandler.start_document_called, 1);
assert_eq!(testhandler.start_element_called, 1);
assert!(!testhandler.elements.is_empty());
assert_eq!(testhandler.elements[0], "<element>");
assert_eq!(testhandler.end_document_called, 1);
}
#[test]
fn test_single_element_single_attribute() {
let test_xml = include_str!("resources/element_with_attribute.xml");
let mut testhandler = TestHandler::new();
parse_string(test_xml.to_string(), Box::new(&mut testhandler))
.expect("Failed to parse test xml");
assert_eq!(testhandler.start_document_called, 1);
assert_eq!(testhandler.start_element_called, 1);
assert!(!testhandler.elements.is_empty());
assert_eq!(testhandler.elements[0], r#"<element a="1">"#);
assert_eq!(testhandler.end_element_called, 1);
assert_eq!(testhandler.end_document_called, 1);
}
#[test]
fn test_ignore_comment() {
let test_xml = include_str!("resources/comment.xml");
let mut testhandler = TestHandler::new();
parse_string(test_xml.to_string(), Box::new(&mut testhandler))
.expect("Failed to parse test xml");
assert_eq!(testhandler.start_document_called, 1);
assert_eq!(testhandler.start_element_called, 1);
assert!(!testhandler.elements.is_empty());
assert_eq!(
testhandler.elements[0],
r#"<http://example.com/books:bookstore xmlns="http://example.com/books">"#
);
assert_eq!(testhandler.end_element_called, 1);
assert_eq!(testhandler.end_document_called, 1);
}
#[test]
fn test_bad_comment() {
let test_xml = include_str!("resources/illegal_dashes_comment.xml");
let mut testhandler = TestHandler::new();
match parse_string(test_xml.to_string(), Box::new(&mut testhandler)) {
Err(e) => assert_eq!(e, SaxError::BadCharacter),
Ok(_) => assert!(false),
}
}
#[test]
fn test_namespaces() {
let test_xml = include_str!("resources/namespaces.xml");
let mut testhandler = TestHandler::new();
parse_string(test_xml.to_string(), Box::new(&mut testhandler))
.expect("Failed to parse test xml");
assert_eq!(testhandler.start_document_called, 1);
assert_eq!(testhandler.start_element_called, 4);
assert!(!testhandler.elements.is_empty());
assert_eq!(testhandler.elements[0], r#"<bookstore>"#);
assert_eq!(
testhandler.elements[1],
r#"<http://example.com/books:book xmlns="http://example.com/books" id="1" category="fiction">"#
);
assert_eq!(
testhandler.elements[2],
r#"<http://example.com/books:page>"#
);
assert_eq!(testhandler.elements[3], r#"<publisher>"#);
assert_eq!(testhandler.end_element_called, 4);
assert_eq!(testhandler.end_document_called, 1);
}
#[test]
fn test_namespace_prefixes() {
let test_xml = include_str!("resources/namespaces-prefix.xml");
let mut testhandler = TestHandler::new();
parse_string(test_xml.to_string(), Box::new(&mut testhandler))
.expect("Failed to parse test xml");
assert_eq!(testhandler.start_document_called, 1);
assert_eq!(testhandler.start_element_called, 5);
assert!(!testhandler.elements.is_empty());
assert_eq!(testhandler.elements[0], r#"<bookstore>"#);
assert_eq!(
testhandler.elements[1],
r#"<book xmlns:books="http://example.com/books" xmlns:covers="http://example.com/covers" id="1" category="fiction">"#
);
assert_eq!(
testhandler.elements[2],
r#"<http://example.com/books:page>"#
);
assert_eq!(
testhandler.elements[3],
r#"<http://example.com/covers:cover>"#
);
assert_eq!(testhandler.elements[4], r#"<publisher>"#);
assert_eq!(testhandler.end_element_called, 5);
assert_eq!(testhandler.end_document_called, 1);
assert_eq!(testhandler.mappings.len(), 2);
assert_eq!(testhandler.mappings["books"], "http://example.com/books");
assert_eq!(testhandler.mappings["covers"], "http://example.com/covers");
}
#[test]
fn characters(){
let test_xml = include_str!("resources/characters.xml");
let mut testhandler = TestHandler::new();
parse_string(test_xml.to_string(), Box::new(&mut testhandler))
.expect("Failed to parse test xml");
assert_eq!(testhandler.characters[0], "Hello World");
}
#[derive(Debug)]
struct TestHandler {
start_document_called: usize,
end_document_called: usize,
start_element_called: usize,
end_element_called: usize,
elements: Vec<String>,
mappings: HashMap<String, String>,
characters: Vec<String>,
}
impl TestHandler {
pub fn new() -> Self {
Self {
start_document_called: 0,
end_document_called: 0,
start_element_called: 0,
end_element_called: 0,
elements: vec![],
mappings: HashMap::new(),
characters: vec![],
}
}
}
impl SaxHandler for TestHandler {
fn start_document(&mut self) {
self.start_document_called += 1;
}
fn end_document(&mut self) {
self.end_document_called += 1;
}
fn start_prefix_mapping(&mut self, prefix: &str, uri: &str) {
self.mappings.insert(prefix.to_string(), uri.to_string());
}
fn start_element(
&mut self,
uri: Option<String>,
_local_name: &str,
qualified_name: &str,
attributes: Vec<Attribute>,
) {
self.start_element_called += 1;
let atts = attributes
.iter()
.map(|att| format!(r#"{}="{}""#, att.name, att.value))
.collect::<Vec<String>>()
.join(" ");
let divider = if atts.is_empty() { "" } else { " " };
self.elements
.push(format!("<{}{}{}>", qualified_name, divider, atts));
}
fn end_element(&mut self, _uri: Option<String>, _local_name: &str, _qualified_name: &str) {
self.end_element_called += 1;
}
fn characters(&mut self, chars: &[char]) {
self.characters.push(chars.iter().cloned().collect());
}
fn error(&mut self, _error: &str) {
todo!()
}
}
// use std::collections::HashMap;
// use undeepend::xml::sax_parser::parse_string;
// use undeepend::xml::{Attribute, SaxError, SaxHandler};
// use undeepend::xml::dom_parser::DomSaxHandler;
//
// #[test]
// fn test_xml_header() {
// let test_xml = include_str!("resources/header.xml");
// let mut testhandler = TestHandler::new();
// parse_string(test_xml, Box::new(&mut testhandler))
// .expect("Failed to parse test xml");
// println!("{:?}", testhandler);
// assert_eq!(testhandler.start_document_called, 1);
// assert_eq!(testhandler.end_document_called, 1);
// }
//
// #[test]
// fn test_single_element_short() {
// let test_xml = include_str!("resources/header.xml");
// let mut testhandler = TestHandler::new();
// parse_string(test_xml, Box::new(&mut testhandler))
// .expect("Failed to parse test xml");
// assert_eq!(testhandler.start_document_called, 1);
// assert_eq!(testhandler.start_element_called, 1);
// assert!(!testhandler.elements.is_empty());
// assert_eq!(testhandler.elements[0], "<xml>");
// assert_eq!(testhandler.end_document_called, 1);
// }
//
// #[test]
// fn test_single_element() {
// let test_xml = include_str!("resources/element.xml");
// let mut testhandler = TestHandler::new();
// parse_string(test_xml, Box::new(&mut testhandler))
// .expect("Failed to parse test xml");
// assert_eq!(testhandler.start_document_called, 1);
// assert_eq!(testhandler.start_element_called, 1);
// assert!(!testhandler.elements.is_empty());
// assert_eq!(testhandler.elements[0], "<element>");
// assert_eq!(testhandler.end_document_called, 1);
// }
//
// #[test]
// fn test_single_element_single_attribute() {
// let test_xml = include_str!("resources/element_with_attribute.xml");
// let mut testhandler = TestHandler::new();
// parse_string(test_xml, Box::new(&mut testhandler))
// .expect("Failed to parse test xml");
// assert_eq!(testhandler.start_document_called, 1);
// assert_eq!(testhandler.start_element_called, 1);
// assert!(!testhandler.elements.is_empty());
// assert_eq!(testhandler.elements[0], r#"<element a="1">"#);
// assert_eq!(testhandler.end_element_called, 1);
// assert_eq!(testhandler.end_document_called, 1);
// }
//
// #[test]
// fn test_ignore_comment() {
// let test_xml = include_str!("resources/comment.xml");
// let mut testhandler = TestHandler::new();
// parse_string(test_xml, Box::new(&mut testhandler))
// .expect("Failed to parse test xml");
// assert_eq!(testhandler.start_document_called, 1);
// assert_eq!(testhandler.start_element_called, 1);
// assert!(!testhandler.elements.is_empty());
// assert_eq!(
// testhandler.elements[0],
// r#"<http://example.com/books:bookstore xmlns="http://example.com/books">"#
// );
// assert_eq!(testhandler.end_element_called, 1);
// assert_eq!(testhandler.end_document_called, 1);
// }
//
// #[test]
// fn test_bad_comment() {
// let test_xml = include_str!("resources/illegal_dashes_comment.xml");
// let mut testhandler = TestHandler::new();
// match parse_string(test_xml, Box::new(&mut testhandler)) {
// Err(e) => assert_eq!(e, SaxError::BadCharacter),
// Ok(_) => assert!(false),
// }
// }
//
// #[test]
// fn test_namespaces() {
// let test_xml = include_str!("resources/namespaces.xml");
// let mut testhandler = TestHandler::new();
// parse_string(test_xml, Box::new(&mut testhandler))
// .expect("Failed to parse test xml");
// assert_eq!(testhandler.start_document_called, 1);
// assert_eq!(testhandler.start_element_called, 4);
// assert!(!testhandler.elements.is_empty());
// assert_eq!(testhandler.elements[0], r#"<bookstore>"#);
// assert_eq!(
// testhandler.elements[1],
// r#"<http://example.com/books:book xmlns="http://example.com/books" id="1" category="fiction">"#
// );
// assert_eq!(
// testhandler.elements[2],
// r#"<http://example.com/books:page>"#
// );
// assert_eq!(testhandler.elements[3], r#"<publisher>"#);
// assert_eq!(testhandler.end_element_called, 4);
// assert_eq!(testhandler.end_document_called, 1);
// }
//
// #[test]
// fn test_namespace_prefixes() {
// let test_xml = include_str!("resources/namespaces-prefix.xml");
// let mut testhandler = TestHandler::new();
// parse_string(test_xml, Box::new(&mut testhandler))
// .expect("Failed to parse test xml");
// assert_eq!(testhandler.start_document_called, 1);
// assert_eq!(testhandler.start_element_called, 5);
// assert!(!testhandler.elements.is_empty());
// assert_eq!(testhandler.elements[0], r#"<bookstore>"#);
// assert_eq!(
// testhandler.elements[1],
// r#"<book xmlns:books="http://example.com/books" xmlns:covers="http://example.com/covers" id="1" category="fiction">"#
// );
// assert_eq!(
// testhandler.elements[2],
// r#"<http://example.com/books:page>"#
// );
// assert_eq!(
// testhandler.elements[3],
// r#"<http://example.com/covers:cover>"#
// );
// assert_eq!(testhandler.elements[4], r#"<publisher>"#);
// assert_eq!(testhandler.end_element_called, 5);
// assert_eq!(testhandler.end_document_called, 1);
// assert_eq!(testhandler.mappings.len(), 2);
// assert_eq!(testhandler.mappings["books"], "http://example.com/books");
// assert_eq!(testhandler.mappings["covers"], "http://example.com/covers");
// }
//
// #[test]
// fn characters(){
// let test_xml = include_str!("resources/characters.xml");
// let mut testhandler = TestHandler::new();
// parse_string(test_xml, DomSaxHandler{})
// .expect("Failed to parse test xml");
// assert_eq!(testhandler.characters[0], "Hello World");
// }
// #[derive(Debug)]
// struct TestHandler {
// start_document_called: usize,
// end_document_called: usize,
// start_element_called: usize,
// end_element_called: usize,
// elements: Vec<String>,
// mappings: HashMap<String, String>,
// characters: Vec<String>,
// }
//
// impl TestHandler {
// pub fn new() -> Self {
// Self {
// start_document_called: 0,
// end_document_called: 0,
// start_element_called: 0,
// end_element_called: 0,
// elements: vec![],
// mappings: HashMap::new(),
// characters: vec![],
// }
// }
// }
//
// impl SaxHandler for TestHandler {
// fn start_document(&mut self) {
// self.start_document_called += 1;
// }
//
// fn end_document(&mut self) {
// self.end_document_called += 1;
// }
//
// fn start_prefix_mapping(&mut self, prefix: &str, uri: &str) {
// self.mappings.insert(prefix.to_string(), uri.to_string());
// }
//
// fn start_element(
// &mut self,
// uri: Option<String>,
// _local_name: &str,
// qualified_name: &str,
// attributes: Vec<Attribute>,
// ) {
// self.start_element_called += 1;
// let atts = attributes
// .iter()
// .map(|att| format!(r#"{}="{}""#, att.name, att.value))
// .collect::<Vec<String>>()
// .join(" ");
//
// let divider = if atts.is_empty() { "" } else { " " };
// self.elements
// .push(format!("<{}{}{}>", qualified_name, divider, atts));
// }
//
// fn end_element(&mut self, _uri: Option<String>, _local_name: &str, _qualified_name: &str) {
// self.end_element_called += 1;
// }
//
// fn characters(&mut self, chars: &[char]) {
// self.characters.push(chars.iter().cloned().collect());
// }
//
// fn error(&mut self, _error: &str) {
// todo!()
// }
// }