rustnetconf_yang/
serialize.rs1use quick_xml::events::{BytesEnd, BytesStart, BytesText, Event};
8pub use quick_xml::Writer;
9pub use std::io::Cursor;
10
11pub trait WriteXmlFields {
17 fn write_xml_fields(&self, writer: &mut Writer<Cursor<Vec<u8>>>) -> Result<(), XmlError>;
22}
23
24pub trait ToNetconfXml: WriteXmlFields {
28 fn namespace(&self) -> &str;
30
31 fn root_element(&self) -> &str;
33
34 fn to_xml(&self) -> Result<String, XmlError>;
36}
37
38pub trait FromNetconfXml: Sized {
40 fn from_xml(xml: &str) -> Result<Self, XmlError>;
42}
43
44#[derive(Debug, thiserror::Error)]
46pub enum XmlError {
47 #[error("XML write error: {0}")]
48 Write(String),
49
50 #[error("XML parse error: {0}")]
51 Parse(String),
52
53 #[error("missing required field: {0}")]
54 MissingField(String),
55
56 #[error("invalid value for field '{field}': {message}")]
57 InvalidValue { field: String, message: String },
58}
59
60pub fn write_text_element(
62 writer: &mut Writer<Cursor<Vec<u8>>>,
63 name: &str,
64 value: &str,
65) -> Result<(), XmlError> {
66 let start = BytesStart::new(name);
67 writer
68 .write_event(Event::Start(start))
69 .map_err(|e| XmlError::Write(e.to_string()))?;
70 writer
71 .write_event(Event::Text(BytesText::new(value)))
72 .map_err(|e| XmlError::Write(e.to_string()))?;
73 writer
74 .write_event(Event::End(BytesEnd::new(name)))
75 .map_err(|e| XmlError::Write(e.to_string()))?;
76 Ok(())
77}
78
79pub fn write_start_with_ns(
81 writer: &mut Writer<Cursor<Vec<u8>>>,
82 name: &str,
83 namespace: &str,
84) -> Result<(), XmlError> {
85 let mut start = BytesStart::new(name);
86 start.push_attribute(("xmlns", namespace));
87 writer
88 .write_event(Event::Start(start))
89 .map_err(|e| XmlError::Write(e.to_string()))?;
90 Ok(())
91}
92
93pub fn write_end(writer: &mut Writer<Cursor<Vec<u8>>>, name: &str) -> Result<(), XmlError> {
95 writer
96 .write_event(Event::End(BytesEnd::new(name)))
97 .map_err(|e| XmlError::Write(e.to_string()))?;
98 Ok(())
99}
100
101pub fn write_element_with_fields<T: WriteXmlFields>(
105 writer: &mut Writer<Cursor<Vec<u8>>>,
106 name: &str,
107 value: &T,
108) -> Result<(), XmlError> {
109 writer
110 .write_event(Event::Start(BytesStart::new(name)))
111 .map_err(|e| XmlError::Write(e.to_string()))?;
112 value.write_xml_fields(writer)?;
113 writer
114 .write_event(Event::End(BytesEnd::new(name)))
115 .map_err(|e| XmlError::Write(e.to_string()))?;
116 Ok(())
117}
118
119pub fn new_writer() -> Writer<Cursor<Vec<u8>>> {
121 Writer::new_with_indent(Cursor::new(Vec::new()), b' ', 2)
122}
123
124pub fn finish_writer(writer: Writer<Cursor<Vec<u8>>>) -> Result<String, XmlError> {
126 let buf = writer.into_inner().into_inner();
127 String::from_utf8(buf).map_err(|e| XmlError::Write(e.to_string()))
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133
134 #[test]
135 fn test_write_text_element() {
136 let mut writer = new_writer();
137 write_text_element(&mut writer, "host-name", "spine-01").unwrap();
138 let xml = finish_writer(writer).unwrap();
139 assert_eq!(xml, "<host-name>spine-01</host-name>");
140 }
141
142 #[test]
143 fn test_write_start_with_ns() {
144 let mut writer = new_writer();
145 write_start_with_ns(
146 &mut writer,
147 "interfaces",
148 "urn:ietf:params:xml:ns:yang:ietf-interfaces",
149 )
150 .unwrap();
151 write_end(&mut writer, "interfaces").unwrap();
152 let xml = finish_writer(writer).unwrap();
153 assert!(xml.contains("xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\""));
154 }
155}