hard_xml/
lib.rs

1//! Strong typed xml, based on xmlparser.
2//!
3//! ## Quick Start
4//!
5//! ```rust
6//! use std::borrow::Cow;
7//! use hard_xml::{XmlRead, XmlWrite};
8//!
9//! #[derive(XmlWrite, XmlRead, PartialEq, Debug)]
10//! #[xml(tag = "parent")]
11//! struct Parent<'a> {
12//!     #[xml(attr = "attr1")]
13//!     attr1: Cow<'a, str>,
14//!     #[xml(attr = "attr2")]
15//!     attr2: Option<Cow<'a, str>>,
16//!     #[xml(child = "child")]
17//!     child: Vec<Child<'a>>,
18//! }
19//!
20//! #[derive(XmlWrite, XmlRead, PartialEq, Debug)]
21//! #[xml(tag = "child")]
22//! struct Child<'a> {
23//!     #[xml(text)]
24//!     text: Cow<'a, str>,
25//! }
26//!
27//! assert_eq!(
28//!     (Parent { attr1: "val".into(), attr2: None, child: vec![] }).to_string().unwrap(),
29//!     r#"<parent attr1="val"/>"#
30//! );
31//!
32//! assert_eq!(
33//!     Parent::from_str(r#"<parent attr1="val" attr2="val"><child></child></parent>"#).unwrap(),
34//!     Parent { attr1: "val".into(), attr2: Some("val".into()), child: vec![Child { text: "".into() }] }
35//! );
36//! ```
37//!
38//! ## Attributes
39//!
40//! ### `#[xml(strict(...))]`
41//!
42//! Opt-in to stricter input handling.
43//!
44//! ```rust
45//! #[derive(hard_xml::XmlWrite, hard_xml::XmlRead, PartialEq, Debug)]
46//! #[xml(tag = "ex")]
47//! #[xml(strict(unknown_attribute, unknown_element))]
48//! struct Ex {
49//!     // ...
50//! }
51//! ```
52//!
53//! #### Strict Options
54//!
55//! ##### `strict(unknown_attribute)`
56//!
57//! Fail to parse if an unknown attribute is encountered.
58//!
59//! ```rust
60//! # use hard_xml::XmlRead;
61//! #[derive(Debug, hard_xml::XmlRead)]
62//! #[xml(tag = "ex")]
63//! #[xml(strict(unknown_attribute))]
64//! struct Ex {
65//! }
66//!
67//! assert_eq!(
68//!     Ex::from_str("<ex foo='bar'/>").unwrap_err().to_string(),
69//!     r#"unknown field "foo" in element "Ex""#);
70//! ```
71//!
72//! ##### `strict(unknown_element)`
73//!
74//!  Fail to parse if an unknown child element is encountered.
75//!
76//! ```rust
77//! # use hard_xml::XmlRead;
78//! #[derive(Debug, hard_xml::XmlRead)]
79//! #[xml(tag = "ex")]
80//! #[xml(strict(unknown_element))]
81//! struct Ex {
82//! }
83//!
84//! assert_eq!(
85//!     Ex::from_str("<ex><unknown/></ex>").unwrap_err().to_string(),
86//!     r#"unknown field "unknown" in element "Ex""#);
87//! ```
88//!
89//! ### `#[xml(tag = "")]`
90//!
91//! Specifies the xml tag of a struct or an enum variant.
92//!
93//! ```rust
94//! # use hard_xml::{XmlRead, XmlWrite};
95//! #[derive(XmlWrite, XmlRead, PartialEq, Debug)]
96//! #[xml(tag = "parent")]
97//! struct Parent {}
98//!
99//! assert_eq!(
100//!     (Parent {}).to_string().unwrap(),
101//!     r#"<parent/>"#
102//! );
103//!
104//! assert_eq!(
105//!     Parent::from_str(r#"<parent/>"#).unwrap(),
106//!     Parent {}
107//! );
108//! ```
109//!
110//! ```rust
111//! # use hard_xml::{XmlRead, XmlWrite};
112//! #[derive(XmlWrite, XmlRead, PartialEq, Debug)]
113//! #[xml(tag = "tag1")]
114//! struct Tag1 {}
115//!
116//! #[derive(XmlWrite, XmlRead, PartialEq, Debug)]
117//! #[xml(tag = "tag2")]
118//! struct Tag2 {}
119//!
120//! #[derive(XmlWrite, XmlRead, PartialEq, Debug)]
121//! enum Tag {
122//!     #[xml(tag = "tag1")]
123//!     Tag1(Tag1),
124//!     #[xml(tag = "tag2")]
125//!     Tag2(Tag2),
126//! }
127//!
128//! assert_eq!(
129//!     (Tag::Tag1(Tag1 {})).to_string().unwrap(),
130//!     r#"<tag1/>"#
131//! );
132//!
133//! assert_eq!(
134//!     Tag::from_str(r#"<tag2></tag2>"#).unwrap(),
135//!     Tag::Tag2(Tag2 {})
136//! );
137//! ```
138//!
139//! ### `#[xml(attr = "")]`
140//!
141//! Specifies that a struct field is attribute. Support
142//! `Cow<str>`, `Option<Cow<str>>`, `T` and `Option<T>`
143//! where `T: FromStr + Display`.
144//!
145//! ```rust
146//! use hard_xml::{XmlRead, XmlWrite};
147//!
148//! #[derive(XmlWrite, XmlRead, PartialEq, Debug)]
149//! #[xml(tag = "parent")]
150//! struct Parent {
151//!     #[xml(attr = "attr")]
152//!     attr: usize
153//! }
154//!
155//! assert_eq!(
156//!     (Parent { attr: 42 }).to_string().unwrap(),
157//!     r#"<parent attr="42"/>"#
158//! );
159//!
160//! assert_eq!(
161//!     Parent::from_str(r#"<parent attr="48"></parent>"#).unwrap(),
162//!     Parent { attr: 48 }
163//! );
164//! ```
165//!
166//! ### `#[xml(attr = "", with = "")]`
167//!
168//! Parse and serialize the field with the module specified as an argument. That module must provide the following interface given that the fields is of type `T`.
169//!
170//! ```ignore
171//! mod some_module_name {
172//!     fn from_xml(mode: &str) -> hard_xml::XmlResult<T>;
173//!     fn to_xml(mode: &T) -> hard_xml::XmlResult<impl std::convert::AsRef<str>>;
174//! }
175//! ```
176//!
177//! #### Example
178//!
179//! ```rust
180//! # use hard_xml::{XmlRead, XmlWrite};
181//! #
182//! mod parse_drive_mode {
183//!     pub fn from_xml(mode: &str) -> hard_xml::XmlResult<bool> {
184//!         match mode {
185//!             "agile" => Ok(false),
186//!             "fast" => Ok(true),
187//!             _ => Err(hard_xml::XmlError::FromStr(
188//!                 format!("Expected mode to be agile or fast, got {mode:?}").into())),
189//!         }
190//!     }
191//!
192//!     pub fn to_xml(mode: &bool) -> hard_xml::XmlResult<&'static str> {
193//!         match mode {
194//!             false => Ok("agile"),
195//!             true => Ok("fast"),
196//!         }
197//!     }
198//! }
199//!
200//! #[derive(XmlRead, XmlWrite, PartialEq, Debug)]
201//! #[xml(tag = "spaceship")]
202//! struct Spaceship {
203//!     #[xml(attr = "drive_mode", with = "parse_drive_mode")]
204//!     drive_mode: bool,
205//! }
206//!
207//! assert_eq!(
208//!     Spaceship::from_str(r#"<spaceship drive_mode="agile"/>"#).unwrap(),
209//!     Spaceship {
210//!         drive_mode: false,
211//!     }
212//! );
213//!
214//! // match with a reversed string in from_xml of withmod.
215//! assert_eq!(
216//!     Spaceship {
217//!         drive_mode: true,
218//!     }.to_string().unwrap(),
219//!     r#"<spaceship drive_mode="fast"/>"#,
220//! );
221//! ```
222//!
223//! ### `#[xml(attr = "", duplicate = "last")]`
224//!
225//! When reading invalid XML use the last instance of any duplicated attribute instead of raising a parse error.
226//!
227//! **Use of this attribute is strongly discouraged as it could create a security vulnerability.** If two applications process invalid XML and choose to use different fields this difference could cause bugs including security exploits. This attribute is only provided for compatibility with strong-xml and hard-xml before 1.39.0. If you need this option you should plan to move off of it as soon as possible. This option will be removed in hard-xml v2.
228//!
229//! ### `#[xml(child = "")]`
230//!
231//! Specifies that a struct field is a child element. Support
232//! `T`, `Option<T>`, `Vec<T>` where `T: XmlRead + XmlWrite`.
233//!
234//! ```rust
235//! use hard_xml::{XmlRead, XmlWrite};
236//!
237//! #[derive(XmlWrite, XmlRead, PartialEq, Debug)]
238//! #[xml(tag = "tag1")]
239//! struct Tag1 {}
240//!
241//! #[derive(XmlWrite, XmlRead, PartialEq, Debug)]
242//! #[xml(tag = "tag2")]
243//! struct Tag2 {}
244//!
245//! #[derive(XmlWrite, XmlRead, PartialEq, Debug)]
246//! #[xml(tag = "tag3")]
247//! struct Tag3 {}
248//!
249//! #[derive(XmlWrite, XmlRead, PartialEq, Debug)]
250//! enum Tag12 {
251//!     #[xml(tag = "tag1")]
252//!     Tag1(Tag1),
253//!     #[xml(tag = "tag2")]
254//!     Tag2(Tag2),
255//! }
256//!
257//! #[derive(XmlWrite, XmlRead, PartialEq, Debug)]
258//! #[xml(tag = "parent")]
259//! struct Parent {
260//!     #[xml(child = "tag3")]
261//!     tag3: Vec<Tag3>,
262//!     #[xml(child = "tag1", child = "tag2")]
263//!     tag12: Option<Tag12>
264//! }
265//!
266//! assert_eq!(
267//!     (Parent { tag3: vec![Tag3 {}], tag12: None }).to_string().unwrap(),
268//!     r#"<parent><tag3/></parent>"#
269//! );
270//!
271//! assert_eq!(
272//!     Parent::from_str(r#"<parent><tag2></tag2></parent>"#).unwrap(),
273//!     Parent { tag3: vec![], tag12: Some(Tag12::Tag2(Tag2 {})) }
274//! );
275//! ```
276//!
277//! ### `#[xml(text)]`
278//!
279//! Specifies that a struct field is text content.
280//! Support `Cow<str>`, `Vec<Cow<str>>`, `Option<Cow<str>>`,
281//! `T`, `Vec<T>`, `Option<T>` where `T: FromStr + Display`.
282//!
283//! ```rust
284//! use std::borrow::Cow;
285//! use hard_xml::{XmlRead, XmlWrite};
286//!
287//! #[derive(XmlWrite, XmlRead, PartialEq, Debug)]
288//! #[xml(tag = "parent")]
289//! struct Parent<'a> {
290//!     #[xml(text)]
291//!     content: Cow<'a, str>,
292//! }
293//!
294//! assert_eq!(
295//!     (Parent { content: "content".into() }).to_string().unwrap(),
296//!     r#"<parent>content</parent>"#
297//! );
298//!
299//! assert_eq!(
300//!     Parent::from_str(r#"<parent></parent>"#).unwrap(),
301//!     Parent { content: "".into() }
302//! );
303//! ```
304//!
305//! ### `#[xml(flatten_text = "")]`
306//!
307//! Specifies that a struct field is child text element.
308//! Support `Cow<str>`, `Vec<Cow<str>>`, `Option<Cow<str>>`,
309//! `T`, `Vec<T>`, `Option<T>` where `T: FromStr + Display`.
310//!
311//! ```rust
312//! use std::borrow::Cow;
313//! use hard_xml::{XmlRead, XmlWrite};
314//!
315//! #[derive(XmlWrite, XmlRead, PartialEq, Debug)]
316//! #[xml(tag = "parent")]
317//! struct Parent<'a> {
318//!     #[xml(flatten_text = "child")]
319//!     content: Cow<'a, str>,
320//! }
321//!
322//! assert_eq!(
323//!     (Parent { content: "content".into() }).to_string().unwrap(),
324//!     r#"<parent><child>content</child></parent>"#
325//! );
326//!
327//! assert_eq!(
328//!     Parent::from_str(r#"<parent><child></child></parent>"#).unwrap(),
329//!     Parent { content: "".into() }
330//! );
331//! ```
332//!
333//! ### `#[xml(cdata)]`
334//!
335//! Specifies a CDATA text. Should be used together with `text` or `flatten_text`.
336//!
337//! > `#[xml(cdata)]` only changes the behavior of writing,
338//! > text field without `#[xml(cdata)]` can still works with cdata tag.
339//!
340//! ```rust
341//! use std::borrow::Cow;
342//! use hard_xml::{XmlRead, XmlWrite};
343//!
344//! #[derive(XmlWrite, XmlRead, PartialEq, Debug)]
345//! #[xml(tag = "parent")]
346//! struct Parent<'a> {
347//!     #[xml(text, cdata)]
348//!     content: Cow<'a, str>,
349//! }
350//!
351//! assert_eq!(
352//!     (Parent { content: "content".into() }).to_string().unwrap(),
353//!     r#"<parent><![CDATA[content]]></parent>"#
354//! );
355//! ```
356//!
357//! ```rust
358//! use std::borrow::Cow;
359//! use hard_xml::{XmlRead, XmlWrite};
360//!
361//! #[derive(XmlWrite, XmlRead, PartialEq, Debug)]
362//! #[xml(tag = "parent")]
363//! struct Parent<'a> {
364//!     #[xml(flatten_text = "code", cdata)]
365//!     content: Cow<'a, str>,
366//! }
367//!
368//! assert_eq!(
369//!     (Parent { content: r#"hello("deities!");"#.into() }).to_string().unwrap(),
370//!     r#"<parent><code><![CDATA[hello("deities!");]]></code></parent>"#
371//! );
372//! ```
373//!
374//! ### `#[xml(default)]`
375//!
376//! Use `Default::default()` if the value is not present when reading.
377//!
378//! ```rust
379//! use std::borrow::Cow;
380//! use hard_xml::XmlRead;
381//!
382//! #[derive(XmlRead, PartialEq, Debug)]
383//! #[xml(tag = "root")]
384//! struct Root {
385//!     #[xml(default, attr = "attr")]
386//!     attr: bool,
387//! }
388//!
389//! assert_eq!(
390//!     Root::from_str(r#"<root/>"#).unwrap(),
391//!     Root { attr: false }
392//! );
393//!
394//! assert_eq!(
395//!     Root::from_str(r#"<root attr="1"/>"#).unwrap(),
396//!     Root { attr: true }
397//! );
398//! ```
399//!
400//! ### `#[xml(duplicate = "error")]`
401//!
402//! Fail parsing if multiple instances of this field are encountered. This can be used for `child` and `flatten_text` fields. It has no effect when the type is a `Vec<_>` (as all values will be recorded).
403//!
404//! Using this option is **strongly recommended** as it can prevent bugs including security vulnerabilities when different systems use different values of a repeated child element.
405
406#[cfg(feature = "log")]
407mod log;
408#[cfg(not(feature = "log"))]
409mod noop_log;
410
411#[cfg(feature = "log")]
412#[doc(hidden)]
413pub mod lib {
414    pub use log;
415}
416
417mod xml_error;
418mod xml_escape;
419mod xml_extended_error;
420mod xml_read;
421mod xml_reader;
422mod xml_unescape;
423mod xml_write;
424mod xml_writer;
425
426pub use self::xml_error::{XmlError, XmlResult};
427pub use self::xml_extended_error::XmlExtendedError;
428pub use self::xml_read::{XmlRead, XmlReadOwned};
429pub use self::xml_reader::XmlReader;
430pub use self::xml_write::XmlWrite;
431pub use self::xml_writer::XmlWriter;
432
433pub use hard_xml_derive::{XmlRead, XmlWrite};
434
435pub use xmlparser;
436
437pub mod utils {
438    pub use super::xml_escape::xml_escape;
439    pub use super::xml_unescape::xml_unescape;
440}