diff --git a/CHANGELOG.md b/CHANGELOG.md index eddfd62..dc5b6b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Implement `std::error::Error` for `XmlError`. Mark helper traits as `pub(crate)` to prevent their accidental leakage to public API [`#66`](https://github.com/rust-syndication/atom/pull/66) - Bump MSRV (Minimum Supported Rust Version) from 1.40.0 to 1.54.0 [`#66`](https://github.com/rust-syndication/atom/pull/66) and [`#69`](https://github.com/rust-syndication/atom/pull/69) - Upgrade `quick_xml` to `0.27` and `derive_builder` to `0.12` [`#67`](https://github.com/rust-syndication/atom/pull/67) +- Allow to configure emitted XML [`#70`](https://github.com/rust-syndication/atom/pull/70) ## 0.11.0 - 2021-10-20 diff --git a/src/feed.rs b/src/feed.rs index f9fea92..36eadff 100644 --- a/src/feed.rs +++ b/src/feed.rs @@ -22,6 +22,24 @@ use crate::util::{ atom_datetime, atom_text, attr_value, decode, default_fixed_datetime, skip, FixedDateTime, }; +/// Various options which control XML writer +#[derive(Clone, Copy)] +pub struct WriteConfig { + /// Write XML document declaration at the beginning of a document. Default is `true`. + pub write_document_declaration: bool, + /// Indent XML tags. Default is `None`. + pub indent_size: Option, +} + +impl Default for WriteConfig { + fn default() -> Self { + Self { + write_document_declaration: true, + indent_size: None, + } + } +} + /// Represents an Atom feed #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] #[derive(Debug, Clone, PartialEq)] @@ -116,28 +134,77 @@ impl Feed { Err(Error::Eof) } - /// Attempt to write this Atom feed to a writer. + /// Attempt to write this Atom feed to a writer using default `WriteConfig`. /// /// # Examples /// - /// ```no_run + /// ``` /// use std::io::BufReader; /// use std::fs::File; /// use atom_syndication::Feed; /// - /// let file = File::open("example.xml").unwrap(); - /// let feed = Feed::read_from(BufReader::new(file)).unwrap(); - /// let out = File::create("out.xml").unwrap(); - /// feed.write_to(out).unwrap(); + /// # fn main() -> Result<(), Box> { + /// let feed = Feed { + /// title: "Feed Title".into(), + /// id: "Feed ID".into(), + /// ..Default::default() + /// }; + /// + /// let out = feed.write_to(Vec::new())?; + /// assert_eq!(&out, br#" + /// Feed TitleFeed ID1970-01-01T00:00:00+00:00"#); + /// # Ok(()) } /// ``` pub fn write_to(&self, writer: W) -> Result { - let mut writer = Writer::new(writer); - writer - .write_event(Event::Decl(BytesDecl::new("1.0", None, None))) - .map_err(XmlError::new)?; - writer - .write_event(Event::Text(BytesText::new("\n"))) - .map_err(XmlError::new)?; + self.write_with_config(writer, WriteConfig::default()) + } + + /// Attempt to write this Atom feed to a writer. + /// + /// # Examples + /// + /// ``` + /// use std::io::BufReader; + /// use std::fs::File; + /// use atom_syndication::{Feed, WriteConfig}; + /// + /// # fn main() -> Result<(), Box> { + /// let feed = Feed { + /// title: "Feed Title".into(), + /// id: "Feed ID".into(), + /// ..Default::default() + /// }; + /// + /// let mut out = Vec::new(); + /// let config = WriteConfig { + /// write_document_declaration: false, + /// indent_size: Some(2), + /// }; + /// feed.write_with_config(&mut out, config)?; + /// assert_eq!(&out, br#" + /// Feed Title + /// Feed ID + /// 1970-01-01T00:00:00+00:00 + /// "#); + /// # Ok(()) } + /// ``` + pub fn write_with_config( + &self, + writer: W, + write_config: WriteConfig, + ) -> Result { + let mut writer = match write_config.indent_size { + Some(indent_size) => Writer::new_with_indent(writer, b' ', indent_size), + None => Writer::new(writer), + }; + if write_config.write_document_declaration { + writer + .write_event(Event::Decl(BytesDecl::new("1.0", None, None))) + .map_err(XmlError::new)?; + writer + .write_event(Event::Text(BytesText::from_escaped("\n"))) + .map_err(XmlError::new)?; + } self.to_xml(&mut writer)?; Ok(writer.into_inner()) } @@ -906,4 +973,67 @@ mod test { assert_eq!(loaded_feed.base(), Some("http://example.com/blog/")); assert_eq!(loaded_feed.lang(), Some("fr_FR")); } + + #[test] + fn test_write_no_decl() { + let feed = Feed::default(); + let xml = feed + .write_with_config( + Vec::new(), + WriteConfig { + write_document_declaration: false, + indent_size: None, + }, + ) + .unwrap(); + assert_eq!( + String::from_utf8_lossy(&xml), + r#"1970-01-01T00:00:00+00:00"# + ); + } + + #[test] + fn test_write_indented() { + let feed = Feed::default(); + let xml = feed + .write_with_config( + Vec::new(), + WriteConfig { + write_document_declaration: true, + indent_size: Some(4), + }, + ) + .unwrap(); + assert_eq!( + String::from_utf8_lossy(&xml), + r#" + + + + 1970-01-01T00:00:00+00:00 +"# + ); + } + + #[test] + fn test_write_no_decl_indented() { + let feed = Feed::default(); + let xml = feed + .write_with_config( + Vec::new(), + WriteConfig { + write_document_declaration: false, + indent_size: Some(4), + }, + ) + .unwrap(); + assert_eq!( + String::from_utf8_lossy(&xml), + r#" + + + 1970-01-01T00:00:00+00:00 +"# + ); + } } diff --git a/src/lib.rs b/src/lib.rs index 075e5f5..313b9ad 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -83,6 +83,7 @@ pub use crate::error::Error; pub use crate::feed::Feed; #[cfg(feature = "builders")] pub use crate::feed::FeedBuilder; +pub use crate::feed::WriteConfig; pub use crate::generator::Generator; #[cfg(feature = "builders")] pub use crate::generator::GeneratorBuilder;