mirror of
https://github.com/lennart-k/rustical.git
synced 2025-12-14 03:32:15 +00:00
Some initial work on xml parsing
This commit is contained in:
45
crates/xml/tests/de_enum.rs
Normal file
45
crates/xml/tests/de_enum.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
use rustical_xml::XmlRoot;
|
||||
use xml_derive::XmlDeserialize;
|
||||
|
||||
#[test]
|
||||
fn test_struct_untagged_enum() {
|
||||
#[derive(Debug, XmlDeserialize, PartialEq)]
|
||||
struct Propfind {
|
||||
prop: Prop,
|
||||
}
|
||||
|
||||
#[derive(Debug, XmlDeserialize, PartialEq)]
|
||||
struct Prop {
|
||||
#[xml(untagged)]
|
||||
prop: PropEnum,
|
||||
}
|
||||
|
||||
#[derive(Debug, XmlDeserialize, PartialEq)]
|
||||
enum PropEnum {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
impl XmlRoot for Propfind {
|
||||
fn root_tag() -> &'static [u8] {
|
||||
b"propfind"
|
||||
}
|
||||
}
|
||||
|
||||
let doc = Propfind::parse_str(
|
||||
r#"
|
||||
<propfind>
|
||||
<prop>
|
||||
<A/>
|
||||
</prop>
|
||||
</propfind>
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
doc,
|
||||
Propfind {
|
||||
prop: Prop { prop: PropEnum::A }
|
||||
}
|
||||
);
|
||||
}
|
||||
171
crates/xml/tests/de_struct.rs
Normal file
171
crates/xml/tests/de_struct.rs
Normal file
@@ -0,0 +1,171 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use rustical_xml::XmlRoot;
|
||||
use xml_derive::XmlDeserialize;
|
||||
|
||||
#[test]
|
||||
fn test_struct_text_field() {
|
||||
#[derive(Debug, XmlDeserialize, PartialEq)]
|
||||
struct Document {
|
||||
#[xml(text)]
|
||||
text: String,
|
||||
#[xml(text)]
|
||||
text2: String,
|
||||
}
|
||||
|
||||
impl XmlRoot for Document {
|
||||
fn root_tag() -> &'static [u8] {
|
||||
b"document"
|
||||
}
|
||||
}
|
||||
|
||||
let doc = Document::parse_str(r#"<document>Hello!</document>"#).unwrap();
|
||||
assert_eq!(
|
||||
doc,
|
||||
Document {
|
||||
text: "Hello!".to_owned(),
|
||||
text2: "Hello!".to_owned()
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_struct_document() {
|
||||
#[derive(Debug, XmlDeserialize, PartialEq)]
|
||||
struct Document {
|
||||
child: Child,
|
||||
}
|
||||
|
||||
#[derive(Debug, XmlDeserialize, PartialEq, Default)]
|
||||
struct Child {
|
||||
#[xml(text)]
|
||||
text: String,
|
||||
}
|
||||
|
||||
impl XmlRoot for Document {
|
||||
fn root_tag() -> &'static [u8] {
|
||||
b"document"
|
||||
}
|
||||
}
|
||||
|
||||
let doc = Document::parse_str(r#"<document><child>Hello!</child></document>"#).unwrap();
|
||||
assert_eq!(
|
||||
doc,
|
||||
Document {
|
||||
child: Child {
|
||||
text: "Hello!".to_owned()
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_struct_rename_field() {
|
||||
#[derive(Debug, XmlDeserialize, PartialEq)]
|
||||
struct Document {
|
||||
#[xml(rename = "ok-wow")]
|
||||
child: Child,
|
||||
}
|
||||
|
||||
#[derive(Debug, XmlDeserialize, PartialEq, Default)]
|
||||
struct Child {
|
||||
#[xml(text)]
|
||||
text: String,
|
||||
}
|
||||
|
||||
impl XmlRoot for Document {
|
||||
fn root_tag() -> &'static [u8] {
|
||||
b"document"
|
||||
}
|
||||
}
|
||||
|
||||
let doc = Document::parse_str(r#"<document><ok-wow>Hello!</ok-wow></document>"#).unwrap();
|
||||
assert_eq!(
|
||||
doc,
|
||||
Document {
|
||||
child: Child {
|
||||
text: "Hello!".to_owned()
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_struct_optional_field() {
|
||||
#[derive(Debug, XmlDeserialize, PartialEq)]
|
||||
struct Document {
|
||||
#[xml(default = "Default::default")]
|
||||
child: Option<Child>,
|
||||
}
|
||||
|
||||
impl XmlRoot for Document {
|
||||
fn root_tag() -> &'static [u8] {
|
||||
b"document"
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, XmlDeserialize, PartialEq, Default)]
|
||||
struct Child;
|
||||
|
||||
let doc = Document::parse_str(r#"<document><child></child></document>"#).unwrap();
|
||||
assert_eq!(doc, Document { child: Some(Child) });
|
||||
|
||||
let doc = Document::parse_str(r#"<document></document>"#).unwrap();
|
||||
assert_eq!(doc, Document { child: None });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_struct_vec() {
|
||||
#[derive(Debug, XmlDeserialize, PartialEq)]
|
||||
#[xml(root = b"document")]
|
||||
struct Document {
|
||||
#[xml(rename = "child", flatten)]
|
||||
children: Vec<Child>,
|
||||
}
|
||||
|
||||
#[derive(Debug, XmlDeserialize, PartialEq, Default)]
|
||||
struct Child;
|
||||
|
||||
let doc = Document::parse_str(
|
||||
r#"
|
||||
<document>
|
||||
<child />
|
||||
<child />
|
||||
</document>"#,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
doc,
|
||||
Document {
|
||||
children: vec![Child, Child]
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_struct_set() {
|
||||
#[derive(Debug, XmlDeserialize, PartialEq)]
|
||||
#[xml(root = b"document")]
|
||||
struct Document {
|
||||
#[xml(rename = "child", flatten)]
|
||||
children: HashSet<Child>,
|
||||
}
|
||||
|
||||
#[derive(Debug, XmlDeserialize, PartialEq, Default, Eq, Hash)]
|
||||
struct Child;
|
||||
|
||||
let doc = Document::parse_str(
|
||||
r#"
|
||||
<document>
|
||||
<child />
|
||||
<child />
|
||||
</document>"#,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
doc,
|
||||
Document {
|
||||
children: HashSet::from_iter(vec![Child].into_iter())
|
||||
}
|
||||
);
|
||||
}
|
||||
123
crates/xml/tests/propfind.rs
Normal file
123
crates/xml/tests/propfind.rs
Normal file
@@ -0,0 +1,123 @@
|
||||
use quick_xml::events::{BytesStart, Event};
|
||||
use rustical_xml::{Unit, XmlDeserialize, XmlError, XmlRoot};
|
||||
|
||||
#[derive(Debug, XmlDeserialize)]
|
||||
#[xml(rename_all = "kebab-case")]
|
||||
pub enum Prop {
|
||||
#[xml(rename = "displayname")]
|
||||
Displayname(String),
|
||||
#[xml(ns = "DAV:Push", rename = "transports")]
|
||||
Transports,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PropfindElement<T: XmlDeserialize> {
|
||||
// child with name propfind and namespace DAV:
|
||||
pub prop: Vec<T>,
|
||||
pub test: Option<Prop>,
|
||||
}
|
||||
|
||||
impl<T: XmlDeserialize> XmlDeserialize for PropfindElement<T> {
|
||||
fn deserialize<R: std::io::BufRead>(
|
||||
reader: &mut quick_xml::NsReader<R>,
|
||||
start: &BytesStart,
|
||||
empty: bool,
|
||||
) -> Result<Self, XmlError> {
|
||||
// init values for the struct attributes
|
||||
let mut attr_prop: Option<Vec<T>> = None;
|
||||
let mut attr_test: Option<Prop> = None;
|
||||
|
||||
if !empty {
|
||||
let mut buf = Vec::new();
|
||||
loop {
|
||||
match reader.read_event_into(&mut buf)? {
|
||||
Event::End(e) if e.name() == start.name() => {
|
||||
break;
|
||||
}
|
||||
Event::Eof => return Err(XmlError::Eof),
|
||||
Event::Start(start) => {
|
||||
let (_ns, name) = reader.resolve_element(start.name());
|
||||
match name.as_ref() {
|
||||
b"prop" => {
|
||||
if attr_prop.is_none() {
|
||||
attr_prop = Some(Vec::<T>::deserialize(reader, &start, false)?);
|
||||
}
|
||||
}
|
||||
b"test" => {
|
||||
if attr_test.is_none() {
|
||||
attr_test = Some(Prop::deserialize(reader, &start, false)?);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return Err(XmlError::InvalidTag(
|
||||
String::from_utf8_lossy(name.as_ref()).to_string(),
|
||||
"prop".to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
Event::Empty(start) => {
|
||||
let (_ns, name) = reader.resolve_element(start.name());
|
||||
match name.as_ref() {
|
||||
b"prop" => {
|
||||
if attr_prop.is_none() {
|
||||
attr_prop = Some(Vec::<T>::deserialize(reader, &start, true)?);
|
||||
}
|
||||
}
|
||||
b"test" => {
|
||||
if attr_test.is_none() {
|
||||
attr_test = Some(Prop::deserialize(reader, &start, true)?);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return Err(XmlError::InvalidTag(
|
||||
String::from_utf8_lossy(name.as_ref()).to_string(),
|
||||
"prop".to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
a => {
|
||||
dbg!(a);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let attr_prop = attr_prop.ok_or(XmlError::MissingField("prop"))?;
|
||||
Ok(Self {
|
||||
prop: attr_prop,
|
||||
test: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: XmlDeserialize> XmlRoot for PropfindElement<T> {
|
||||
fn root_tag() -> &'static [u8] {
|
||||
b"propfind"
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_propfind() {
|
||||
let propfind: PropfindElement<Prop> = PropfindElement::parse_str(
|
||||
r#"
|
||||
<propfind xmlns="DAV:" xmlns:P="DAV:Push">
|
||||
<P:prop>
|
||||
<displayname>Hello!</displayname>
|
||||
<transports xmlns="DAV:Push" />
|
||||
<transports xmlns="DAV:Push"></transports>
|
||||
</P:prop>
|
||||
<test>
|
||||
<displayname>Okay wow!</displayname>
|
||||
</test>
|
||||
</propfind>
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
dbg!(propfind);
|
||||
}
|
||||
|
||||
fn asd() {
|
||||
let a: Option<String> = None;
|
||||
}
|
||||
Reference in New Issue
Block a user