From 189f1a5ec489fa8f17d3590fbebdba3f051fedc5 Mon Sep 17 00:00:00 2001 From: Andrey Kutejko Date: Sun, 12 Sep 2021 00:58:07 +0200 Subject: [PATCH] Add base and lang fields to a Feed --- CHANGELOG.md | 1 + src/feed.rs | 104 ++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 91 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d4214aa..05a32e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Escape Content's value unless it contains xhtml [`#52`](https://github.com/rust-syndication/atom/pull/52) - Preserve entities and open tags (e.g `&`, `
`) in xhtml content [`#53`](https://github.com/rust-syndication/atom/pull/53) +- Add support of xml:base and xml:land in a Feed [`#55`](https://github.com/rust-syndication/atom/pull/55) ## 0.10.0 - 2021-06-06 diff --git a/src/feed.rs b/src/feed.rs index c0b9855..a1be1be 100644 --- a/src/feed.rs +++ b/src/feed.rs @@ -71,6 +71,10 @@ pub struct Feed { /// The namespaces present in the feed tag. #[cfg_attr(feature = "builders", builder(setter(each = "namespace")))] pub namespaces: BTreeMap, + /// Base URL for resolving any relative references found in the element. + pub base: Option, + /// Indicates the natural language for the element. + pub lang: Option, } impl Feed { @@ -96,19 +100,7 @@ impl Feed { match reader.read_event(&mut buf)? { Event::Start(element) => { if element.name() == b"feed" { - let mut feed = Feed::from_xml(&mut reader, element.attributes())?; - - for attr in element.attributes().with_checks(false).flatten() { - if !attr.key.starts_with(b"xmlns:") || attr.key == b"xmlns:dc" { - continue; - } - - let key = str::from_utf8(&attr.key[6..])?.to_string(); - let value = attr.unescape_and_decode_value(&reader)?; - feed.namespaces.insert(key, value); - } - - return Ok(feed); + return Feed::from_xml(&mut reader, element.attributes()); } else { return Err(Error::InvalidStartTag); } @@ -648,13 +640,56 @@ impl Feed { { self.namespaces = namespaces.into() } + + /// Return base URL of the feed. + pub fn base(&self) -> Option<&str> { + self.base.as_deref() + } + + /// Set base URL of the feed. + pub fn set_base(&mut self, base: V) + where + V: Into>, + { + self.base = base.into(); + } + + /// Return natural language of the feed. + pub fn lang(&self) -> Option<&str> { + self.lang.as_deref() + } + + /// Set the base URL of the feed. + pub fn set_lang(&mut self, lang: V) + where + V: Into>, + { + self.lang = lang.into(); + } } impl FromXml for Feed { - fn from_xml(reader: &mut Reader, _: Attributes<'_>) -> Result { + fn from_xml( + reader: &mut Reader, + mut atts: Attributes<'_>, + ) -> Result { let mut feed = Feed::default(); let mut buf = Vec::new(); + for attr in atts.with_checks(false).flatten() { + match attr.key { + b"xml:base" => feed.base = Some(attr.unescape_and_decode_value(reader)?), + b"xml:lang" => feed.lang = Some(attr.unescape_and_decode_value(reader)?), + b"xmlns:dc" => {} + attr_key if attr_key.starts_with(b"xmlns:") => { + let ns = str::from_utf8(&attr_key[6..])?.to_string(); + let ns_url = attr.unescape_and_decode_value(reader)?; + feed.namespaces.insert(ns, ns_url); + } + _ => {} + } + } + loop { match reader.read_event(&mut buf)? { Event::Start(element) => match element.name() { @@ -723,6 +758,14 @@ impl ToXml for Feed { element.push_attribute((format!("xmlns:{}", ns).as_bytes(), uri.as_bytes())); } + if let Some(ref base) = self.base { + element.push_attribute(("xml:base", base.as_str())); + } + + if let Some(ref lang) = self.lang { + element.push_attribute(("xml:lang", lang.as_str())); + } + writer.write_event(Event::Start(element))?; writer.write_object_named(&self.title, b"title")?; writer.write_text_element(b"id", &*self.id)?; @@ -801,6 +844,8 @@ impl Default for Feed { entries: Vec::new(), extensions: ExtensionMap::default(), namespaces: BTreeMap::default(), + base: None, + lang: None, } } } @@ -812,3 +857,34 @@ impl FeedBuilder { self.build_impl().unwrap() } } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_default() { + let feed = Feed::default(); + let xml_fragment = r#" +1970-01-01T00:00:00+00:00"#; + assert_eq!(feed.to_string(), xml_fragment); + let loaded_feed = Feed::read_from(xml_fragment.as_bytes()).unwrap(); + assert_eq!(loaded_feed, feed); + assert_eq!(loaded_feed.base(), None); + assert_eq!(loaded_feed.lang(), None); + } + + #[test] + fn test_base_and_lang() { + let mut feed = Feed::default(); + feed.set_base(Some("http://example.com/blog/".into())); + feed.set_lang(Some("fr_FR".into())); + let xml_fragment = r#" +1970-01-01T00:00:00+00:00"#; + assert_eq!(feed.to_string(), xml_fragment); + let loaded_feed = Feed::read_from(xml_fragment.as_bytes()).unwrap(); + assert_eq!(loaded_feed, feed); + assert_eq!(loaded_feed.base(), Some("http://example.com/blog/")); + assert_eq!(loaded_feed.lang(), Some("fr_FR")); + } +}