element with single attribute
This commit is contained in:
commit
8cd82fffbd
13 changed files with 368 additions and 0 deletions
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/undeepend2.iml" filepath="$PROJECT_DIR$/.idea/undeepend2.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
11
.idea/undeepend2.iml
generated
Normal file
11
.idea/undeepend2.iml
generated
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="EMPTY_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
1
src/lib.rs
Normal file
1
src/lib.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
mod maven;
|
||||
1
src/maven/mod.rs
Normal file
1
src/maven/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
mod xml;
|
||||
37
src/maven/xml/debug.rs
Normal file
37
src/maven/xml/debug.rs
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
use log::debug;
|
||||
use crate::maven::xml::SaxHandler;
|
||||
|
||||
pub struct DebugHandler {}
|
||||
|
||||
impl SaxHandler for DebugHandler {
|
||||
fn start_document(&mut self) {
|
||||
debug!("start_document");
|
||||
}
|
||||
fn end_document(&mut self) {
|
||||
debug!("end_document");
|
||||
}
|
||||
fn start_prefix_mapping(&mut self, _prefix: &str, _uri: &str) {
|
||||
debug!("start_prefix_mapping");
|
||||
}
|
||||
fn end_prefix_mapping(&mut self, _prefix: &str, _uri: &str) {
|
||||
debug!("end_prefix_mapping");
|
||||
}
|
||||
fn start_element(
|
||||
&mut self,
|
||||
_uri: &str,
|
||||
local_name: &str,
|
||||
_qualified_name: &str,
|
||||
attributes: Vec<crate::maven::xml::Attribute>,
|
||||
) {
|
||||
debug!("start_element {}, {:?}", local_name, attributes);
|
||||
}
|
||||
fn end_element(&mut self, _uri: &str, local_name: &str, _qualified_name: &str) {
|
||||
debug!("end_element {} ", local_name);
|
||||
}
|
||||
fn characters(&mut self, chars: &[char]) {
|
||||
debug!("characters {:?}", chars.iter().collect::<String>());
|
||||
}
|
||||
fn error(&mut self, _error: &str) {
|
||||
debug!("error");
|
||||
}
|
||||
}
|
||||
28
src/maven/xml/mod.rs
Normal file
28
src/maven/xml/mod.rs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
mod sax_parser;
|
||||
mod sax_parser_test;
|
||||
mod debug;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Attribute {
|
||||
name: String,
|
||||
namespace: Option<String>,
|
||||
value: String,
|
||||
}
|
||||
|
||||
pub trait SaxHandler {
|
||||
fn start_document(&mut self);
|
||||
fn end_document(&mut self);
|
||||
fn start_prefix_mapping(&mut self, prefix: &str, uri: &str);
|
||||
fn end_prefix_mapping(&mut self, prefix: &str, uri: &str);
|
||||
fn start_element(
|
||||
&mut self,
|
||||
uri: &str,
|
||||
local_name: &str,
|
||||
qualified_name: &str,
|
||||
attributes: Vec<Attribute>,
|
||||
);
|
||||
fn end_element(&mut self, uri: &str, local_name: &str, qualified_name: &str);
|
||||
fn characters(&mut self, chars: &[char]);
|
||||
fn error(&mut self, error: &str);
|
||||
}
|
||||
|
||||
130
src/maven/xml/sax_parser.rs
Normal file
130
src/maven/xml/sax_parser.rs
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
use crate::maven::xml::{Attribute, SaxHandler};
|
||||
|
||||
pub fn parse_string(xml: String, handler: Box<&mut dyn SaxHandler>) -> anyhow::Result<()> {
|
||||
let mut parser = SAXParser::new(xml, handler);
|
||||
parser.parse()
|
||||
}
|
||||
|
||||
struct SAXParser<'a> {
|
||||
xml: Vec<char>,
|
||||
handler: Box<&'a mut dyn SaxHandler>,
|
||||
position: usize,
|
||||
current: char,
|
||||
}
|
||||
|
||||
impl<'a> SAXParser<'a> {
|
||||
pub fn new(xml: String, handler: Box<&'a mut dyn SaxHandler>) -> Self {
|
||||
Self {
|
||||
xml: xml.chars().collect(),
|
||||
handler,
|
||||
position: 0,
|
||||
current: '\0',
|
||||
}
|
||||
}
|
||||
|
||||
fn parse(&mut self) -> anyhow::Result<()> {
|
||||
self.expect(
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
|
||||
"Content is not allowed in prolog.",
|
||||
)?;
|
||||
self.skip_whitespace()?;
|
||||
self.handler.start_document();
|
||||
self.parse_elements()
|
||||
}
|
||||
|
||||
fn parse_elements(&mut self) -> anyhow::Result<()> {
|
||||
if self.current == '<' {
|
||||
self.advance()?;
|
||||
if self.next_char()? != '/' {
|
||||
self.parse_start_element()?;
|
||||
} else {
|
||||
self.parse_end_element()?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_start_element(&mut self) -> anyhow::Result<()> {
|
||||
let name = self.read_until(" />")?;
|
||||
let mut atts = vec![];
|
||||
let mut c = self.current;
|
||||
while c == ' ' {
|
||||
self.skip_whitespace()?;
|
||||
atts.push(self.parse_attribute()?);
|
||||
c = self.advance()?;
|
||||
}
|
||||
|
||||
self.handler.start_element("", name.as_str(), "", atts);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_attribute(&mut self) -> anyhow::Result<Attribute> {
|
||||
let att_name = self.read_until("=")?;
|
||||
self.skip_whitespace()?;
|
||||
self.expect("\"", "Expected start of attribute value")?;
|
||||
let att_value = self.read_until("\"")?;
|
||||
|
||||
Ok(Attribute {
|
||||
name: att_name.trim().to_string(),
|
||||
namespace: Some("".to_string()),
|
||||
value: att_value,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_end_element(&mut self) -> anyhow::Result<()> {
|
||||
let name = self.read_until(">")?;
|
||||
self.handler.end_element("", name.as_str(), "");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_until(&mut self, until: &str) -> anyhow::Result<String> {
|
||||
let start = self.position;
|
||||
let mut c = self.current;
|
||||
let until = until.chars().collect::<Vec<char>>();
|
||||
while !until.contains(&c) {
|
||||
c = self.advance()?;
|
||||
}
|
||||
Ok(self.xml[start - 1..self.position - 1]
|
||||
.iter()
|
||||
.collect::<String>())
|
||||
}
|
||||
|
||||
fn skip_whitespace(&mut self) -> anyhow::Result<()> {
|
||||
let mut c = self.current;
|
||||
while (c.is_whitespace()) && self.position < self.xml.len() {
|
||||
c = self.advance()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn advance(&mut self) -> anyhow::Result<char> {
|
||||
self.position += 1;
|
||||
self.current = self.xml[self.position - 1];
|
||||
Ok(self.current)
|
||||
}
|
||||
|
||||
fn next_char(&mut self) -> anyhow::Result<char> {
|
||||
if self.position >= self.xml.len() {
|
||||
Err(anyhow::anyhow!("End reached"))
|
||||
} else {
|
||||
Ok(self.xml[self.position + 1])
|
||||
}
|
||||
}
|
||||
|
||||
fn expect(&mut self, header_line: &str, message: &str) -> anyhow::Result<()> {
|
||||
for c in header_line.chars() {
|
||||
if !self.expect_char(c)? {
|
||||
return Err(anyhow::anyhow!(message.to_string()));
|
||||
}
|
||||
}
|
||||
self.advance()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn expect_char(&mut self, expected: char) -> anyhow::Result<bool> {
|
||||
if self.position >= self.xml.len() {
|
||||
return Ok(false);
|
||||
}
|
||||
Ok(self.advance()? == expected)
|
||||
}
|
||||
}
|
||||
132
src/maven/xml/sax_parser_test.rs
Normal file
132
src/maven/xml/sax_parser_test.rs
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
use crate::maven::xml::{Attribute, SaxHandler};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::maven::xml::sax_parser::parse_string;
|
||||
use crate::maven::xml::sax_parser_test::TestHandler;
|
||||
use std::sync::Once;
|
||||
|
||||
static INIT: Once = Once::new();
|
||||
|
||||
pub fn initialize() {
|
||||
INIT.call_once(|| {
|
||||
env_logger::init();
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_xml_header() {
|
||||
let test_xml = include_str!("test/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!(testhandler.start_document_called);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_single_element_short() {
|
||||
let test_xml = include_str!("test/header.xml");
|
||||
let mut testhandler = TestHandler::new();
|
||||
parse_string(test_xml.to_string(), Box::new(&mut testhandler))
|
||||
.expect("Failed to parse test xml");
|
||||
assert!(testhandler.start_document_called);
|
||||
assert!(testhandler.start_element_called);
|
||||
assert!(!testhandler.elements.is_empty());
|
||||
assert_eq!(testhandler.elements[0], "<xml>");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_single_element() {
|
||||
let test_xml = include_str!("test/element.xml");
|
||||
let mut testhandler = TestHandler::new();
|
||||
parse_string(test_xml.to_string(), Box::new(&mut testhandler))
|
||||
.expect("Failed to parse test xml");
|
||||
assert!(testhandler.start_document_called);
|
||||
assert!(testhandler.start_element_called);
|
||||
assert!(!testhandler.elements.is_empty());
|
||||
assert_eq!(testhandler.elements[0], "<element>");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_single_element_single_attribute() {
|
||||
let test_xml = include_str!("test/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!(testhandler.start_document_called);
|
||||
assert!(testhandler.start_element_called);
|
||||
assert!(!testhandler.elements.is_empty());
|
||||
assert_eq!(testhandler.elements[0], r#"<element a="1">"#);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct TestHandler {
|
||||
start_document_called: bool,
|
||||
end_document_called: bool,
|
||||
start_element_called: bool,
|
||||
end_element_called: bool,
|
||||
elements: Vec<String>,
|
||||
}
|
||||
|
||||
impl TestHandler {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
start_document_called: false,
|
||||
end_document_called: false,
|
||||
start_element_called: false,
|
||||
end_element_called: false,
|
||||
elements: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SaxHandler for TestHandler {
|
||||
fn start_document(&mut self) {
|
||||
self.start_document_called = true;
|
||||
}
|
||||
|
||||
fn end_document(&mut self) {
|
||||
self.end_document_called = true;
|
||||
}
|
||||
|
||||
fn start_prefix_mapping(&mut self, _prefix: &str, _uri: &str) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn end_prefix_mapping(&mut self, _prefix: &str, _uri: &str) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn start_element(
|
||||
&mut self,
|
||||
_uri: &str,
|
||||
local_name: &str,
|
||||
_qualified_name: &str,
|
||||
attributes: Vec<Attribute>,
|
||||
) {
|
||||
self.start_element_called = true;
|
||||
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!("<{}{}{}>", local_name, divider, atts));
|
||||
}
|
||||
|
||||
fn end_element(&mut self, _uri: &str, _local_name: &str, _qualified_name: &str) {
|
||||
self.end_element_called = true;
|
||||
}
|
||||
|
||||
fn characters(&mut self, _chars: &[char]) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn error(&mut self, _error: &str) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
2
src/maven/xml/test/element.xml
Normal file
2
src/maven/xml/test/element.xml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<element></element>
|
||||
2
src/maven/xml/test/element_with_attribute.xml
Normal file
2
src/maven/xml/test/element_with_attribute.xml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<element a="1"></element>
|
||||
2
src/maven/xml/test/header.xml
Normal file
2
src/maven/xml/test/header.xml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xml/>
|
||||
Loading…
Add table
Reference in a new issue