Repository: tafia/quick-xml
Pull request #456 (2022-08-15 15:41:40)

Make reader and writer modules public, fix a small bug + polishing
In anticipation of release 0.24, some polishing, plus fix of a minor bug in `EndEventMismatch` error generation.

Also, make `reader` and `writer` modules public, because the content of those modules are tend to grow and reexport of everything under a crate root seems to me a bad idea. All examples was updated to use the new recommended way to consume `Reader`s and `Writer`s.

Other commits contains some polishing.

I not yet finished checking documentation, so probably there will be one more PR before release, the last, I hope.


Patch:
diff --git a/Cargo.toml b/Cargo.toml
index af05e66..8e2eecc 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -59,6 +59,9 @@ async-tokio = ["tokio"]
 ## [standard compliant]: https://www.w3.org/TR/xml11/#charencoding
 encoding = ["encoding_rs"]
 
+## Enables support for recognizing all [HTML 5 entities](https://dev.w3.org/html5/html-author/charref)
+escape-html = []
+
 ## This feature enables support for deserializing lists where tags are overlapped
 ## with tags that do not correspond to the list.
 ##
@@ -108,9 +111,6 @@ overlapped-lists = []
 ## Enables support for [`serde`] serialization and deserialization
 serialize = ["serde"]
 
-## Enables support for recognizing all [HTML 5 entities](https://dev.w3.org/html5/html-author/charref)
-escape-html = []
-
 [package.metadata.docs.rs]
 # document all features
 all-features = true
diff --git a/Changelog.md b/Changelog.md
index aa8e287..f11a770 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -65,6 +65,7 @@
   and a document encoding is not an UTF-8
 - [#434]: Fixed incorrect error generated in some cases by serde deserializer
 - [#445]: Use local name without namespace prefix when selecting enum variants based on element names
+  in a serde deserializer
 
 ### Misc Changes
 
@@ -181,11 +182,15 @@
   |`BytesText::from_plain_str(&str)`                 |_(as above)_
   |`BytesCData::new(impl Into<Cow<[u8]>>)`           |`BytesCData::new(impl Into<Cow<str>>)`
   |`BytesCData::from_str(&str)`                      |_(as above)_
+
 - [#440]: Removed `Deserializer::from_slice` and `quick_xml::de::from_slice` methods because deserializing from a byte
   array cannot guarantee borrowing due to possible copying while decoding.
 
 - [#455]: Removed `Reader::read_text_into` which is only not a better wrapper over match on `Event::Text`
 
+- [#456]: Reader and writer stuff grouped under `reader` and `writer` modules.
+  You still can use re-exported definitions from a crate root
+
 ### New Tests
 
 - [#9]: Added tests for incorrect nested tags in input
@@ -225,8 +230,10 @@
 [#439]: https://github.com/tafia/quick-xml/pull/439
 [#440]: https://github.com/tafia/quick-xml/pull/440
 [#443]: https://github.com/tafia/quick-xml/pull/443
+[#445]: https://github.com/tafia/quick-xml/pull/445
 [#450]: https://github.com/tafia/quick-xml/pull/450
 [#455]: https://github.com/tafia/quick-xml/pull/455
+[#456]: https://github.com/tafia/quick-xml/pull/456
 
 
 ## 0.23.0 -- 2022-05-08
diff --git a/README.md b/README.md
index 7b5d85f..fab801f 100644
--- a/README.md
+++ b/README.md
@@ -19,8 +19,8 @@ Syntax is inspired by [xml-rs](https://github.com/netvl/xml-rs).
 ### Reader
 
 ```rust
-use quick_xml::Reader;
 use quick_xml::events::Event;
+use quick_xml::reader::Reader;
 
 let xml = r#"<tag1 att1 = "test">
                 <tag2><!--Test comment-->Test</tag2>
@@ -66,7 +66,8 @@ loop {
 
 ```rust
 use quick_xml::events::{Event, BytesEnd, BytesStart};
-use quick_xml::{Reader, Writer};
+use quick_xml::reader::Reader;
+use quick_xml::writer::Writer;
 use std::io::Cursor;
 
 let xml = r#"<this_tag k1="v1" k2="v2"><child>text</child></this_tag>"#;
diff --git a/benches/macrobenches.rs b/benches/macrobenches.rs
index ebc348c..a8cbbd5 100644
--- a/benches/macrobenches.rs
+++ b/benches/macrobenches.rs
@@ -1,7 +1,7 @@
 use criterion::{self, criterion_group, criterion_main, Criterion, Throughput};
 use quick_xml::events::Event;
+use quick_xml::reader::{NsReader, Reader};
 use quick_xml::Result as XmlResult;
-use quick_xml::{NsReader, Reader};
 
 static RPM_PRIMARY: &str = include_str!("../tests/documents/rpm_primary.xml");
 static RPM_PRIMARY2: &str = include_str!("../tests/documents/rpm_primary2.xml");
diff --git a/benches/microbenches.rs b/benches/microbenches.rs
index c9ddd14..12ef056 100644
--- a/benches/microbenches.rs
+++ b/benches/microbenches.rs
@@ -3,7 +3,7 @@ use pretty_assertions::assert_eq;
 use quick_xml::escape::{escape, unescape};
 use quick_xml::events::Event;
 use quick_xml::name::QName;
-use quick_xml::{NsReader, Reader};
+use quick_xml::reader::{NsReader, Reader};
 
 static SAMPLE: &str = include_str!("../tests/documents/sample_rss.xml");
 static PLAYERS: &str = include_str!("../tests/documents/players.xml");
diff --git a/compare/benches/bench.rs b/compare/benches/bench.rs
index 7df5f02..10c7479 100644
--- a/compare/benches/bench.rs
+++ b/compare/benches/bench.rs
@@ -1,6 +1,7 @@
 use criterion::{self, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
 use pretty_assertions::assert_eq;
-use quick_xml::{self, events::Event, Reader};
+use quick_xml::events::Event;
+use quick_xml::reader::Reader;
 use serde::Deserialize;
 use serde_xml_rs;
 use xml::reader::{EventReader, XmlEvent};
diff --git a/examples/custom_entities.rs b/examples/custom_entities.rs
index 16f0348..a31e3f4 100644
--- a/examples/custom_entities.rs
+++ b/examples/custom_entities.rs
@@ -10,7 +10,7 @@
 use std::collections::HashMap;
 
 use quick_xml::events::Event;
-use quick_xml::Reader;
+use quick_xml::reader::Reader;
 use regex::bytes::Regex;
 
 const DATA: &str = r#"
diff --git a/examples/nested_readers.rs b/examples/nested_readers.rs
index 1e5b9c7..3b30776 100644
--- a/examples/nested_readers.rs
+++ b/examples/nested_readers.rs
@@ -1,6 +1,6 @@
 use pretty_assertions::assert_eq;
 use quick_xml::events::Event;
-use quick_xml::Reader;
+use quick_xml::reader::Reader;
 
 // a structure to capture the rows we've extracted
 // from a ECMA-376 table in document.xml
diff --git a/examples/read_buffered.rs b/examples/read_buffered.rs
index 25b28ee..16cb2c6 100644
--- a/examples/read_buffered.rs
+++ b/examples/read_buffered.rs
@@ -5,7 +5,7 @@
 
 fn main() -> Result<(), quick_xml::Error> {
     use quick_xml::events::Event;
-    use quick_xml::Reader;
+    use quick_xml::reader::Reader;
 
     let mut reader = Reader::from_file("tests/documents/document.xml")?;
     reader.trim_text(true);
diff --git a/examples/read_texts.rs b/examples/read_texts.rs
index 9a0dd24..c2d79f0 100644
--- a/examples/read_texts.rs
+++ b/examples/read_texts.rs
@@ -1,6 +1,6 @@
 fn main() {
     use quick_xml::events::Event;
-    use quick_xml::Reader;
+    use quick_xml::reader::Reader;
 
     let xml = "<tag1>text1</tag1><tag1>text2</tag1>\
                <tag1>text3</tag1><tag1><tag2>text4</tag2></tag1>";
diff --git a/fuzz/fuzz_targets/fuzz_target_1.rs b/fuzz/fuzz_targets/fuzz_target_1.rs
index 5035285..b7d0640 100644
--- a/fuzz/fuzz_targets/fuzz_target_1.rs
+++ b/fuzz/fuzz_targets/fuzz_target_1.rs
@@ -1,8 +1,8 @@
 #![no_main]
 #[macro_use] extern crate libfuzzer_sys;
 
-use quick_xml::Reader;
 use quick_xml::events::Event;
+use quick_xml::reader::Reader;
 use std::io::Cursor;
 
 fuzz_target!(|data: &[u8]| {
diff --git a/src/de/mod.rs b/src/de/mod.rs
index 8f65ff6..5c90cb9 100644
--- a/src/de/mod.rs
+++ b/src/de/mod.rs
@@ -219,7 +219,7 @@ use crate::{
     errors::Error,
     events::{BytesCData, BytesEnd, BytesStart, BytesText, Event},
     name::QName,
-    Reader,
+    reader::Reader,
 };
 use serde::de::{self, Deserialize, DeserializeOwned, Visitor};
 use std::borrow::Cow;
diff --git a/src/events/mod.rs b/src/events/mod.rs
index 2366d1f..a70f6a5 100644
--- a/src/events/mod.rs
+++ b/src/events/mod.rs
@@ -190,9 +190,10 @@ impl<'a> BytesStart<'a> {
     ///
     /// # Example
     ///
-    /// ```rust
-    /// # use quick_xml::{Error, Writer};
+    /// ```
     /// use quick_xml::events::{BytesStart, Event};
+    /// # use quick_xml::writer::Writer;
+    /// # use quick_xml::Error;
     ///
     /// struct SomeStruct<'a> {
     ///     attrs: BytesStart<'a>,
@@ -957,8 +958,8 @@ pub enum Event<'a> {
     /// ```
     /// # use pretty_assertions::assert_eq;
     /// use std::borrow::Cow;
-    /// use quick_xml::Reader;
     /// use quick_xml::events::Event;
+    /// use quick_xml::reader::Reader;
     ///
     /// // XML in UTF-8 with BOM
     /// let xml = b"\xEF\xBB\xBF<?xml version='1.0'?>".as_ref();
diff --git a/src/lib.rs b/src/lib.rs
index 14249d1..fde3f29 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -54,13 +54,13 @@ pub mod escape {
 }
 pub mod events;
 pub mod name;
-mod reader;
+pub mod reader;
 #[cfg(feature = "serialize")]
 pub mod se;
 /// Not an official API, public for integration tests
 #[doc(hidden)]
 pub mod utils;
-mod writer;
+pub mod writer;
 
 // reexports
 pub use crate::encoding::Decoder;
diff --git a/src/reader/async_tokio.rs b/src/reader/async_tokio.rs
index 26ec9ed..07719ed 100644
--- a/src/reader/async_tokio.rs
+++ b/src/reader/async_tokio.rs
@@ -49,7 +49,7 @@ impl<R: AsyncBufRead + Unpin> Reader<R> {
     /// # tokio_test::block_on(async {
     /// # use pretty_assertions::assert_eq;
     /// use quick_xml::events::Event;
-    /// use quick_xml::Reader;
+    /// use quick_xml::reader::Reader;
     ///
     /// // This explicitly uses `from_reader("...".as_bytes())` to use a buffered
     /// // reader instead of relying on the zero-copy optimizations for reading
@@ -106,7 +106,7 @@ impl<R: AsyncBufRead + Unpin> Reader<R> {
     /// # tokio_test::block_on(async {
     /// # use pretty_assertions::assert_eq;
     /// use quick_xml::events::{BytesStart, Event};
-    /// use quick_xml::Reader;
+    /// use quick_xml::reader::Reader;
     ///
     /// let mut reader = Reader::from_reader(r#"
     ///     <outer>
@@ -185,7 +185,7 @@ impl<R: AsyncBufRead + Unpin> NsReader<R> {
     /// # use pretty_assertions::assert_eq;
     /// use quick_xml::events::Event;
     /// use quick_xml::name::{Namespace, ResolveResult::*};
-    /// use quick_xml::NsReader;
+    /// use quick_xml::reader::NsReader;
     ///
     /// let mut reader = NsReader::from_reader(r#"
     ///     <x:tag1 xmlns:x="www.xxxx" xmlns:y="www.yyyy" att1 = "test">
@@ -248,7 +248,7 @@ impl<R: AsyncBufRead + Unpin> NsReader<R> {
     /// # use pretty_assertions::assert_eq;
     /// use quick_xml::name::{Namespace, ResolveResult};
     /// use quick_xml::events::{BytesStart, Event};
-    /// use quick_xml::NsReader;
+    /// use quick_xml::reader::NsReader;
     ///
     /// let mut reader = NsReader::from_reader(r#"
     ///     <outer xmlns="namespace 1">
@@ -320,7 +320,7 @@ impl<R: AsyncBufRead + Unpin> NsReader<R> {
     /// # use pretty_assertions::assert_eq;
     /// use quick_xml::events::Event;
     /// use quick_xml::name::{Namespace, QName, ResolveResult::*};
-    /// use quick_xml::NsReader;
+    /// use quick_xml::reader::NsReader;
     ///
     /// let mut reader = NsReader::from_reader(r#"
     ///     <x:tag1 xmlns:x="www.xxxx" xmlns:y="www.yyyy" att1 = "test">
diff --git a/src/reader/buffered_reader.rs b/src/reader/buffered_reader.rs
index ca3c209..54da39b 100644
--- a/src/reader/buffered_reader.rs
+++ b/src/reader/buffered_reader.rs
@@ -159,8 +159,6 @@ macro_rules! impl_buffered_source {
             }
         }
 
-        /// Consume and discard all the whitespace until the next non-whitespace
-        /// character or EOF.
         $($async)? fn skip_whitespace(&mut self, position: &mut usize) -> Result<()> {
             loop {
                 break match self $(.$reader)? .fill_buf() $(.$await)? {
@@ -180,8 +178,6 @@ macro_rules! impl_buffered_source {
             }
         }
 
-        /// Consume and discard one character if it matches the given byte. Return
-        /// true if it matched.
         $($async)? fn skip_one(&mut self, byte: u8, position: &mut usize) -> Result<bool> {
             // search byte must be within the ascii range
             debug_assert!(byte.is_ascii());
@@ -196,8 +192,6 @@ macro_rules! impl_buffered_source {
             }
         }
 
-        /// Return one character without consuming it, so that future `read_*` calls
-        /// will still include it. On EOF, return None.
         $($async)? fn peek_one(&mut self) -> Result<Option<u8>> {
             loop {
                 break match self $(.$reader)? .fill_buf() $(.$await)? {
@@ -243,8 +237,8 @@ impl<R: BufRead> Reader<R> {
     /// # Examples
     ///
     /// ```
-    /// use quick_xml::Reader;
     /// use quick_xml::events::Event;
+    /// use quick_xml::reader::Reader;
     ///
     /// let xml = r#"<tag1 att1 = "test">
     ///                 <tag2><!--Test comment-->Test</tag2>
@@ -323,7 +317,7 @@ impl<R: BufRead> Reader<R> {
     /// ```
     /// # use pretty_assertions::assert_eq;
     /// use quick_xml::events::{BytesStart, Event};
-    /// use quick_xml::Reader;
+    /// use quick_xml::reader::Reader;
     ///
     /// let mut reader = Reader::from_str(r#"
     ///     <outer>
diff --git a/src/reader/mod.rs b/src/reader/mod.rs
index 29b5596..0c8ee79 100644
--- a/src/reader/mod.rs
+++ b/src/reader/mod.rs
@@ -1,4 +1,4 @@
-//! A module to handle `Reader`
+//! Contains high-level interface for a pull-based XML parser.
 
 #[cfg(feature = "encoding")]
 use encoding_rs::Encoding;
@@ -305,7 +305,7 @@ enum ParseState {
     /// State after seeing the `<` symbol. Depending on the next symbol all other
     /// events (except `StartText`) could be generated.
     ///
-    /// After generating ane event the reader moves to the `ClosedTag` state.
+    /// After generating one event the reader moves to the `ClosedTag` state.
     OpenedTag,
     /// State in which reader searches the `<` symbol of a markup. All bytes before
     /// that symbol will be returned in the [`Event::Text`] event. After that
@@ -380,8 +380,8 @@ impl EncodingRef {
 /// # Examples
 ///
 /// ```
-/// use quick_xml::Reader;
 /// use quick_xml::events::Event;
+/// use quick_xml::reader::Reader;
 ///
 /// let xml = r#"<tag1 att1 = "test">
 ///                 <tag2><!--Test comment-->Test</tag2>
@@ -456,8 +456,8 @@ impl<R> Reader<R> {
     /// ```
     /// # use pretty_assertions::assert_eq;
     /// use std::{str, io::Cursor};
-    /// use quick_xml::Reader;
     /// use quick_xml::events::Event;
+    /// use quick_xml::reader::Reader;
     ///
     /// let xml = r#"<tag1 att1 = "test">
     ///                 <tag2><!--Test comment-->Test</tag2>
@@ -667,10 +667,22 @@ trait XmlSource<'r, B> {
     /// [events]: crate::events::Event
     fn read_element(&mut self, buf: B, position: &mut usize) -> Result<Option<&'r [u8]>>;
 
+    /// Consume and discard all the whitespace until the next non-whitespace
+    /// character or EOF.
+    ///
+    /// # Parameters
+    /// - `position`: Will be increased by amount of bytes consumed
     fn skip_whitespace(&mut self, position: &mut usize) -> Result<()>;
 
+    /// Consume and discard one character if it matches the given byte. Return
+    /// `true` if it matched.
+    ///
+    /// # Parameters
+    /// - `position`: Will be increased by 1 if byte is matched
     fn skip_one(&mut self, byte: u8, position: &mut usize) -> Result<bool>;
 
+    /// Return one character without consuming it, so that future `read_*` calls
+    /// will still include it. On EOF, return `None`.
     fn peek_one(&mut self) -> Result<Option<u8>>;
 }
 
@@ -1496,11 +1508,11 @@ mod test {
 
             mod issue_344 {
                 use crate::errors::Error;
+                use crate::reader::Reader;
 
                 #[$test]
                 $($async)? fn cdata() {
-                    let doc = "![]]>";
-                    let mut reader = crate::Reader::from_str(doc);
+                    let mut reader = Reader::from_str("![]]>");
 
                     match reader.$read_until_close($buf) $(.$await)? {
                         Err(Error::UnexpectedEof(s)) if s == "CData" => {}
@@ -1514,8 +1526,7 @@ mod test {
 
                 #[$test]
                 $($async)? fn comment() {
-                    let doc = "!- -->";
-                    let mut reader = crate::Reader::from_str(doc);
+                    let mut reader = Reader::from_str("!- -->");
 
                     match reader.$read_until_close($buf) $(.$await)? {
                         Err(Error::UnexpectedEof(s)) if s == "Comment" => {}
@@ -1529,8 +1540,7 @@ mod test {
 
                 #[$test]
                 $($async)? fn doctype_uppercase() {
-                    let doc = "!D>";
-                    let mut reader = crate::Reader::from_str(doc);
+                    let mut reader = Reader::from_str("!D>");
 
                     match reader.$read_until_close($buf) $(.$await)? {
                         Err(Error::UnexpectedEof(s)) if s == "DOCTYPE" => {}
@@ -1544,8 +1554,7 @@ mod test {
 
                 #[$test]
                 $($async)? fn doctype_lowercase() {
-                    let doc = "!d>";
-                    let mut reader = crate::Reader::from_str(doc);
+                    let mut reader = Reader::from_str("!d>");
 
                     match reader.$read_until_close($buf) $(.$await)? {
                         Err(Error::UnexpectedEof(s)) if s == "DOCTYPE" => {}
diff --git a/src/reader/ns_reader.rs b/src/reader/ns_reader.rs
index 6ad5cab..8eba75a 100644
--- a/src/reader/ns_reader.rs
+++ b/src/reader/ns_reader.rs
@@ -205,7 +205,7 @@ impl<R> NsReader<R> {
     /// # use pretty_assertions::assert_eq;
     /// use quick_xml::events::Event;
     /// use quick_xml::name::{Namespace, QName, ResolveResult::*};
-    /// use quick_xml::NsReader;
+    /// use quick_xml::reader::NsReader;
     ///
     /// let mut reader = NsReader::from_str("<tag xmlns='root namespace'/>");
     ///
@@ -258,7 +258,7 @@ impl<R> NsReader<R> {
     /// use quick_xml::events::Event;
     /// use quick_xml::events::attributes::Attribute;
     /// use quick_xml::name::{Namespace, QName, ResolveResult::*};
-    /// use quick_xml::NsReader;
+    /// use quick_xml::reader::NsReader;
     ///
     /// let mut reader = NsReader::from_str("
     ///     <tag one='1'
@@ -312,9 +312,9 @@ impl<R: BufRead> NsReader<R> {
     ///
     /// ```
     /// # use pretty_assertions::assert_eq;
-    /// use quick_xml::NsReader;
     /// use quick_xml::events::Event;
     /// use quick_xml::name::{Namespace, ResolveResult::*};
+    /// use quick_xml::reader::NsReader;
     ///
     /// let mut reader = NsReader::from_str(r#"
     ///     <x:tag1 xmlns:x="www.xxxx" xmlns:y="www.yyyy" att1 = "test">
@@ -370,9 +370,9 @@ impl<R: BufRead> NsReader<R> {
     ///
     /// ```
     /// # use pretty_assertions::assert_eq;
-    /// use quick_xml::NsReader;
     /// use quick_xml::events::Event;
     /// use quick_xml::name::{Namespace, QName, ResolveResult::*};
+    /// use quick_xml::reader::NsReader;
     ///
     /// let mut reader = NsReader::from_str(r#"
     ///     <x:tag1 xmlns:x="www.xxxx" xmlns:y="www.yyyy" att1 = "test">
@@ -467,7 +467,7 @@ impl<R: BufRead> NsReader<R> {
     /// # use pretty_assertions::assert_eq;
     /// use quick_xml::events::{BytesStart, Event};
     /// use quick_xml::name::{Namespace, ResolveResult};
-    /// use quick_xml::NsReader;
+    /// use quick_xml::reader::NsReader;
     ///
     /// let mut reader = NsReader::from_str(r#"
     ///     <outer xmlns="namespace 1">
@@ -552,9 +552,9 @@ impl<'i> NsReader<&'i [u8]> {
     ///
     /// ```
     /// # use pretty_assertions::assert_eq;
-    /// use quick_xml::NsReader;
     /// use quick_xml::events::Event;
     /// use quick_xml::name::{Namespace, ResolveResult::*};
+    /// use quick_xml::reader::NsReader;
     ///
     /// let mut reader = NsReader::from_str(r#"
     ///     <x:tag1 xmlns:x="www.xxxx" xmlns:y="www.yyyy" att1 = "test">
@@ -613,9 +613,9 @@ impl<'i> NsReader<&'i [u8]> {
     ///
     /// ```
     /// # use pretty_assertions::assert_eq;
-    /// use quick_xml::NsReader;
     /// use quick_xml::events::Event;
     /// use quick_xml::name::{Namespace, QName, ResolveResult::*};
+    /// use quick_xml::reader::NsReader;
     ///
     /// let mut reader = NsReader::from_str(r#"
     ///     <x:tag1 xmlns:x="www.xxxx" xmlns:y="www.yyyy" att1 = "test">
@@ -699,7 +699,7 @@ impl<'i> NsReader<&'i [u8]> {
     /// # use pretty_assertions::assert_eq;
     /// use quick_xml::events::{BytesStart, Event};
     /// use quick_xml::name::{Namespace, ResolveResult};
-    /// use quick_xml::NsReader;
+    /// use quick_xml::reader::NsReader;
     ///
     /// let mut reader = NsReader::from_str(r#"
     ///     <outer xmlns="namespace 1">
@@ -779,7 +779,7 @@ impl<'i> NsReader<&'i [u8]> {
     /// # use pretty_assertions::assert_eq;
     /// # use std::borrow::Cow;
     /// use quick_xml::events::{BytesStart, Event};
-    /// use quick_xml::NsReader;
+    /// use quick_xml::reader::NsReader;
     ///
     /// let mut reader = NsReader::from_str(r#"
     ///     <html>
diff --git a/src/reader/parser.rs b/src/reader/parser.rs
index 16114ea..baeacda 100644
--- a/src/reader/parser.rs
+++ b/src/reader/parser.rs
@@ -1,5 +1,3 @@
-use std::str::from_utf8;
-
 #[cfg(feature = "encoding")]
 use encoding_rs::UTF_8;
 
@@ -84,7 +82,7 @@ impl Parser {
         }
 
         let content = if self.trim_text_end {
-            // Skip the ending '<
+            // Skip the ending '<'
             let len = bytes
                 .iter()
                 .rposition(|&b| !is_whitespace(b))
@@ -143,9 +141,8 @@ impl Parser {
         }
     }
 
-    /// reads `BytesElement` starting with a `/`,
-    /// if `self.check_end_names`, checks that element matches last opened element
-    /// return `End` event
+    /// Wraps content of `buf` into the [`Event::End`] event. Does the check that
+    /// end name matches the last opened start name if `self.check_end_names` is set.
     pub fn read_end<'b>(&mut self, buf: &'b [u8]) -> Result<Event<'b>> {
         // XML standard permits whitespaces after the markup name in closing tags.
         // Let's strip them from the buffer before comparing tag names.
@@ -160,24 +157,26 @@ impl Parser {
             &buf[1..]
         };
         if self.check_end_names {
-            let mismatch_err = |expected: &[u8], found: &[u8], offset: &mut usize| {
+            let decoder = self.decoder();
+            let mismatch_err = |expected: String, found: &[u8], offset: &mut usize| {
                 *offset -= buf.len();
                 Err(Error::EndEventMismatch {
-                    expected: from_utf8(expected).unwrap_or("").to_owned(),
-                    found: from_utf8(found).unwrap_or("").to_owned(),
+                    expected,
+                    found: decoder.decode(found).unwrap_or_default().into_owned(),
                 })
             };
             match self.opened_starts.pop() {
                 Some(start) => {
                     let expected = &self.opened_buffer[start..];
                     if name != expected {
+                        let expected = decoder.decode(expected).unwrap_or_default().into_owned();
                         mismatch_err(expected, name, &mut self.offset)
                     } else {
                         self.opened_buffer.truncate(start);
                         Ok(Event::End(BytesEnd::wrap(name.into())))
                     }
                 }
-                None => mismatch_err(b"", &buf[1..], &mut self.offset),
+                None => mismatch_err("".to_string(), &buf[1..], &mut self.offset),
             }
         } else {
             Ok(Event::End(BytesEnd::wrap(name.into())))
diff --git a/src/reader/slice_reader.rs b/src/reader/slice_reader.rs
index 4d9c80b..fbe3e31 100644
--- a/src/reader/slice_reader.rs
+++ b/src/reader/slice_reader.rs
@@ -45,7 +45,7 @@ impl<'a> Reader<&'a [u8]> {
     /// ```
     /// # use pretty_assertions::assert_eq;
     /// use quick_xml::events::Event;
-    /// use quick_xml::Reader;
+    /// use quick_xml::reader::Reader;
     ///
     /// let mut reader = Reader::from_str(r#"
     ///     <tag1 att1 = "test">
@@ -117,7 +117,7 @@ impl<'a> Reader<&'a [u8]> {
     /// ```
     /// # use pretty_assertions::assert_eq;
     /// use quick_xml::events::{BytesStart, Event};
-    /// use quick_xml::Reader;
+    /// use quick_xml::reader::Reader;
     ///
     /// let mut reader = Reader::from_str(r#"
     ///     <outer>
@@ -183,7 +183,7 @@ impl<'a> Reader<&'a [u8]> {
     /// # use pretty_assertions::assert_eq;
     /// # use std::borrow::Cow;
     /// use quick_xml::events::{BytesStart, Event};
-    /// use quick_xml::Reader;
+    /// use quick_xml::reader::Reader;
     ///
     /// let mut reader = Reader::from_str("
     ///     <html>
diff --git a/src/se/mod.rs b/src/se/mod.rs
index b05c988..2c9cd8e 100644
--- a/src/se/mod.rs
+++ b/src/se/mod.rs
@@ -49,11 +49,11 @@ impl<'r, W: Write> Serializer<'r, W> {
     ///
     /// When serializing a primitive type, only its representation will be written:
     ///
-    /// ```edition2018
+    /// ```
     /// # use pretty_assertions::assert_eq;
     /// # use serde::Serialize;
-    /// use quick_xml::Writer;
     /// # use quick_xml::se::Serializer;
+    /// use quick_xml::writer::Writer;
     ///
     /// let mut buffer = Vec::new();
     /// let mut writer = Writer::new_with_indent(&mut buffer, b' ', 2);
@@ -66,11 +66,11 @@ impl<'r, W: Write> Serializer<'r, W> {
     /// When serializing a struct, newtype struct, unit struct or tuple `root_tag`
     /// is used as tag name of root(s) element(s):
     ///
-    /// ```edition2018
+    /// ```
     /// # use pretty_assertions::assert_eq;
     /// # use serde::Serialize;
-    /// use quick_xml::Writer;
     /// use quick_xml::se::Serializer;
+    /// use quick_xml::writer::Writer;
     ///
     /// #[derive(Debug, PartialEq, Serialize)]
     /// struct Struct {
diff --git a/src/writer.rs b/src/writer.rs
index e42c231..af3f18c 100644
--- a/src/writer.rs
+++ b/src/writer.rs
@@ -1,4 +1,4 @@
-//! A module to handle `Writer`
+//! Contains high-level interface for an events-based XML emitter.
 
 use crate::errors::{Error, Result};
 use crate::events::{attributes::Attribute, BytesCData, BytesStart, BytesText, Event};
@@ -13,7 +13,8 @@ use std::io::Write;
 /// ```
 /// # use pretty_assertions::assert_eq;
 /// use quick_xml::events::{Event, BytesEnd, BytesStart};
-/// use quick_xml::{Reader, Writer};
+/// use quick_xml::reader::Reader;
+/// use quick_xml::writer::Writer;
 /// use std::io::Cursor;
 ///
 /// let xml = r#"<this_tag k1="v1" k2="v2"><child>text</child></this_tag>"#;
@@ -179,8 +180,9 @@ impl<W: Write> Writer<W> {
     /// ```rust
     /// # use quick_xml::Result;
     /// # fn main() -> Result<()> {
-    /// use quick_xml::{Error, Writer};
     /// use quick_xml::events::{BytesStart, BytesText, Event};
+    /// use quick_xml::writer::Writer;
+    /// use quick_xml::Error;
     /// use std::io::Cursor;
     ///
     /// let mut writer = Writer::new(Cursor::new(Vec::new()));


Test patch:
diff --git a/tests/async-tokio.rs b/tests/async-tokio.rs
index 4d81158..c378181 100644
--- a/tests/async-tokio.rs
+++ b/tests/async-tokio.rs
@@ -1,5 +1,5 @@
 use quick_xml::events::Event::*;
-use quick_xml::Reader;
+use quick_xml::reader::Reader;
 
 #[tokio::test]
 async fn test_sample() {
diff --git a/tests/namespaces.rs b/tests/namespaces.rs
index 41f594f..58f8c67 100644
--- a/tests/namespaces.rs
+++ b/tests/namespaces.rs
@@ -3,7 +3,7 @@ use quick_xml::events::attributes::Attribute;
 use quick_xml::events::Event::*;
 use quick_xml::name::ResolveResult::*;
 use quick_xml::name::{Namespace, QName};
-use quick_xml::NsReader;
+use quick_xml::reader::NsReader;
 use std::borrow::Cow;
 
 #[test]
diff --git a/tests/test.rs b/tests/test.rs
index 5f80465..34d9c9c 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -1,5 +1,8 @@
+use quick_xml::events::attributes::Attribute;
+use quick_xml::events::Event::*;
 use quick_xml::name::QName;
-use quick_xml::{events::attributes::Attribute, events::Event::*, Error, Reader};
+use quick_xml::reader::Reader;
+use quick_xml::Error;
 use std::{borrow::Cow, io::Cursor};
 
 #[cfg(feature = "serialize")]
diff --git a/tests/unit_tests.rs b/tests/unit_tests.rs
index d7da1fd..e98328d 100644
--- a/tests/unit_tests.rs
+++ b/tests/unit_tests.rs
@@ -6,7 +6,9 @@ use quick_xml::events::attributes::{AttrError, Attribute};
 use quick_xml::events::Event::*;
 use quick_xml::events::{BytesDecl, BytesEnd, BytesStart, BytesText};
 use quick_xml::name::QName;
-use quick_xml::{Reader, Result, Writer};
+use quick_xml::reader::Reader;
+use quick_xml::writer::Writer;
+use quick_xml::Result;
 
 use pretty_assertions::assert_eq;
 
diff --git a/tests/xmlrs_reader_tests.rs b/tests/xmlrs_reader_tests.rs
index b466a8b..0bffc68 100644
--- a/tests/xmlrs_reader_tests.rs
+++ b/tests/xmlrs_reader_tests.rs
@@ -1,7 +1,8 @@
+use quick_xml::encoding::Decoder;
 use quick_xml::escape::unescape;
 use quick_xml::events::{BytesStart, Event};
 use quick_xml::name::{QName, ResolveResult};
-use quick_xml::{Decoder, NsReader};
+use quick_xml::reader::NsReader;
 use std::str::from_utf8;
 
 #[test]