Repository: tafia/quick-xml
Pull request #416 (2022-07-10 20:03:29)

Add `<event>::borrow()` and some minor stuff
This PR makes some refactoring that I do on my path to the implementing namespaces support.

Currently I need to fix lifetime issues and I plan split reader and writer events into separate structs, which requires some preparing work. In order to not make that PR too big I made this PR with this work.

I fixed some intradoc links, move tests to appropriate place and introduced two improvements:
- `BytesStart` and `BytesEnd` structs now implements `From<QName>` that allows to create them easely (useful for writing)
- `Writer::write_event` now consumes event using an `Into` trait. `AsRef` trait, used before, does not allow to accept two unrelated structs which `reader::Event` and `writer::Event` will be. On the contrary, `writer::Event` will implement `From<reader::Event>` which would allow to write reader events as before. In order to not lose the ownership, the `Event::borrow()` method is introduced, as a counterpart to the `Event::into_owned()` method, so `Writer::write_event` could consume only borrowed event


Patch:
diff --git a/Changelog.md b/Changelog.md
index 91dee4f..f8ff447 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -25,6 +25,8 @@
 - [#395]: Add support for XML Schema `xs:list`
 - [#324]: `Reader::from_str` / `Deserializer::from_str` / `from_str` now ignore
   the XML declared encoding and always use UTF-8
+- [#416]: Add `borrow()` methods in all event structs which allows to get
+  a borrowed version of any event
 
 ### Bug Fixes
 
@@ -108,6 +110,9 @@
   |`read_to_end_unbuffered` |`read_to_end`
 - [#412]: Change `read_to_end*` and `read_text_into` to accept `QName` instead of `AsRef<[u8]>`
 
+- [#416]: `BytesStart::to_borrowed` renamed to `BytesStart::borrow`, the same method
+  added to all events
+
 ### New Tests
 
 - [#9]: Added tests for incorrect nested tags in input
@@ -131,6 +136,7 @@
 [#403]: https://github.com/tafia/quick-xml/pull/403
 [#407]: https://github.com/tafia/quick-xml/pull/407
 [#412]: https://github.com/tafia/quick-xml/pull/412
+[#416]: https://github.com/tafia/quick-xml/pull/416
 
 ## 0.23.0 -- 2022-05-08
 
diff --git a/compare/benches/bench.rs b/compare/benches/bench.rs
index 4dfb5a4..0a14fb4 100644
--- a/compare/benches/bench.rs
+++ b/compare/benches/bench.rs
@@ -25,7 +25,10 @@ fn low_level_comparison(c: &mut Criterion) {
                 }
                 buf.clear();
             }
-            assert_eq!(count, 1550, "Overall tag count in ./tests/documents/sample_rss.xml");
+            assert_eq!(
+                count, 1550,
+                "Overall tag count in ./tests/documents/sample_rss.xml"
+            );
         })
     });
 
@@ -49,7 +52,10 @@ fn low_level_comparison(c: &mut Criterion) {
                 }
                 input = &input[consumed..];
             }
-            assert_eq!(count, 1550, "Overall tag count in ./tests/documents/sample_rss.xml");
+            assert_eq!(
+                count, 1550,
+                "Overall tag count in ./tests/documents/sample_rss.xml"
+            );
         })
     });
 
@@ -68,7 +74,10 @@ fn low_level_comparison(c: &mut Criterion) {
                     _ => (),
                 }
             }
-            assert_eq!(count, 1550, "Overall tag count in ./tests/documents/sample_rss.xml");
+            assert_eq!(
+                count, 1550,
+                "Overall tag count in ./tests/documents/sample_rss.xml"
+            );
         })
     });
 
@@ -83,7 +92,10 @@ fn low_level_comparison(c: &mut Criterion) {
                     _ => (),
                 }
             }
-            assert_eq!(count, 1550, "Overall tag count in ./tests/documents/sample_rss.xml");
+            assert_eq!(
+                count, 1550,
+                "Overall tag count in ./tests/documents/sample_rss.xml"
+            );
         })
     });
 
@@ -101,7 +113,10 @@ fn low_level_comparison(c: &mut Criterion) {
                     _ => (),
                 }
             }
-            assert_eq!(count, 1550, "Overall tag count in ./tests/documents/sample_rss.xml");
+            assert_eq!(
+                count, 1550,
+                "Overall tag count in ./tests/documents/sample_rss.xml"
+            );
         })
     });
 
@@ -166,7 +181,10 @@ fn low_level_comparison(c: &mut Criterion) {
                     count += 1;
                 }
             }
-            assert_eq!(count, 1550, "Overall tag count in ./tests/documents/sample_rss.xml");
+            assert_eq!(
+                count, 1550,
+                "Overall tag count in ./tests/documents/sample_rss.xml"
+            );
         })
     });
     group.finish();
diff --git a/src/de/map.rs b/src/de/map.rs
index efe98ab..beecaae 100644
--- a/src/de/map.rs
+++ b/src/de/map.rs
@@ -517,7 +517,7 @@ where
     forward!(deserialize_any);
     forward!(deserialize_ignored_any);
 
-    /// Tuple representation is the same as [sequences](#method.deserialize_seq).
+    /// Tuple representation is the same as [sequences](Self::deserialize_seq).
     fn deserialize_tuple<V>(self, _len: usize, visitor: V) -> Result<V::Value, DeError>
     where
         V: Visitor<'de>,
@@ -525,7 +525,7 @@ where
         self.deserialize_seq(visitor)
     }
 
-    /// Named tuple representation is the same as [unnamed tuples](#method.deserialize_tuple).
+    /// Named tuple representation is the same as [unnamed tuples](Self::deserialize_tuple).
     fn deserialize_tuple_struct<V>(
         self,
         _name: &'static str,
@@ -687,7 +687,7 @@ where
     forward!(deserialize_any);
     forward!(deserialize_ignored_any);
 
-    /// Representation of tuples the same as [sequences](#method.deserialize_seq).
+    /// Representation of tuples the same as [sequences](Self::deserialize_seq).
     fn deserialize_tuple<V>(self, _len: usize, visitor: V) -> Result<V::Value, DeError>
     where
         V: Visitor<'de>,
@@ -695,7 +695,7 @@ where
         self.deserialize_seq(visitor)
     }
 
-    /// Representation of named tuples the same as [unnamed tuples](#method.deserialize_tuple).
+    /// Representation of named tuples the same as [unnamed tuples](Self::deserialize_tuple).
     fn deserialize_tuple_struct<V>(
         self,
         _name: &'static str,
diff --git a/src/errors.rs b/src/errors.rs
index c1af08a..8d00763 100644
--- a/src/errors.rs
+++ b/src/errors.rs
@@ -79,8 +79,6 @@ impl From<AttrError> for Error {
 }
 
 /// A specialized `Result` type where the error is hard-wired to [`Error`].
-///
-/// [`Error`]: enum.Error.html
 pub type Result<T> = std::result::Result<T, Error>;
 
 impl std::fmt::Display for Error {
diff --git a/src/events/attributes.rs b/src/events/attributes.rs
index 51f1455..4910527 100644
--- a/src/events/attributes.rs
+++ b/src/events/attributes.rs
@@ -17,15 +17,13 @@ use std::{borrow::Cow, collections::HashMap, ops::Range};
 /// want to access the value using one of the [`unescaped_value`] and [`unescape_and_decode_value`]
 /// functions.
 ///
-/// [`unescaped_value`]: #method.unescaped_value
-/// [`unescape_and_decode_value`]: #method.unescape_and_decode_value
+/// [`unescaped_value`]: Self::unescaped_value
+/// [`unescape_and_decode_value`]: Self::unescape_and_decode_value
 #[derive(Clone, PartialEq)]
 pub struct Attribute<'a> {
     /// The key to uniquely define the attribute.
     ///
     /// If [`Attributes::with_checks`] is turned off, the key might not be unique.
-    ///
-    /// [`Attributes::with_checks`]: struct.Attributes.html#method.with_checks
     pub key: QName<'a>,
     /// The raw value of the attribute.
     pub value: Cow<'a, [u8]>,
@@ -39,7 +37,7 @@ impl<'a> Attribute<'a> {
     ///
     /// This will allocate if the value contains any escape sequences.
     ///
-    /// See also [`unescaped_value_with_custom_entities()`](#method.unescaped_value_with_custom_entities)
+    /// See also [`unescaped_value_with_custom_entities()`](Self::unescaped_value_with_custom_entities)
     pub fn unescaped_value(&self) -> XmlResult<Cow<[u8]>> {
         self.make_unescaped_value(None)
     }
@@ -52,7 +50,7 @@ impl<'a> Attribute<'a> {
     ///
     /// This will allocate if the value contains any escape sequences.
     ///
-    /// See also [`unescaped_value()`](#method.unescaped_value)
+    /// See also [`unescaped_value()`](Self::unescaped_value)
     ///
     /// # Pre-condition
     ///
@@ -76,11 +74,11 @@ impl<'a> Attribute<'a> {
     /// This allocates a `String` in all cases. For performance reasons it might be a better idea to
     /// instead use one of:
     ///
-    /// * [`Reader::decode()`], as it only allocates when the decoding can't be performed otherwise.
+    /// * [`Reader::decoder().decode()`], as it only allocates when the decoding can't be performed otherwise.
     /// * [`unescaped_value()`], as it doesn't allocate when no escape sequences are used.
     ///
-    /// [`unescaped_value()`]: #method.unescaped_value
-    /// [`Reader::decode()`]: ../../reader/struct.Reader.html#method.decode
+    /// [`unescaped_value()`]: Self::unescaped_value
+    /// [`Reader::decoder().decode()`]: crate::reader::Decoder::decode
     pub fn unescape_and_decode_value<B>(&self, reader: &Reader<B>) -> XmlResult<String> {
         self.do_unescape_and_decode_value(reader, None)
     }
@@ -90,11 +88,11 @@ impl<'a> Attribute<'a> {
     /// This allocates a `String` in all cases. For performance reasons it might be a better idea to
     /// instead use one of:
     ///
-    /// * [`Reader::decode()`], as it only allocates when the decoding can't be performed otherwise.
+    /// * [`Reader::decoder().decode()`], as it only allocates when the decoding can't be performed otherwise.
     /// * [`unescaped_value_with_custom_entities()`], as it doesn't allocate when no escape sequences are used.
     ///
-    /// [`unescaped_value_with_custom_entities()`]: #method.unescaped_value_with_custom_entities
-    /// [`Reader::decode()`]: ../../reader/struct.Reader.html#method.decode
+    /// [`unescaped_value_with_custom_entities()`]: Self::unescaped_value_with_custom_entities
+    /// [`Reader::decoder().decode()`]: crate::reader::Decoder::decode
     ///
     /// # Pre-condition
     ///
@@ -189,7 +187,7 @@ impl<'a> From<Attr<&'a [u8]>> for Attribute<'a> {
 /// Yields `Result<Attribute>`. An `Err` will be yielded if an attribute is malformed or duplicated.
 /// The duplicate check can be turned off by calling [`with_checks(false)`].
 ///
-/// [`with_checks(false)`]: #method.with_checks
+/// [`with_checks(false)`]: Self::with_checks
 #[derive(Clone, Debug)]
 pub struct Attributes<'a> {
     /// slice of `Element` corresponding to attributes
diff --git a/src/events/mod.rs b/src/events/mod.rs
index 51c66ec..24d9f6f 100644
--- a/src/events/mod.rs
+++ b/src/events/mod.rs
@@ -34,7 +34,7 @@
 
 pub mod attributes;
 
-#[cfg(feature = "encoding_rs")]
+#[cfg(feature = "encoding")]
 use encoding_rs::Encoding;
 use std::borrow::Cow;
 use std::collections::HashMap;
@@ -75,6 +75,14 @@ impl<'a> BytesStartText<'a> {
         self.content.into_inner()
     }
 
+    /// Converts the event into a borrowed event.
+    #[inline]
+    pub fn borrow(&self) -> BytesStartText {
+        BytesStartText {
+            content: self.content.borrow(),
+        }
+    }
+
     /// Decodes bytes of event, stripping byte order mark (BOM) if it is presented
     /// in the event.
     ///
@@ -108,13 +116,12 @@ impl<'a> From<BytesText<'a>> for BytesStartText<'a> {
 ///
 /// `<name attr="value">`.
 ///
-/// The name can be accessed using the [`name`], [`local_name`] or [`unescaped`] methods. An
-/// iterator over the attributes is returned by the [`attributes`] method.
+/// The name can be accessed using the [`name`] or [`local_name`] methods.
+/// An iterator over the attributes is returned by the [`attributes`] method.
 ///
-/// [`name`]: #method.name
-/// [`local_name`]: #method.local_name
-/// [`unescaped`]: #method.unescaped
-/// [`attributes`]: #method.attributes
+/// [`name`]: Self::name
+/// [`local_name`]: Self::local_name
+/// [`attributes`]: Self::attributes
 #[derive(Clone, Eq, PartialEq)]
 pub struct BytesStart<'a> {
     /// content of the element, before any utf8 conversion
@@ -196,15 +203,15 @@ impl<'a> BytesStart<'a> {
     /// # fn example(&self) -> Result<(), Error> {
     /// # let mut writer = Writer::new(Vec::new());
     ///
-    /// writer.write_event(Event::Start(self.attrs.to_borrowed()))?;
+    /// writer.write_event(Event::Start(self.attrs.borrow()))?;
     /// // ...
     /// writer.write_event(Event::End(self.attrs.to_end()))?;
     /// # Ok(())
     /// # }}
     /// ```
     ///
-    /// [`to_end`]: #method.to_end
-    pub fn to_borrowed(&self) -> BytesStart {
+    /// [`to_end`]: Self::to_end
+    pub fn borrow(&self) -> BytesStart {
         BytesStart::borrowed(&self.buf, self.name_len)
     }
 
@@ -344,13 +351,13 @@ impl<'a> Deref for BytesStart<'a> {
 /// [W3C XML 1.1 Prolog and Document Type Declaration](http://w3.org/TR/xml11/#sec-prolog-dtd)
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub struct BytesDecl<'a> {
-    element: BytesStart<'a>,
+    content: BytesStart<'a>,
 }
 
 impl<'a> BytesDecl<'a> {
     /// Creates a `BytesDecl` from a `BytesStart`
-    pub fn from_start(start: BytesStart<'a>) -> BytesDecl<'a> {
-        BytesDecl { element: start }
+    pub fn from_start(start: BytesStart<'a>) -> Self {
+        Self { content: start }
     }
 
     /// Gets xml version, excluding quotes (`'` or `"`).
@@ -408,7 +415,7 @@ impl<'a> BytesDecl<'a> {
     /// [grammar]: https://www.w3.org/TR/xml11/#NT-XMLDecl
     pub fn version(&self) -> Result<Cow<[u8]>> {
         // The version *must* be the first thing in the declaration.
-        match self.element.attributes().with_checks(false).next() {
+        match self.content.attributes().with_checks(false).next() {
             Some(Ok(a)) if a.key.as_ref() == b"version" => Ok(a.value),
             // first attribute was not "version"
             Some(Ok(a)) => {
@@ -458,7 +465,7 @@ impl<'a> BytesDecl<'a> {
     ///
     /// [grammar]: https://www.w3.org/TR/xml11/#NT-XMLDecl
     pub fn encoding(&self) -> Option<Result<Cow<[u8]>>> {
-        self.element
+        self.content
             .try_get_attribute("encoding")
             .map(|a| a.map(|a| a.value))
             .transpose()
@@ -500,7 +507,7 @@ impl<'a> BytesDecl<'a> {
     ///
     /// [grammar]: https://www.w3.org/TR/xml11/#NT-XMLDecl
     pub fn standalone(&self) -> Option<Result<Cow<[u8]>>> {
-        self.element
+        self.content
             .try_get_attribute("standalone")
             .map(|a| a.map(|a| a.value))
             .transpose()
@@ -549,12 +556,12 @@ impl<'a> BytesDecl<'a> {
         buf.push(b'"');
 
         BytesDecl {
-            element: BytesStart::owned(buf, 3),
+            content: BytesStart::owned(buf, 3),
         }
     }
 
     /// Gets the decoder struct
-    #[cfg(feature = "encoding_rs")]
+    #[cfg(feature = "encoding")]
     pub fn encoder(&self) -> Option<&'static Encoding> {
         self.encoding()
             .and_then(|e| e.ok())
@@ -564,7 +571,15 @@ impl<'a> BytesDecl<'a> {
     /// Converts the event into an owned event.
     pub fn into_owned(self) -> BytesDecl<'static> {
         BytesDecl {
-            element: self.element.into_owned(),
+            content: self.content.into_owned(),
+        }
+    }
+
+    /// Converts the event into a borrowed event.
+    #[inline]
+    pub fn borrow(&self) -> BytesDecl {
+        BytesDecl {
+            content: self.content.borrow(),
         }
     }
 }
@@ -573,7 +588,7 @@ impl<'a> Deref for BytesDecl<'a> {
     type Target = [u8];
 
     fn deref(&self) -> &[u8] {
-        &*self.element
+        &*self.content
     }
 }
 
@@ -609,6 +624,14 @@ impl<'a> BytesEnd<'a> {
         }
     }
 
+    /// Converts the event into a borrowed event.
+    #[inline]
+    pub fn borrow(&self) -> BytesEnd {
+        BytesEnd {
+            name: Cow::Borrowed(&self.name),
+        }
+    }
+
     /// Gets the undecoded raw tag name, as present in the input stream.
     #[inline]
     pub fn name(&self) -> QName {
@@ -647,7 +670,9 @@ impl<'a> Deref for BytesEnd<'a> {
 /// in escaped form. Internally data is stored in escaped form
 #[derive(Clone, Eq, PartialEq)]
 pub struct BytesText<'a> {
-    // Invariant: The content is always escaped.
+    /// Escaped then encoded content of the event. Content is encoded in the XML
+    /// document encoding when event comes from the reader and should be in the
+    /// document encoding when event passed to the writer
     content: Cow<'a, [u8]>,
 }
 
@@ -700,6 +725,14 @@ impl<'a> BytesText<'a> {
         self.content
     }
 
+    /// Converts the event into a borrowed event.
+    #[inline]
+    pub fn borrow(&self) -> BytesText {
+        BytesText {
+            content: Cow::Borrowed(&self.content),
+        }
+    }
+
     /// Returns unescaped version of the text content, that can be written
     /// as CDATA in XML
     #[cfg(feature = "serialize")]
@@ -718,7 +751,7 @@ impl<'a> BytesText<'a> {
     /// Searches for '&' into content and try to escape the coded character if possible
     /// returns Malformed error with index within element if '&' is not followed by ';'
     ///
-    /// See also [`unescaped_with_custom_entities()`](#method.unescaped_with_custom_entities)
+    /// See also [`unescaped_with_custom_entities()`](Self::unescaped_with_custom_entities)
     pub fn unescaped(&self) -> Result<Cow<[u8]>> {
         self.make_unescaped(None)
     }
@@ -733,7 +766,7 @@ impl<'a> BytesText<'a> {
     ///
     /// The keys and values of `custom_entities`, if any, must be valid UTF-8.
     ///
-    /// See also [`unescaped()`](#method.unescaped)
+    /// See also [`unescaped()`](Self::unescaped)
     pub fn unescaped_with_custom_entities<'s>(
         &'s self,
         custom_entities: &HashMap<Vec<u8>, Vec<u8>>,
@@ -854,6 +887,14 @@ impl<'a> BytesCData<'a> {
         self.content
     }
 
+    /// Converts the event into a borrowed event.
+    #[inline]
+    pub fn borrow(&self) -> BytesCData {
+        BytesCData {
+            content: Cow::Borrowed(&self.content),
+        }
+    }
+
     /// Converts this CDATA content to an escaped version, that can be written
     /// as an usual text in XML.
     ///
@@ -1016,6 +1057,24 @@ impl<'a> Event<'a> {
             Event::Eof => Event::Eof,
         }
     }
+
+    /// Converts the event into a borrowed event.
+    #[inline]
+    pub fn borrow(&self) -> Event {
+        match self {
+            Event::StartText(e) => Event::StartText(e.borrow()),
+            Event::Start(e) => Event::Start(e.borrow()),
+            Event::End(e) => Event::End(e.borrow()),
+            Event::Empty(e) => Event::Empty(e.borrow()),
+            Event::Text(e) => Event::Text(e.borrow()),
+            Event::Comment(e) => Event::Comment(e.borrow()),
+            Event::CData(e) => Event::CData(e.borrow()),
+            Event::Decl(e) => Event::Decl(e.borrow()),
+            Event::PI(e) => Event::PI(e.borrow()),
+            Event::DocType(e) => Event::DocType(e.borrow()),
+            Event::Eof => Event::Eof,
+        }
+    }
 }
 
 impl<'a> Deref for Event<'a> {
@@ -1050,47 +1109,6 @@ mod test {
     use super::*;
     use pretty_assertions::assert_eq;
 
-    #[test]
-    fn local_name() {
-        use std::str::from_utf8;
-        let xml = r#"
-            <foo:bus attr='bar'>foobusbar</foo:bus>
-            <foo: attr='bar'>foobusbar</foo:>
-            <:foo attr='bar'>foobusbar</:foo>
-            <foo:bus:baz attr='bar'>foobusbar</foo:bus:baz>
-            "#;
-        let mut rdr = Reader::from_str(xml);
-        let mut buf = Vec::new();
-        let mut parsed_local_names = Vec::new();
-        loop {
-            match rdr
-                .read_event_into(&mut buf)
-                .expect("unable to read xml event")
-            {
-                Event::Start(ref e) => parsed_local_names.push(
-                    from_utf8(e.local_name().as_ref())
-                        .expect("unable to build str from local_name")
-                        .to_string(),
-                ),
-                Event::End(ref e) => parsed_local_names.push(
-                    from_utf8(e.local_name().as_ref())
-                        .expect("unable to build str from local_name")
-                        .to_string(),
-                ),
-                Event::Eof => break,
-                _ => {}
-            }
-        }
-        assert_eq!(parsed_local_names[0], "bus".to_string());
-        assert_eq!(parsed_local_names[1], "bus".to_string());
-        assert_eq!(parsed_local_names[2], "".to_string());
-        assert_eq!(parsed_local_names[3], "".to_string());
-        assert_eq!(parsed_local_names[4], "foo".to_string());
-        assert_eq!(parsed_local_names[5], "foo".to_string());
-        assert_eq!(parsed_local_names[6], "bus:baz".to_string());
-        assert_eq!(parsed_local_names[7], "bus:baz".to_string());
-    }
-
     #[test]
     fn bytestart_create() {
         let b = BytesStart::owned_name("test");
diff --git a/src/name.rs b/src/name.rs
index 83c80d0..b3edf3a 100644
--- a/src/name.rs
+++ b/src/name.rs
@@ -797,4 +797,31 @@ mod namespaces {
         );
         assert_eq!(resolver.find(name, &buffer), Unknown(b"unknown".to_vec()));
     }
+
+    /// Checks how the QName is decomposed to a prefix and a local name
+    #[test]
+    fn prefix_and_local_name() {
+        let name = QName(b"foo:bus");
+        assert_eq!(name.prefix(), Some(Prefix(b"foo")));
+        assert_eq!(name.local_name(), LocalName(b"bus"));
+        assert_eq!(name.decompose(), (LocalName(b"bus"), Some(Prefix(b"foo"))));
+
+        let name = QName(b"foo:");
+        assert_eq!(name.prefix(), Some(Prefix(b"foo")));
+        assert_eq!(name.local_name(), LocalName(b""));
+        assert_eq!(name.decompose(), (LocalName(b""), Some(Prefix(b"foo"))));
+
+        let name = QName(b":foo");
+        assert_eq!(name.prefix(), Some(Prefix(b"")));
+        assert_eq!(name.local_name(), LocalName(b"foo"));
+        assert_eq!(name.decompose(), (LocalName(b"foo"), Some(Prefix(b""))));
+
+        let name = QName(b"foo:bus:baz");
+        assert_eq!(name.prefix(), Some(Prefix(b"foo")));
+        assert_eq!(name.local_name(), LocalName(b"bus:baz"));
+        assert_eq!(
+            name.decompose(),
+            (LocalName(b"bus:baz"), Some(Prefix(b"foo")))
+        );
+    }
 }
diff --git a/src/reader.rs b/src/reader.rs
index 12221eb..9a967fb 100644
--- a/src/reader.rs
+++ b/src/reader.rs
@@ -234,9 +234,9 @@ impl<R> Reader<R> {
     ///
     /// (`false` by default)
     ///
-    /// [`Empty`]: events/enum.Event.html#variant.Empty
-    /// [`Start`]: events/enum.Event.html#variant.Start
-    /// [`End`]: events/enum.Event.html#variant.End
+    /// [`Empty`]: Event::Empty
+    /// [`Start`]: Event::Start
+    /// [`End`]: Event::End
     pub fn expand_empty_elements(&mut self, val: bool) -> &mut Self {
         self.expand_empty_elements = val;
         self
@@ -249,7 +249,7 @@ impl<R> Reader<R> {
     ///
     /// (`false` by default)
     ///
-    /// [`Text`]: events/enum.Event.html#variant.Text
+    /// [`Text`]: Event::Text
     pub fn trim_text(&mut self, val: bool) -> &mut Self {
         self.trim_text_start = val;
         self.trim_text_end = val;
@@ -262,7 +262,7 @@ impl<R> Reader<R> {
     ///
     /// (`false` by default)
     ///
-    /// [`Text`]: events/enum.Event.html#variant.Text
+    /// [`Text`]: Event::Text
     pub fn trim_text_end(&mut self, val: bool) -> &mut Self {
         self.trim_text_end = val;
         self
@@ -278,7 +278,7 @@ impl<R> Reader<R> {
     ///
     /// (`true` by default)
     ///
-    /// [`End`]: events/enum.Event.html#variant.End
+    /// [`End`]: Event::End
     pub fn trim_markup_names_in_closing_tags(&mut self, val: bool) -> &mut Self {
         self.trim_markup_names_in_closing_tags = val;
         self
@@ -300,7 +300,7 @@ impl<R> Reader<R> {
     ///
     /// (`true` by default)
     ///
-    /// [`End`]: events/enum.Event.html#variant.End
+    /// [`End`]: Event::End
     pub fn check_end_names(&mut self, val: bool) -> &mut Self {
         self.check_end_names = val;
         self
@@ -315,7 +315,7 @@ impl<R> Reader<R> {
     ///
     /// (`false` by default)
     ///
-    /// [`Comment`]: events/enum.Event.html#variant.Comment
+    /// [`Comment`]: Event::Comment
     pub fn check_comments(&mut self, val: bool) -> &mut Self {
         self.check_comments = val;
         self
diff --git a/src/se/var.rs b/src/se/var.rs
index 46039de..4ce0ede 100644
--- a/src/se/var.rs
+++ b/src/se/var.rs
@@ -154,7 +154,7 @@ where
         } else {
             self.parent
                 .writer
-                .write_event(Event::Start(self.attrs.to_borrowed()))?;
+                .write_event(Event::Start(self.attrs.borrow()))?;
             self.parent.writer.write(&self.children)?;
             self.parent
                 .writer
diff --git a/src/writer.rs b/src/writer.rs
index 16e3dc8..7c5cf30 100644
--- a/src/writer.rs
+++ b/src/writer.rs
@@ -152,13 +152,15 @@ impl<W: Write> Writer<W> {
 
     /// Manually write a newline and indentation at the proper level.
     ///
-    /// This can be used when the heuristic to line break and indent after any [Event] apart
-    /// from [Text] fails such as when a [Start] occurs directly after [Text].
-    /// This method will do nothing if `Writer` was not constructed with `new_with_indent`.
+    /// This can be used when the heuristic to line break and indent after any
+    /// [`Event`] apart from [`Text`] fails such as when a [`Start`] occurs directly
+    /// after [`Text`].
     ///
-    /// [Event]: events/enum.Event.html
-    /// [Text]: events/enum.Event.html#variant.Text
-    /// [Start]: events/enum.Event.html#variant.Start
+    /// This method will do nothing if `Writer` was not constructed with [`new_with_indent`].
+    ///
+    /// [`Text`]: Event::Text
+    /// [`Start`]: Event::Start
+    /// [`new_with_indent`]: Self::new_with_indent
     pub fn write_indent(&mut self) -> Result<()> {
         if let Some(ref i) = self.indent {
             self.writer.write_all(b"\n").map_err(Error::Io)?;
@@ -171,7 +173,8 @@ impl<W: Write> Writer<W> {
 
     /// Provides a simple, high-level API for writing XML elements.
     ///
-    /// Returns an [ElementWriter] that simplifies setting attributes and writing content inside the element.
+    /// Returns an [`ElementWriter`] that simplifies setting attributes and writing
+    /// content inside the element.
     ///
     /// # Example
     ///
@@ -253,7 +256,7 @@ impl<'a, W: Write> ElementWriter<'a, W> {
     /// Write some text inside the current element.
     pub fn write_text_content(self, text: BytesText) -> Result<&'a mut Writer<W>> {
         self.writer
-            .write_event(Event::Start(self.start_tag.to_borrowed()))?;
+            .write_event(Event::Start(self.start_tag.borrow()))?;
         self.writer.write_event(Event::Text(text))?;
         self.writer
             .write_event(Event::End(self.start_tag.to_end()))?;
@@ -263,7 +266,7 @@ impl<'a, W: Write> ElementWriter<'a, W> {
     /// Write a CData event `<![CDATA[...]]>` inside the current element.
     pub fn write_cdata_content(self, text: BytesCData) -> Result<&'a mut Writer<W>> {
         self.writer
-            .write_event(Event::Start(self.start_tag.to_borrowed()))?;
+            .write_event(Event::Start(self.start_tag.borrow()))?;
         self.writer.write_event(Event::CData(text))?;
         self.writer
             .write_event(Event::End(self.start_tag.to_end()))?;
@@ -273,7 +276,7 @@ impl<'a, W: Write> ElementWriter<'a, W> {
     /// Write a processing instruction `<?...?>` inside the current element.
     pub fn write_pi_content(self, text: BytesText) -> Result<&'a mut Writer<W>> {
         self.writer
-            .write_event(Event::Start(self.start_tag.to_borrowed()))?;
+            .write_event(Event::Start(self.start_tag.borrow()))?;
         self.writer.write_event(Event::PI(text))?;
         self.writer
             .write_event(Event::End(self.start_tag.to_end()))?;
@@ -292,7 +295,7 @@ impl<'a, W: Write> ElementWriter<'a, W> {
         F: Fn(&mut Writer<W>) -> Result<()>,
     {
         self.writer
-            .write_event(Event::Start(self.start_tag.to_borrowed()))?;
+            .write_event(Event::Start(self.start_tag.borrow()))?;
         closure(&mut self.writer)?;
         self.writer
             .write_event(Event::End(self.start_tag.to_end()))?;


Test patch:
diff --git a/tests/test.rs b/tests/test.rs
index 36338cf..f5a91dc 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -95,7 +95,7 @@ fn test_comment_starting_with_gt() {
 }
 
 #[test]
-#[cfg(feature = "encoding_rs")]
+#[cfg(feature = "encoding")]
 fn test_koi8_r_encoding() {
     let src: &[u8] = include_bytes!("documents/opennews_all.rss");
     let mut r = Reader::from_reader(src as &[u8]);
@@ -122,7 +122,7 @@ fn fuzz_53() {
     let mut buf = vec![];
     loop {
         match reader.read_event_into(&mut buf) {
-            Ok(quick_xml::events::Event::Eof) | Err(..) => break,
+            Ok(Eof) | Err(..) => break,
             _ => buf.clear(),
         }
     }
@@ -138,7 +138,7 @@ fn test_issue94() {
     let mut buf = vec![];
     loop {
         match reader.read_event_into(&mut buf) {
-            Ok(quick_xml::events::Event::Eof) | Err(..) => break,
+            Ok(Eof) | Err(..) => break,
             _ => buf.clear(),
         }
         buf.clear();
diff --git a/tests/unit_tests.rs b/tests/unit_tests.rs
index 6971cfe..fc4cb98 100644
--- a/tests/unit_tests.rs
+++ b/tests/unit_tests.rs
@@ -3,9 +3,10 @@ use std::io::Cursor;
 use std::str::from_utf8;
 
 use quick_xml::events::attributes::{AttrError, Attribute};
-use quick_xml::events::{BytesDecl, BytesEnd, BytesStart, BytesText, Event};
+use quick_xml::events::Event::*;
+use quick_xml::events::{BytesDecl, BytesEnd, BytesStart, BytesText};
 use quick_xml::name::QName;
-use quick_xml::{events::Event::*, Reader, Result, Writer};
+use quick_xml::{Reader, Result, Writer};
 
 use pretty_assertions::assert_eq;
 
@@ -621,7 +622,7 @@ fn test_read_write_roundtrip_escape() -> Result<()> {
             Text(e) => {
                 let t = e.escaped();
                 assert!(writer
-                    .write_event(Event::Text(BytesText::from_escaped(t.to_vec())))
+                    .write_event(Text(BytesText::from_escaped(t.to_vec())))
                     .is_ok());
             }
             e => assert!(writer.write_event(e).is_ok()),
@@ -654,7 +655,7 @@ fn test_read_write_roundtrip_escape_text() -> Result<()> {
             Text(e) => {
                 let t = e.unescape_and_decode(&reader).unwrap();
                 assert!(writer
-                    .write_event(Event::Text(BytesText::from_plain_str(&t)))
+                    .write_event(Text(BytesText::from_plain_str(&t)))
                     .is_ok());
             }
             e => assert!(writer.write_event(e).is_ok()),
@@ -799,10 +800,8 @@ mod decode_with_bom_removal {
 
         loop {
             match reader.read_event_into(&mut buf) {
-                Ok(Event::StartText(e)) => {
-                    txt.push(e.decode_with_bom_removal(reader.decoder()).unwrap())
-                }
-                Ok(Event::Eof) => break,
+                Ok(StartText(e)) => txt.push(e.decode_with_bom_removal(reader.decoder()).unwrap()),
+                Ok(Eof) => break,
                 _ => (),
             }
         }
@@ -825,10 +824,8 @@ mod decode_with_bom_removal {
 
         loop {
             match reader.read_event_into(&mut buf) {
-                Ok(Event::StartText(e)) => {
-                    txt.push(e.decode_with_bom_removal(reader.decoder()).unwrap())
-                }
-                Ok(Event::Eof) => break,
+                Ok(StartText(e)) => txt.push(e.decode_with_bom_removal(reader.decoder()).unwrap()),
+                Ok(Eof) => break,
                 _ => (),
             }
         }
@@ -846,10 +843,8 @@ mod decode_with_bom_removal {
 
         loop {
             match reader.read_event_into(&mut buf) {
-                Ok(Event::StartText(e)) => {
-                    txt.push(e.decode_with_bom_removal(reader.decoder()).unwrap())
-                }
-                Ok(Event::Eof) => break,
+                Ok(StartText(e)) => txt.push(e.decode_with_bom_removal(reader.decoder()).unwrap()),
+                Ok(Eof) => break,
                 _ => (),
             }
         }
@@ -869,10 +864,8 @@ mod decode_with_bom_removal {
 
         loop {
             match reader.read_event_into(&mut buf) {
-                Ok(Event::StartText(e)) => {
-                    txt.push(e.decode_with_bom_removal(reader.decoder()).unwrap())
-                }
-                Ok(Event::Eof) => break,
+                Ok(StartText(e)) => txt.push(e.decode_with_bom_removal(reader.decoder()).unwrap()),
+                Ok(Eof) => break,
                 _ => (),
             }
         }