yaserde/
lib.rs

1//! # YaSerDe
2//!
3//! YaSerDe is a framework for ***ser***ializing and ***de***serializing Rust data
4//! structures efficiently and generically from and into XML.
5//!
6//! YaSerDe makes it easy to serialize XML documents given an properly annotated struct.
7//! Please refer to the `examples` directory for the complete code shown below.
8//!
9//! # Serialize
10//!
11//! For instance, let's say that one wants to generate a XML file for the
12//! [Rust-Embedded community](https://github.com/rust-embedded/). A well known XML
13//! file for microcontrollers is called [SVD](https://github.com/rust-embedded/svd/)
14//! and it can be defined on YaSerDe via structs like so:
15//!
16//!```rust
17//! use yaserde_derive::YaSerialize;
18//!
19//! #[derive(PartialEq, Debug, YaSerialize)]
20//! #[yaserde(rename = "device")]
21//! struct Device {
22//!   #[yaserde(attribute = true)]
23//!   schemaversion: String,
24//!   #[yaserde(attribute = true)]
25//!   xmlns: String,
26//!   #[yaserde(attribute = true)]
27//!   xsnonamespaceschemalocation: String,
28//!   attributes: DeviceAttributes
29//! }
30//!
31//! #[derive(PartialEq, Debug, YaSerialize)]
32//! struct DeviceAttributes {
33//!   vendor: String,
34//! }
35//!```
36//!
37//! The interspersed `#[yaserde()]` macros give some indication of what the resulting XML
38//! Will look like, namely, a short snippet of the struct above in XML would be depending on
39//! concrete values passed to the struct (not shown):
40//!
41//!```xml
42//! (...)
43//! <device schemaversion: "1.0-example", xmlns: "ns:.... example"
44//! xsnonamespaceschemalocation: "foo_bar_baz">
45//!    <devattributes>
46//!    </devattributes>
47//! (...)
48//!```
49//!
50//! Notice the important difference in **XML output representation between `attributes` vs
51//! `child`**, since SVD expects information in that particular arrangement. YaSerDe allows that
52//! serialized XML to be valid unlike other Rust XML (de)serialization crates (i.e quick-xml).
53//!
54//! Also the last `DevAttrs` struct field is indeed another struct, so one can chain several
55//! structs to compose the XML structure (again, see examples folder for the complete
56//! example).
57//!
58//!
59//!```toml
60//! [dependencies]
61//! # serde = { version = "1.0.123", features = [ "derive" ] }
62//! # quick-xml = { version = "0.21.0", features = [ "serialize" ] }
63//! yaserde = "0.5.1"
64//! yaserde_derive = "0.5.1"
65//! ```
66//!
67//! Last but not least, in order to have a nice, pretty printed XML output one can do:
68//!
69//! ```ignore
70//!     // Display pretty printed XML
71//!    let yaserde_cfg = yaserde::ser::Config{
72//!        perform_indent: true,
73//!        .. Default::default()
74//!    };
75//!
76//!     println!("{}", yaserde::ser::to_string_with_config(&dev, &yaserde_cfg).ok().unwrap());
77//! ```
78//!
79//! Avoid using either `{:?}` or `{:#?}` println! formatters since it'll garble the output of your
80//! XML.
81
82#[cfg(feature = "yaserde_derive")]
83#[allow(unused_imports)]
84#[macro_use]
85extern crate yaserde_derive;
86
87#[cfg(feature = "yaserde_derive")]
88#[doc(hidden)]
89pub use yaserde_derive::*;
90
91use std::io::{Read, Write};
92use xml::writer::XmlEvent;
93
94pub mod de;
95pub mod primitives;
96pub mod ser;
97
98/// A **data structure** that can be deserialized from any data format supported by YaSerDe.
99pub trait YaDeserialize: Sized {
100  fn deserialize<R: Read>(reader: &mut de::Deserializer<R>) -> Result<Self, String>;
101}
102
103/// A **data structure** that can be serialized into any data format supported by YaSerDe.
104pub trait YaSerialize: Sized {
105  fn serialize<W: Write>(&self, writer: &mut ser::Serializer<W>) -> Result<(), String>;
106
107  fn serialize_attributes(
108    &self,
109    attributes: Vec<xml::attribute::OwnedAttribute>,
110    namespace: xml::namespace::Namespace,
111  ) -> Result<
112    (
113      Vec<xml::attribute::OwnedAttribute>,
114      xml::namespace::Namespace,
115    ),
116    String,
117  >;
118}
119
120/// A **visitor** that can be implemented to retrieve information from source file.
121pub trait Visitor<'de>: Sized {
122  /// The value produced by this visitor.
123  type Value;
124
125  fn visit_bool(self, v: &str) -> Result<Self::Value, String> {
126    Err(format!("Unexpected bool {:?}", v))
127  }
128
129  fn visit_i8(self, v: &str) -> Result<Self::Value, String> {
130    Err(format!("Unexpected i8 {:?}", v))
131  }
132
133  fn visit_u8(self, v: &str) -> Result<Self::Value, String> {
134    Err(format!("Unexpected u8 {:?}", v))
135  }
136
137  fn visit_i16(self, v: &str) -> Result<Self::Value, String> {
138    Err(format!("Unexpected i16 {:?}", v))
139  }
140
141  fn visit_u16(self, v: &str) -> Result<Self::Value, String> {
142    Err(format!("Unexpected u16 {:?}", v))
143  }
144
145  fn visit_i32(self, v: &str) -> Result<Self::Value, String> {
146    Err(format!("Unexpected i32 {:?}", v))
147  }
148
149  fn visit_u32(self, v: &str) -> Result<Self::Value, String> {
150    Err(format!("Unexpected u32 {:?}", v))
151  }
152
153  fn visit_i64(self, v: &str) -> Result<Self::Value, String> {
154    Err(format!("Unexpected i64 {:?}", v))
155  }
156
157  fn visit_u64(self, v: &str) -> Result<Self::Value, String> {
158    Err(format!("Unexpected u64 {:?}", v))
159  }
160
161  fn visit_f32(self, v: &str) -> Result<Self::Value, String> {
162    Err(format!("Unexpected f32 {:?}", v))
163  }
164
165  fn visit_f64(self, v: &str) -> Result<Self::Value, String> {
166    Err(format!("Unexpected f64 {:?}", v))
167  }
168
169  fn visit_str(self, v: &str) -> Result<Self::Value, String> {
170    Err(format!("Unexpected str {:?}", v))
171  }
172}
173
174macro_rules! serialize_type {
175  ($type:ty) => {
176    impl YaSerialize for $type {
177      fn serialize<W: Write>(&self, writer: &mut ser::Serializer<W>) -> Result<(), String> {
178        let content = format!("{}", self);
179        let event = XmlEvent::characters(&content);
180        let _ret = writer.write(event);
181        Ok(())
182      }
183
184      fn serialize_attributes(
185        &self,
186        attributes: Vec<xml::attribute::OwnedAttribute>,
187        namespace: xml::namespace::Namespace,
188      ) -> Result<
189        (
190          Vec<xml::attribute::OwnedAttribute>,
191          xml::namespace::Namespace,
192        ),
193        String,
194      > {
195        Ok((attributes, namespace))
196      }
197    }
198  };
199}
200
201serialize_type!(bool);
202serialize_type!(char);
203
204serialize_type!(usize);
205serialize_type!(u8);
206serialize_type!(u16);
207serialize_type!(u32);
208serialize_type!(u64);
209
210serialize_type!(isize);
211serialize_type!(i8);
212serialize_type!(i16);
213serialize_type!(i32);
214serialize_type!(i64);
215
216serialize_type!(f32);
217serialize_type!(f64);
218
219/// Re-export for use in yaserde_derive
220#[doc(hidden)]
221pub use xml as __xml;
222
223/// Re-export for use in yaserde_derive
224#[doc(hidden)]
225pub use log as __log;
226
227/// Used in code generated by yaserde_derive for logging
228#[macro_export]
229#[doc(hidden)]
230macro_rules! __derive_debug {
231  ($($arg:tt)+) => { ::yaserde::__log::debug!(target: "yaserde_derive", $($arg)+) };
232}
233
234/// Used in code generated by yaserde_derive for logging
235#[macro_export]
236#[doc(hidden)]
237macro_rules! __derive_trace {
238  ($($arg:tt)+) => { ::yaserde::__log::trace!(target: "yaserde_derive", $($arg)+) };
239}
240
241#[test]
242fn default_visitor() {
243  struct Test;
244  impl<'de> Visitor<'de> for Test {
245    type Value = u8;
246  }
247
248  macro_rules! test_type {
249    ($visitor:tt, $message:expr) => {{
250      let t = Test {};
251      assert_eq!(t.$visitor(""), Err($message.to_string()));
252    }};
253  }
254
255  test_type!(visit_bool, "Unexpected bool \"\"");
256  test_type!(visit_i8, "Unexpected i8 \"\"");
257  test_type!(visit_u8, "Unexpected u8 \"\"");
258  test_type!(visit_i16, "Unexpected i16 \"\"");
259  test_type!(visit_u16, "Unexpected u16 \"\"");
260  test_type!(visit_i32, "Unexpected i32 \"\"");
261  test_type!(visit_u32, "Unexpected u32 \"\"");
262  test_type!(visit_i64, "Unexpected i64 \"\"");
263  test_type!(visit_u64, "Unexpected u64 \"\"");
264  test_type!(visit_str, "Unexpected str \"\"");
265}
266
267#[doc(hidden)]
268#[macro_export]
269macro_rules! test_for_type {
270  ($type:ty, $value:expr, $content:expr) => {{
271    #[derive(Debug, PartialEq, YaDeserialize, YaSerialize)]
272    #[yaserde(rename = "data")]
273    pub struct Data {
274      item: $type,
275    }
276
277    let model = Data { item: $value };
278
279    let content = if let Some(str_value) = $content {
280      let str_value: &str = str_value;
281      format!("<data><item>{}</item></data>", str_value)
282    } else {
283      "<data />".to_owned()
284    };
285
286    serialize_and_validate!(model, content);
287    deserialize_and_validate!(&content, model, Data);
288  }};
289}
290
291#[doc(hidden)]
292#[macro_export]
293macro_rules! test_for_attribute_type {
294  ($type: ty, $value: expr, $content: expr) => {{
295    #[derive(Debug, PartialEq, YaDeserialize, YaSerialize)]
296    #[yaserde(rename = "data")]
297    pub struct Data {
298      #[yaserde(attribute = true)]
299      item: $type,
300    }
301    let model = Data { item: $value };
302
303    let content = if let Some(str_value) = $content {
304      "<data item=\"".to_string() + str_value + "\" />"
305    } else {
306      "<data />".to_string()
307    };
308
309    serialize_and_validate!(model, content);
310    deserialize_and_validate!(&content, model, Data);
311  }};
312}
313
314#[doc(hidden)]
315#[macro_export]
316macro_rules! deserialize_and_validate {
317  ($content: expr, $model: expr, $struct: tt) => {
318    log::debug!("deserialize_and_validate @ {}:{}", file!(), line!());
319    let loaded: Result<$struct, String> = yaserde::de::from_str($content);
320    assert_eq!(loaded, Ok($model));
321  };
322}
323
324#[doc(hidden)]
325#[macro_export]
326macro_rules! serialize_and_validate {
327  ($model: expr, $content: expr) => {
328    log::debug!("serialize_and_validate @ {}:{}", file!(), line!());
329    let data: Result<String, String> = yaserde::ser::to_string(&$model);
330
331    let content = &format!(r#"<?xml version="1.0" encoding="utf-8"?>{}"#, $content);
332    assert_eq!(
333      data,
334      Ok(content.split("\n").map(|s| s.trim()).collect::<String>())
335    );
336  };
337}