harmony/harmony-rs/opnsense-config/src/infra/generic_xml.rs

250 lines
10 KiB
Rust

use xml::reader::{EventReader, XmlEvent as ReadEvent};
use xml::writer::{EventWriter, XmlEvent as WriteEvent};
use yaserde::{ser, YaDeserialize as YaDeserializeTrait, YaSerialize as YaSerializeTrait};
use yaserde_derive::{YaDeserialize, YaSerialize};
#[derive(Debug, PartialEq, Default, YaDeserialize)]
pub struct Parent {
// pub rawxml_child: RawXml,
pub string_child: String,
pub child_child: Child,
}
#[derive(Debug, PartialEq, Default, YaDeserialize)]
pub struct Child {
pub child_val: String,
pub child_val2: String,
pub child_option: Option<String>,
}
#[derive(Debug, PartialEq, Default)]
pub struct RawXml(String);
impl YaDeserializeTrait for RawXml {
fn deserialize<R: std::io::Read>(
reader: &mut yaserde::de::Deserializer<R>,
) -> Result<Self, String> {
let mut buffer = String::new();
let mut depth = 0;
let own_name = match reader.peek()? {
ReadEvent::StartElement { name, .. } => name.local_name.clone(),
_ => return Err("RawXml Should start deserializing with StartElement".to_string()),
};
println!("RawXml deserialize from root element name : {own_name}");
loop {
let current_event = reader.peek()?.to_owned();
match current_event.clone() {
ReadEvent::StartElement {
name, attributes, ..
} => {
println!("StartElement {name} depth {depth}");
depth += 1;
let mut attr_string = String::new();
attributes.iter().for_each(|a| {
attr_string.push_str(&format!(r#" {}="{}""#, &a.name, &a.value));
});
buffer.push_str(&format!("<{}{}>", name, attr_string));
let _event = reader.next_event()?;
}
ReadEvent::EndElement { name } => {
println!("EndElement {name} depth {depth}");
depth -= 1;
buffer.push_str(&format!("</{}>", name));
println!(
"Checking if name.local_name {} matches own_name {} at depth {depth}",
&name.local_name, &own_name
);
if name.local_name == own_name && depth == 0 {
println!(
"Found next EndElement is closing my struct, breaking out of loop"
);
break;
} else {
let _event = reader.next_event()?;
}
}
ReadEvent::Characters(content) => {
println!("Characters {content} depth {depth}");
buffer.push_str(&content);
let _event = reader.next_event()?;
}
ReadEvent::StartDocument {
version,
encoding,
standalone,
} => todo!(
"StartDocument {:?} {:?} {:?}",
version,
encoding,
standalone
),
ReadEvent::EndDocument => todo!(),
ReadEvent::ProcessingInstruction { name, data } => {
todo!("ProcessingInstruction {:?}, {:?}", name, data)
}
ReadEvent::CData(cdata) => todo!("CData, {:?}", cdata),
ReadEvent::Comment(comment) => todo!("Comment, {:?}", comment),
ReadEvent::Whitespace(whitespace) => todo!("Whitespace, {:?}", whitespace),
}
let next = reader.peek()?;
println!(
"Processing done on \ncurrent_event : {:?} \nnext : {:?}",
&current_event, &next
);
}
println!("buffered events {buffer}");
Ok(RawXml(buffer))
}
}
impl YaSerializeTrait for RawXml {
fn serialize<W: std::io::Write>(&self, writer: &mut ser::Serializer<W>) -> Result<(), String> {
let content = self.0.clone();
let content = xml::EventReader::from_str(content.as_str());
let mut reader = yaserde::de::Deserializer::new(content);
loop {
let e = reader.next_event()?;
if let ReadEvent::EndDocument = e {
break;
}
writer
.write(e.as_writer_event().unwrap())
.expect("Writer should write write event");
}
Ok(())
}
fn serialize_attributes(
&self,
attributes: Vec<xml::attribute::OwnedAttribute>,
namespace: xml::namespace::Namespace,
) -> Result<
(
Vec<xml::attribute::OwnedAttribute>,
xml::namespace::Namespace,
),
String,
> {
todo!()
}
}
// impl YaSerializeTrait for RawXml {
// fn serialize<W: std::io::Write>(
// &self,
// writer: &mut yaserde::ser::Serializer<W>,
// ) -> Result<(), String> {
// let mut reader = EventReader::from_str(&self.0);
// loop {
// match reader.next() {
// Ok(ReadEvent::StartElement {
// name,
// attributes,
// namespace,
// }) => {
// let write = WriteEvent::from(reader.next().unwrap());
// writer
// .write(WriteEvent::StartElement {
// name: name.clone(),
// attributes: attributes.clone(),
// namespace: namespace.clone(),
// })
// .map_err(|e| e.to_string())?;
// }
// Ok(ReadEvent::EndElement { name }) => {
// writer
// .write(WriteEvent::EndElement { name: Some(name) })
// .map_err(|e| e.to_string())?;
// }
// Ok(ReadEvent::Characters(content)) => {
// writer
// .write(WriteEvent::Characters(&content))
// .map_err(|e| e.to_string())?;
// }
// Ok(ReadEvent::Eof) => break,
// Err(e) => return Err(e.to_string()),
// _ => {}
// }
// }
// Ok(())
// }
// }
//
#[test]
fn rawxml_should_buffer_empty_element() {
let rawxml: RawXml = yaserde::de::from_str("<something/>").unwrap();
assert_eq!(rawxml.0, String::from("<something></something>"));
}
#[test]
fn rawxml_should_buffer_elements_with_different_case_as_they_are() {
let xml = "<xml><Some_thing></Some_thing><something></something></xml>";
let rawxml: RawXml = yaserde::de::from_str(xml).unwrap();
assert_eq!(rawxml.0, String::from(xml));
}
#[test]
fn rawxml_should_buffer_elements_with_attributes() {
let xml = r#"<xml version="ababa"><Some_thing></Some_thing><something></something></xml>"#;
let rawxml: RawXml = yaserde::de::from_str(xml).unwrap();
assert_eq!(rawxml.0, String::from(xml));
}
#[test]
fn rawxml_should_handle_complex_documents() {
let xml = r#"<xml><OpenVPN version="1.0.0"><Overwrites></Overwrites><Instances></Instances><StaticKeys></StaticKeys></OpenVPN><Gateways version="0.0.1"></Gateways><HAProxy version="4.0.0"><general><enabled>1</enabled><gracefulStop>0</gracefulStop><hardStopAfter>60s</hardStopAfter><closeSpreadTime></closeSpreadTime><seamlessReload>0</seamlessReload><storeOcsp>0</storeOcsp><showIntro>1</showIntro><peers><enabled>0</enabled><name1></name1><listen1></listen1><port1>1024</port1><name2></name2><listen2></listen2><port2>1024</port2></peers><tuning><root>0</root><maxConnections></maxConnections><nbthread>1</nbthread><sslServerVerify>ignore</sslServerVerify><maxDHSize>2048</maxDHSize><bufferSize>16384</bufferSize></tuning></general></HAProxy></xml>"#;
let rawxml: RawXml = yaserde::de::from_str(xml).unwrap();
assert_eq!(rawxml.0, String::from(xml));
}
#[test]
fn rawxml_should_serialize_simple_documents() {
let xml = r#"<?xml version="1.0" encoding="utf-8"?><xml />"#;
let rawxml: RawXml = yaserde::de::from_str(xml).unwrap();
assert_eq!(yaserde::ser::to_string(&rawxml).unwrap(), xml);
}
#[test]
fn rawxml_should_serialize_complex_documents() {
let xml = r#"<?xml version="1.0" encoding="utf-8"?><xml><OpenVPN version="1.0.0"><Overwrites /><Instances /><StaticKeys /></OpenVPN><Gateways version="0.0.1" /><HAProxy version="4.0.0"><general><enabled>1</enabled><gracefulStop>0</gracefulStop><hardStopAfter>60s</hardStopAfter><closeSpreadTime /><seamlessReload>0</seamlessReload><storeOcsp>0</storeOcsp><showIntro>1</showIntro><peers><enabled>0</enabled><name1 /><listen1 /><port1>1024</port1><name2 /><listen2 /><port2>1024</port2></peers><tuning><root>0</root><maxConnections /><nbthread>1</nbthread><sslServerVerify>ignore</sslServerVerify><maxDHSize>2048</maxDHSize><bufferSize>16384</bufferSize></tuning></general></HAProxy></xml>"#;
let rawxml: RawXml = yaserde::de::from_str(xml).unwrap();
assert_eq!(yaserde::ser::to_string(&rawxml).unwrap(), xml);
}
#[test]
fn rawxml_should_allow_siblings_before() {
#[derive(YaDeserialize, YaSerialize)]
struct Config {
paul: Vec<String>,
raw: RawXml,
}
let xml = r#"<?xml version="1.0" encoding="utf-8"?><Config><paul>bobob</paul><paul>patate</paul><raw>allo something</raw></Config>"#;
let config: Config = yaserde::de::from_str(xml).unwrap();
assert_eq!(yaserde::ser::to_string(&config).unwrap(), xml);
}
#[test]
fn rawxml_should_allow_siblings_after() {
#[derive(YaDeserialize, YaSerialize)]
struct Config {
raw: RawXml,
paul: Vec<String>,
}
let xml = r#"<?xml version="1.0" encoding="utf-8"?><Config><raw>allo something</raw><paul>bobob</paul><paul>patate</paul></Config>"#;
let config: Config = yaserde::de::from_str(xml).unwrap();
assert_eq!(config.paul.get(0).unwrap(), "bobob");
assert_eq!(config.paul.get(1).unwrap(), "patate");
assert_eq!(config.paul.len(), 2);
assert_eq!(config.raw.0, "<raw>allo something</raw>");
assert_eq!(yaserde::ser::to_string(&config).unwrap(), xml);
}
#[test]
fn rawxml_should_allow_being_end_of_document() {
let xml = r#"<?xml version="1.0" encoding="utf-8"?><Config><raw>allo something</raw><paul>bobob</paul><paul>patate</paul></Config>"#;
let config: RawXml = yaserde::de::from_str(xml).unwrap();
assert_eq!(yaserde::ser::to_string(&config).unwrap(), xml);
}