Repository: duesee/imap-codec (language: python)

Issue:
refactor: Just re-export `imap-types`.

`#![deny(missing_docs)`



Discussion / hints:

Fix patch:
diff --git a/.github/workflows/api.yml b/.github/workflows/api.yml
index 64255e43..75e2b34d 100644
--- a/.github/workflows/api.yml
+++ b/.github/workflows/api.yml
@@ -36,4 +36,6 @@ jobs:
           cargo semver-checks check-release
 
       - name: Check for SemVer violations | imap-codec
-        run: cargo semver-checks check-release
+        run: |
+          cd imap-codec
+          cargo semver-checks check-release
diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml
index 8086113d..05899e74 100644
--- a/.github/workflows/audit.yml
+++ b/.github/workflows/audit.yml
@@ -33,4 +33,4 @@ jobs:
 
     steps:
       - uses: actions/checkout@v2
-      - uses: EmbarkStudios/cargo-deny-action@7257a18a9c2fe3f92b85d41ae473520dff953c97
+      - uses: EmbarkStudios/cargo-deny-action@e0a440755b184aa50374330fa75cca0f84fcb59a
diff --git a/Cargo.toml b/Cargo.toml
index 6e889e08..0008a28a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -5,9 +5,7 @@ members = [
     "imap-codec/fuzz",
     "imap-types",
     "imap-types/fuzz",
+    "assets/demos/tokio-support",
     "assets/demos/tokio-client",
     "assets/demos/tokio-server",
 ]
-
-[patch.crates-io]
-imap-types = { path = "imap-types" }
diff --git a/README.md b/README.md
index 2fbe6312..d548d2e9 100644
--- a/README.md
+++ b/README.md
@@ -3,16 +3,18 @@
 [![Coverage](https://coveralls.io/repos/github/duesee/imap-codec/badge.svg?branch=main)](https://coveralls.io/github/duesee/imap-codec?branch=main)
 [![Documentation](https://docs.rs/imap-codec/badge.svg)](https://docs.rs/imap-codec)
 
-# imap-codec
+# imap-{codec,types}
 
-This library provides parsing, serialization, and general support for [IMAP4rev1] implementations.
-It is based on [imap-types] and aims to become a rock-solid and [well-documented] building block for IMAP client and server implementations in Rust.
-The complete [formal syntax] of IMAP4rev1 and several IMAP [extensions] are implemented.
+This workspace contains [imap-codec] and [imap-types], two [rock-solid] and [well-documented] crates to build [IMAP4rev1] clients and servers.
+imap-codec provides parsing and serialization, and is based on imap-types.
+imap-types provides misuse-resistant types, constructors, and general support for IMAP implementations.
+The crates live here together, but imap-types is a perfectly fine standalone crate.
 
 Let's talk on [Matrix]!
 
 ## Features
 
+* Complete [formal syntax] of IMAP4rev1 is implemented. Furthermore, several IMAP [extensions] are supported.
 * Correctness and misuse-resistance are enforced on the type level. It's not possible to construct a message that violates the IMAP specification.
 * Messages automatically use the most efficient representation. For example, atoms are preferred over quoted strings, and quoted strings are preferred over literals. It's equally easy to manually choose a representation.
 * Parsing works in streaming mode. `Incomplete` is returned when there is insufficient data to make a final decision. No message will be truncated.
@@ -24,7 +26,7 @@ Let's talk on [Matrix]!
 ```rust
 use imap_codec::{
     codec::{Decode, Encode},
-    command::Command,
+    imap_types::command::Command,
 };
 
 fn main() {
@@ -198,12 +200,14 @@ Thanks to the [NLnet Foundation](https://nlnet.nl/) for supporting imap-codec th
     <img height="100px" src="https://user-images.githubusercontent.com/8997731/215262235-0db02da9-7c6c-498e-a3d2-7ea7901637bf.png"/>
 </div>
 
+[rock-solid]: https://github.com/duesee/imap-codec/tree/main/imap-codec/fuzz
+[well-documented]: https://docs.rs/imap-codec/latest/imap_codec/
+[imap-codec]: imap-codec
+[imap-types]: imap-types
 [Matrix]: https://matrix.to/#/#imap-codec:matrix.org
 [IMAP4rev1]: https://tools.ietf.org/html/rfc3501
-[imap-types]: https://github.com/duesee/imap-codec/imap-types
 [formal syntax]: https://tools.ietf.org/html/rfc3501#section-9
 [extensions]: https://docs.rs/imap-codec/latest/imap_codec/#features
-[well-documented]: https://docs.rs/imap-codec/latest/imap_codec/
 [cargo fuzz]: https://github.com/rust-fuzz/cargo-fuzz
 [demo client]: https://github.com/duesee/imap-codec/tree/main/assets/demos/tokio-client
 [demo server]: https://github.com/duesee/imap-codec/tree/main/assets/demos/tokio-server
diff --git a/assets/demos/tokio-client/Cargo.toml b/assets/demos/tokio-client/Cargo.toml
index deb55fd7..f7c62fd1 100644
--- a/assets/demos/tokio-client/Cargo.toml
+++ b/assets/demos/tokio-client/Cargo.toml
@@ -9,6 +9,8 @@ license = "MIT OR Apache-2.0"
 [dependencies]
 anyhow = "1.0.71"
 futures = "0.3"
-imap-codec = { path = "../../../imap-codec", features = ["tokio", "ext_sasl_ir", "ext_literal", "ext_idle", "ext_enable"] }
 tokio = { version = "1", features = ["full"] }
 tokio-util = { version = "0.7", features = ["codec"] }
+
+imap-codec = { path = "../../../imap-codec", features = ["ext_sasl_ir", "ext_literal", "ext_idle", "ext_enable"] }
+tokio-support = { path = "../tokio-support" }
diff --git a/assets/demos/tokio-client/src/main.rs b/assets/demos/tokio-client/src/main.rs
index a72dd4f3..ba2eed40 100644
--- a/assets/demos/tokio-client/src/main.rs
+++ b/assets/demos/tokio-client/src/main.rs
@@ -1,12 +1,12 @@
 use anyhow::{Context, Error};
 use futures::{SinkExt, StreamExt};
-use imap_codec::{
+use imap_codec::imap_types::{
     command::{Command, CommandBody},
     core::Tag,
     response::{Response, Status},
-    tokio::client::{Event, ImapClientCodec},
 };
 use tokio::{self, net::TcpStream};
+use tokio_support::client::{Event, ImapClientCodec};
 use tokio_util::codec::Decoder;
 
 // Poor human's terminal color support.
diff --git a/assets/demos/tokio-server/Cargo.toml b/assets/demos/tokio-server/Cargo.toml
index cd3f983c..8f1405cd 100644
--- a/assets/demos/tokio-server/Cargo.toml
+++ b/assets/demos/tokio-server/Cargo.toml
@@ -10,6 +10,8 @@ license = "MIT OR Apache-2.0"
 anyhow = "1.0.71"
 argon2 = "0.5.0"
 futures = "0.3"
-imap-codec = { path = "../../../imap-codec", features = ["tokio"] }
 tokio = { version = "1", features = ["full"] }
 tokio-util = { version = "0.7", features = ["codec"] }
+
+imap-codec = { path = "../../../imap-codec" }
+tokio-support = { path = "../tokio-support" }
diff --git a/assets/demos/tokio-server/src/main.rs b/assets/demos/tokio-server/src/main.rs
index ba28b5eb..5840e860 100644
--- a/assets/demos/tokio-server/src/main.rs
+++ b/assets/demos/tokio-server/src/main.rs
@@ -1,13 +1,13 @@
 use anyhow::{Context, Error};
 use argon2::Argon2;
 use futures::{SinkExt, StreamExt};
-use imap_codec::{
+use imap_codec::imap_types::{
     command::CommandBody,
     core::{NonEmptyVec, Text},
     response::{Capability, Continue, Data, Greeting, Response, Status},
-    tokio::server::{Action, Event, ImapServerCodec},
 };
 use tokio::{self, net::TcpListener};
+use tokio_support::server::{Action, Event, ImapServerCodec};
 use tokio_util::codec::Decoder;
 
 // Poor human's terminal color support.
diff --git a/assets/demos/tokio-support/Cargo.toml b/assets/demos/tokio-support/Cargo.toml
new file mode 100644
index 00000000..8e5a65d6
--- /dev/null
+++ b/assets/demos/tokio-support/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "tokio-support"
+version = "0.1.0"
+edition = "2021"
+license = "MIT OR Apache-2.0"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+bytes = "1.4.0"
+bounded-static = "0.5.0"
+thiserror = "1.0.29"
+tokio-util = { version = "0.7.8", features = ["codec"] }
+
+imap-codec = { path = "../../../imap-codec", features = ["bounded-static"] }
diff --git a/imap-codec/src/tokio/client.rs b/assets/demos/tokio-support/src/client.rs
similarity index 98%
rename from imap-codec/src/tokio/client.rs
rename to assets/demos/tokio-support/src/client.rs
index 82d09a8b..bf439ea9 100644
--- a/imap-codec/src/tokio/client.rs
+++ b/assets/demos/tokio-support/src/client.rs
@@ -2,16 +2,18 @@ use std::io::{Error as IoError, Write};
 
 use bounded_static::IntoBoundedStatic;
 use bytes::{Buf, BufMut, BytesMut};
+use imap_codec::{
+    codec::{Decode, DecodeError, Encode},
+    imap_types::{
+        command::Command,
+        response::{Greeting, Response},
+        state::State as ImapState,
+    },
+};
 use thiserror::Error;
 use tokio_util::codec::{Decoder, Encoder};
 
 use super::{find_crlf_inclusive, FramingError, FramingState};
-use crate::{
-    codec::{Decode, DecodeError, Encode},
-    command::Command,
-    response::{Greeting, Response},
-    state::State as ImapState,
-};
 
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub struct ImapClientCodec {
@@ -202,15 +204,14 @@ mod tests {
     use std::num::NonZeroU32;
 
     use bytes::BytesMut;
-    use tokio_util::codec::Decoder;
-
-    use super::*;
-    use crate::{
+    use imap_codec::imap_types::{
         core::{Literal, NString},
-        fetch::MessageDataItem,
+        fetch::{MessageDataItem, Section},
         response::{Data, GreetingKind},
-        section::Section,
     };
+    use tokio_util::codec::Decoder;
+
+    use super::*;
 
     #[test]
     fn test_decoder_line() {
diff --git a/imap-codec/src/tokio.rs b/assets/demos/tokio-support/src/lib.rs
similarity index 100%
rename from imap-codec/src/tokio.rs
rename to assets/demos/tokio-support/src/lib.rs
diff --git a/imap-codec/src/tokio/server.rs b/assets/demos/tokio-support/src/server.rs
similarity index 98%
rename from imap-codec/src/tokio/server.rs
rename to assets/demos/tokio-support/src/server.rs
index f2b9a28b..e189bbf3 100644
--- a/imap-codec/src/tokio/server.rs
+++ b/assets/demos/tokio-support/src/server.rs
@@ -2,15 +2,17 @@ use std::io::{Error as IoError, Write};
 
 use bounded_static::IntoBoundedStatic;
 use bytes::{Buf, BufMut, BytesMut};
+use imap_codec::{
+    codec::{Decode, DecodeError, Encode},
+    imap_types::{
+        command::Command,
+        response::{Greeting, Response},
+    },
+};
 use thiserror::Error;
 use tokio_util::codec::{Decoder, Encoder};
 
 use super::{find_crlf_inclusive, FramingError, FramingState};
-use crate::{
-    codec::{Decode, DecodeError, Encode},
-    command::Command,
-    response::{Greeting, Response},
-};
 
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub struct ImapServerCodec {
@@ -193,16 +195,16 @@ impl Encoder<&Response<'_>> for ImapServerCodec {
 #[cfg(test)]
 mod tests {
     use bytes::BytesMut;
+    use imap_codec::imap_types::{
+        command::{Command, CommandBody},
+        core::{AString, AtomExt, IString, Literal},
+        secret::Secret,
+    };
     #[cfg(feature = "quirk_crlf_relaxed")]
     use imap_types::core::Tag;
     use tokio_util::codec::Decoder;
 
     use super::*;
-    use crate::{
-        command::{Command, CommandBody},
-        core::{AString, AtomExt, IString, Literal},
-        secret::Secret,
-    };
 
     #[test]
     fn test_decoder_line() {
diff --git a/imap-codec/Cargo.toml b/imap-codec/Cargo.toml
index a8488943..84ff64be 100644
--- a/imap-codec/Cargo.toml
+++ b/imap-codec/Cargo.toml
@@ -3,7 +3,7 @@ name = "imap-codec"
 description = "Rock-solid and complete codec for IMAP"
 keywords = ["email", "imap", "codec", "parser"]
 categories = ["email", "parser-implementations", "encoding", "network-programming"]
-version = "0.10.0"
+version = "0.11.0"
 authors = ["Damian Poddebniak <poddebniak@mailbox.org>"]
 repository = "https://github.com/duesee/imap-codec"
 license = "MIT OR Apache-2.0"
@@ -50,20 +50,15 @@ quirk_rectify_numbers = []
 # * Gmail `* OK [HIGHESTMODSEQ <n>]\r\n`
 quirk_missing_text = []
 
-# Tokio support
-tokio = ["dep:bytes", "dep:tokio-util", "bounded-static"]
-
 [dependencies]
 abnf-core = "0.6.0"
 base64 = "0.21"
-bytes = { version = "1", optional = true }
 bounded-static = { version = "0.5.0", optional = true }
 chrono = { version = "0.4", default-features = false, features = ["alloc"] }
-imap-types = { version = "0.10.0", default-features = false, features = ["unvalidated"] }
+imap-types = { path = "../imap-types", default-features = false, features = ["unvalidated"] }
 nom = "7"
 serde = { version = "1", features = ["derive"], optional = true }
 thiserror = "1.0.29"
-tokio-util = { version = "0.7.1", default-features = false, features = ["codec"], optional = true }
 log = "0.4"
 
 [dev-dependencies]
diff --git a/imap-codec/README.md b/imap-codec/README.md
new file mode 100644
index 00000000..84809e45
--- /dev/null
+++ b/imap-codec/README.md
@@ -0,0 +1,43 @@
+# imap-codec
+
+This library provides parsing and serialization for [IMAP4rev1] implementations.
+It is based on [imap-types] and a [rock-solid] and [well-documented] building block for IMAP client and server implementations in Rust.
+The complete [formal syntax] of IMAP4rev1 and several IMAP [extensions] are implemented.
+
+## Features
+
+* Parsing works in streaming mode. `Incomplete` is returned when there is insufficient data to make a final decision. No message will be truncated.
+* Parsing is zero-copy by default. Allocation is avoided during parsing, but all messages can explicitly be converted into more flexible owned variants.
+* Fuzzing and property-based tests exercise the library. The library is fuzz-tested never to produce a message it can't parse itself.
+
+## Usage
+
+```rust
+use imap_codec::{
+    codec::{Decode, Encode},
+    command::Command,
+};
+
+fn main() {
+    let input = b"ABCD UID FETCH 1,2:* (BODY.PEEK[1.2.3.4.MIME]<42.1337>)\r\n";
+
+    let (remainder, parsed) = Command::decode(input).unwrap();
+    println!("# Parsed\n\n{:#?}\n\n", parsed);
+
+    let buffer = parsed.encode().dump();
+
+    // Note: IMAP4rev1 may produce messages that are not valid UTF-8.
+    println!("# Serialized\n\n{:?}", std::str::from_utf8(&buffer));
+}
+```
+
+# License
+
+This crate is dual-licensed under Apache 2.0 and MIT terms.
+
+[IMAP4rev1]: https://tools.ietf.org/html/rfc3501
+[imap-types]: https://docs.rs/imap-types/latest/imap_types/
+[rock-solid]: https://github.com/duesee/imap-codec/tree/main/imap-codec/fuzz
+[well-documented]: https://docs.rs/imap-codec/latest/imap_codec/
+[formal syntax]: https://tools.ietf.org/html/rfc3501#section-9
+[extensions]: https://docs.rs/imap-codec/latest/imap_codec/#features
diff --git a/imap-codec/benches/parse_command.rs b/imap-codec/benches/parse_command.rs
index e4460313..49a2ef16 100644
--- a/imap-codec/benches/parse_command.rs
+++ b/imap-codec/benches/parse_command.rs
@@ -1,5 +1,5 @@
 use criterion::{black_box, criterion_group, criterion_main, Criterion};
-use imap_codec::{codec::Decode, command::Command};
+use imap_codec::{codec::Decode, imap_types::command::Command};
 
 fn parse_command(input: &[u8]) -> Command {
     let (_remaining, cmd) = Command::decode(input).unwrap();
diff --git a/imap-codec/benches/parse_response.rs b/imap-codec/benches/parse_response.rs
index 415afce7..b6890655 100644
--- a/imap-codec/benches/parse_response.rs
+++ b/imap-codec/benches/parse_response.rs
@@ -1,5 +1,5 @@
 use criterion::{black_box, criterion_group, criterion_main, Criterion};
-use imap_codec::{codec::Decode, response::Response};
+use imap_codec::{codec::Decode, imap_types::response::Response};
 
 fn parse_response(input: &[u8]) -> Response {
     let (_remaining, rsp) = Response::decode(input).unwrap();
diff --git a/imap-codec/benches/serialize_command.rs b/imap-codec/benches/serialize_command.rs
index 73ef7324..9ed5e96c 100644
--- a/imap-codec/benches/serialize_command.rs
+++ b/imap-codec/benches/serialize_command.rs
@@ -3,9 +3,10 @@ use std::num::NonZeroU32;
 use criterion::{black_box, criterion_group, criterion_main, Criterion};
 use imap_codec::{
     codec::Encode,
-    command::{Command, CommandBody},
-    fetch::{MacroOrMessageDataItemNames, MessageDataItemName},
-    section::Section,
+    imap_types::{
+        command::{Command, CommandBody},
+        fetch::{MacroOrMessageDataItemNames, MessageDataItemName, Section},
+    },
 };
 
 fn criterion_benchmark(c: &mut Criterion) {
diff --git a/imap-codec/benches/serialize_response.rs b/imap-codec/benches/serialize_response.rs
index 70100d7b..842b6a3f 100644
--- a/imap-codec/benches/serialize_response.rs
+++ b/imap-codec/benches/serialize_response.rs
@@ -3,7 +3,7 @@ use std::num::NonZeroU32;
 use criterion::{black_box, criterion_group, criterion_main, Criterion};
 use imap_codec::{
     codec::Encode,
-    response::{Code, Response, Status},
+    imap_types::response::{Code, Response, Status},
 };
 
 fn criterion_benchmark(c: &mut Criterion) {
diff --git a/imap-codec/examples/parse_command.rs b/imap-codec/examples/parse_command.rs
index aead5288..551e16c5 100644
--- a/imap-codec/examples/parse_command.rs
+++ b/imap-codec/examples/parse_command.rs
@@ -2,7 +2,7 @@ use std::io::Write;
 
 use imap_codec::{
     codec::{Decode, DecodeError},
-    command::Command,
+    imap_types::command::Command,
 };
 
 const COLOR_SERVER: &str = "\x1b[34m";
diff --git a/imap-codec/examples/parse_greeting.rs b/imap-codec/examples/parse_greeting.rs
index e6572b22..09c7613e 100644
--- a/imap-codec/examples/parse_greeting.rs
+++ b/imap-codec/examples/parse_greeting.rs
@@ -1,6 +1,6 @@
 use std::io::Write;
 
-use imap_codec::{codec::Decode, response::Greeting};
+use imap_codec::{codec::Decode, imap_types::response::Greeting};
 
 fn main() -> std::io::Result<()> {
     loop {
diff --git a/imap-codec/examples/parse_response.rs b/imap-codec/examples/parse_response.rs
index 17ad7cd4..01d7548f 100644
--- a/imap-codec/examples/parse_response.rs
+++ b/imap-codec/examples/parse_response.rs
@@ -1,6 +1,6 @@
 use std::io::Write;
 
-use imap_codec::{codec::Decode, response::Response};
+use imap_codec::{codec::Decode, imap_types::response::Response};
 
 fn main() -> std::io::Result<()> {
     loop {
diff --git a/imap-codec/fuzz/fuzz_targets/authenticate_data.rs b/imap-codec/fuzz/fuzz_targets/authenticate_data.rs
index 9c727779..5ccc42e9 100644
--- a/imap-codec/fuzz/fuzz_targets/authenticate_data.rs
+++ b/imap-codec/fuzz/fuzz_targets/authenticate_data.rs
@@ -1,6 +1,6 @@
 #![no_main]
 
-use imap_codec::auth::AuthenticateData;
+use imap_codec::imap_types::auth::AuthenticateData;
 use imap_codec_fuzz::impl_decode_target;
 
 impl_decode_target!(AuthenticateData);
diff --git a/imap-codec/fuzz/fuzz_targets/authenticate_data_to_bytes_and_back.rs b/imap-codec/fuzz/fuzz_targets/authenticate_data_to_bytes_and_back.rs
index f5eccdca..1a8d2f29 100644
--- a/imap-codec/fuzz/fuzz_targets/authenticate_data_to_bytes_and_back.rs
+++ b/imap-codec/fuzz/fuzz_targets/authenticate_data_to_bytes_and_back.rs
@@ -1,6 +1,6 @@
 #![no_main]
 
-use imap_codec::auth::AuthenticateData;
+use imap_codec::imap_types::auth::AuthenticateData;
 use imap_codec_fuzz::impl_to_bytes_and_back;
 
 impl_to_bytes_and_back!(AuthenticateData);
diff --git a/imap-codec/fuzz/fuzz_targets/command.rs b/imap-codec/fuzz/fuzz_targets/command.rs
index 01497c7f..04240cc7 100644
--- a/imap-codec/fuzz/fuzz_targets/command.rs
+++ b/imap-codec/fuzz/fuzz_targets/command.rs
@@ -1,6 +1,6 @@
 #![no_main]
 
-use imap_codec::command::Command;
+use imap_codec::imap_types::command::Command;
 use imap_codec_fuzz::impl_decode_target;
 
 impl_decode_target!(Command);
diff --git a/imap-codec/fuzz/fuzz_targets/command_to_bytes_and_back.rs b/imap-codec/fuzz/fuzz_targets/command_to_bytes_and_back.rs
index 5501811e..eac2810c 100644
--- a/imap-codec/fuzz/fuzz_targets/command_to_bytes_and_back.rs
+++ b/imap-codec/fuzz/fuzz_targets/command_to_bytes_and_back.rs
@@ -1,6 +1,6 @@
 #![no_main]
 
-use imap_codec::command::Command;
+use imap_codec::imap_types::command::Command;
 use imap_codec_fuzz::impl_to_bytes_and_back;
 
 impl_to_bytes_and_back!(Command);
diff --git a/imap-codec/fuzz/fuzz_targets/greeting.rs b/imap-codec/fuzz/fuzz_targets/greeting.rs
index 53673f81..97ec2cb0 100644
--- a/imap-codec/fuzz/fuzz_targets/greeting.rs
+++ b/imap-codec/fuzz/fuzz_targets/greeting.rs
@@ -1,6 +1,6 @@
 #![no_main]
 
-use imap_codec::response::Greeting;
+use imap_codec::imap_types::response::Greeting;
 use imap_codec_fuzz::impl_decode_target;
 
 impl_decode_target!(Greeting);
diff --git a/imap-codec/fuzz/fuzz_targets/greeting_to_bytes_and_back.rs b/imap-codec/fuzz/fuzz_targets/greeting_to_bytes_and_back.rs
index 3c9ad9bd..e5c174cb 100644
--- a/imap-codec/fuzz/fuzz_targets/greeting_to_bytes_and_back.rs
+++ b/imap-codec/fuzz/fuzz_targets/greeting_to_bytes_and_back.rs
@@ -1,6 +1,6 @@
 #![no_main]
 
-use imap_codec::response::Greeting;
+use imap_codec::imap_types::response::Greeting;
 use imap_codec_fuzz::impl_to_bytes_and_back;
 
 impl_to_bytes_and_back!(Greeting);
diff --git a/imap-codec/fuzz/fuzz_targets/response.rs b/imap-codec/fuzz/fuzz_targets/response.rs
index 65acf490..4e657a35 100644
--- a/imap-codec/fuzz/fuzz_targets/response.rs
+++ b/imap-codec/fuzz/fuzz_targets/response.rs
@@ -1,6 +1,6 @@
 #![no_main]
 
-use imap_codec::response::Response;
+use imap_codec::imap_types::response::Response;
 use imap_codec_fuzz::impl_decode_target;
 
 impl_decode_target!(Response);
diff --git a/imap-codec/fuzz/fuzz_targets/response_to_bytes_and_back.rs b/imap-codec/fuzz/fuzz_targets/response_to_bytes_and_back.rs
index 082079d1..82466658 100644
--- a/imap-codec/fuzz/fuzz_targets/response_to_bytes_and_back.rs
+++ b/imap-codec/fuzz/fuzz_targets/response_to_bytes_and_back.rs
@@ -1,6 +1,6 @@
 #![no_main]
 
-use imap_codec::response::Response;
+use imap_codec::imap_types::response::Response;
 use imap_codec_fuzz::impl_to_bytes_and_back;
 
 impl_to_bytes_and_back!(Response);
diff --git a/imap-codec/fuzz/src/lib.rs b/imap-codec/fuzz/src/lib.rs
index 42228b18..a54bcf39 100644
--- a/imap-codec/fuzz/src/lib.rs
+++ b/imap-codec/fuzz/src/lib.rs
@@ -6,7 +6,7 @@ macro_rules! impl_decode_target {
         fuzz_target!(|input: &[u8]| {
             use imap_codec::codec::{Decode, DecodeError, Encode};
             #[cfg(feature = "debug")]
-            use imap_codec::utils::escape_byte_string;
+            use imap_codec::imap_types::utils::escape_byte_string;
 
             #[cfg(feature = "debug")]
             println!("[!] Input:      {}", escape_byte_string(input));
@@ -81,7 +81,7 @@ macro_rules! impl_to_bytes_and_back {
         fuzz_target!(|input: $object| {
             use imap_codec::codec::{Decode, Encode};
             #[cfg(feature = "debug")]
-            use imap_codec::utils::escape_byte_string;
+            use imap_codec::imap_types::utils::escape_byte_string;
 
             #[cfg(feature = "debug")]
             println!("[!] Input:  {:?}", input);
diff --git a/imap-codec/src/auth.rs b/imap-codec/src/auth.rs
index 4fbeb66b..ff2244c6 100644
--- a/imap-codec/src/auth.rs
+++ b/imap-codec/src/auth.rs
@@ -1,17 +1,16 @@
-//! Please refer to [`imap_types::auth`].
-
 #[cfg(not(feature = "quirk_crlf_relaxed"))]
 use abnf_core::streaming::crlf;
 #[cfg(feature = "quirk_crlf_relaxed")]
 use abnf_core::streaming::crlf_relaxed as crlf;
-/// Re-export everything from imap-types.
-pub use imap_types::auth::*;
+use imap_types::{
+    auth::{AuthMechanism, AuthenticateData},
+    secret::Secret,
+};
 use nom::{combinator::map, sequence::terminated};
 
 use crate::{
     codec::IMAPResult,
     core::{atom, base64},
-    secret::Secret,
 };
 
 // ----- Unsorted IMAP parsers -----
diff --git a/imap-codec/src/body.rs b/imap-codec/src/body.rs
index 0bf07dd9..3cd550fd 100644
--- a/imap-codec/src/body.rs
+++ b/imap-codec/src/body.rs
@@ -1,8 +1,11 @@
-//! Please refer to [`imap_types::body`].
-
 use abnf_core::streaming::sp;
-/// Re-export everything from imap-types.
-pub use imap_types::body::*;
+use imap_types::{
+    body::{
+        BasicFields, Body, BodyExtension, BodyStructure, Disposition, Language, Location,
+        MultiPartExtensionData, SinglePartExtensionData, SpecificFields,
+    },
+    core::{IString, NString, NonEmptyVec},
+};
 use nom::{
     branch::alt,
     bytes::streaming::{tag, tag_no_case},
@@ -13,7 +16,7 @@ use nom::{
 
 use crate::{
     codec::{IMAPErrorKind, IMAPParseError, IMAPResult},
-    core::{nil, nstring, number, string, IString, NString, NonEmptyVec},
+    core::{nil, nstring, number, string},
     envelope::envelope,
 };
 
@@ -573,14 +576,15 @@ pub(crate) fn media_text(input: &[u8]) -> IMAPResult<&[u8], IString> {
 mod tests {
     use std::num::NonZeroU32;
 
-    use super::*;
-    use crate::{
+    use imap_types::{
         core::{Literal, Quoted},
         fetch::MessageDataItem,
         response::{Data, Response},
-        testing::{kat_inverse_response, known_answer_test_encode},
     };
 
+    use super::*;
+    use crate::testing::{kat_inverse_response, known_answer_test_encode};
+
     #[test]
     fn test_parse_media_basic() {
         media_basic(b"\"application\" \"xxx\"").unwrap();
diff --git a/imap-codec/src/codec.rs b/imap-codec/src/codec.rs
index 6b106bbc..72f8e9e4 100644
--- a/imap-codec/src/codec.rs
+++ b/imap-codec/src/codec.rs
@@ -12,7 +12,7 @@
 //! ```
 //! use imap_codec::{
 //!     codec::Encode,
-//!     command::{Command, CommandBody},
+//!     imap_types::command::{Command, CommandBody},
 //! };
 //!
 //! // Create some command.
@@ -47,21 +47,22 @@ mod encode;
 mod tests {
     use std::num::NonZeroU32;
 
-    use imap_types::{auth::AuthenticateData, secret::Secret};
-
-    use super::*;
     #[cfg(feature = "ext_literal")]
-    use crate::core::LiteralMode;
-    use crate::{
+    use imap_types::core::LiteralMode;
+    use imap_types::{
+        auth::AuthenticateData,
         command::{Command, CommandBody},
         core::{IString, Literal, NString, NonEmptyVec},
         fetch::MessageDataItem,
         mailbox::Mailbox,
         response::{Data, Greeting, GreetingKind, Response},
-        testing::{
-            kat_inverse_authenticate_data, kat_inverse_command, kat_inverse_greeting,
-            kat_inverse_response,
-        },
+        secret::Secret,
+    };
+
+    use super::*;
+    use crate::testing::{
+        kat_inverse_authenticate_data, kat_inverse_command, kat_inverse_greeting,
+        kat_inverse_response,
     };
 
     #[test]
diff --git a/imap-codec/src/codec/decode.rs b/imap-codec/src/codec/decode.rs
index 1e974832..ec4e9d27 100644
--- a/imap-codec/src/codec/decode.rs
+++ b/imap-codec/src/codec/decode.rs
@@ -4,14 +4,21 @@ use std::num::{ParseIntError, TryFromIntError};
 use bounded_static::IntoBoundedStatic;
 #[cfg(feature = "ext_literal")]
 use imap_types::core::LiteralMode;
+#[cfg(feature = "ext_idle")]
+use imap_types::extensions::idle::IdleDone;
+use imap_types::{
+    auth::AuthenticateData,
+    command::Command,
+    response::{Continue, Greeting, Response},
+};
 use nom::error::{ErrorKind, FromExternalError, ParseError};
 
 #[cfg(feature = "ext_idle")]
-use crate::extensions::idle::{idle_done, IdleDone};
+use crate::extensions::idle::idle_done;
 use crate::{
-    auth::{authenticate_data, AuthenticateData},
-    command::{command, Command},
-    response::{continue_req, greeting, response, Continue, Greeting, Response},
+    auth::authenticate_data,
+    command::command,
+    response::{continue_req, greeting, response},
 };
 
 /// An extended version of [`nom::IResult`].
@@ -204,19 +211,19 @@ impl_decode_static_for_object!(Continue<'a>, Continue<'static>, continue_req);
 mod tests {
     use std::num::NonZeroU32;
 
-    use imap_types::secret::Secret;
-
-    use super::*;
     #[cfg(feature = "ext_idle")]
-    use crate::extensions::idle::IdleDone;
-    use crate::{
+    use imap_types::extensions::idle::IdleDone;
+    use imap_types::{
         command::{Command, CommandBody},
         core::{IString, Literal, NString, NonEmptyVec},
         fetch::MessageDataItem,
         mailbox::Mailbox,
         response::{Data, Greeting, GreetingKind, Response},
+        secret::Secret,
     };
 
+    use super::*;
+
     #[test]
     fn test_decode_greeting() {
         let tests = [
diff --git a/imap-codec/src/codec/encode.rs b/imap-codec/src/codec/encode.rs
index 50704f01..9a3ab092 100644
--- a/imap-codec/src/codec/encode.rs
+++ b/imap-codec/src/codec/encode.rs
@@ -4,11 +4,9 @@ use base64::{engine::general_purpose::STANDARD as base64, Engine};
 use chrono::{DateTime as ChronoDateTime, FixedOffset};
 #[cfg(feature = "ext_literal")]
 use imap_types::core::LiteralMode;
-use utils::{join_serializable, List1AttributeValueOrNil, List1OrNil};
-
 #[cfg(feature = "ext_compress")]
-use crate::extensions::compress::CompressionAlgorithm;
-use crate::{
+use imap_types::extensions::compress::CompressionAlgorithm;
+use imap_types::{
     auth::{AuthMechanism, AuthenticateData},
     body::{
         BasicFields, Body, BodyExtension, BodyStructure, Disposition, Language, Location,
@@ -20,18 +18,20 @@ use crate::{
     },
     datetime::{DateTime, NaiveDate},
     envelope::{Address, Envelope},
-    fetch::{Macro, MacroOrMessageDataItemNames, MessageDataItem, MessageDataItemName},
+    fetch::{
+        Macro, MacroOrMessageDataItemNames, MessageDataItem, MessageDataItemName, Part, Section,
+    },
     flag::{Flag, FlagExtension, FlagFetch, FlagNameAttribute, FlagPerm, StoreResponse, StoreType},
     mailbox::{ListCharString, ListMailbox, Mailbox, MailboxOther},
     response::{
         Capability, Code, CodeOther, Continue, Data, Greeting, GreetingKind, Response, Status,
     },
     search::SearchKey,
-    section::{Part, Section},
     sequence::{SeqOrUid, Sequence, SequenceSet},
     status::{StatusDataItem, StatusDataItemName},
     utils::escape_quoted,
 };
+use utils::{join_serializable, List1AttributeValueOrNil, List1OrNil};
 
 pub trait Encode {
     /// Create an [`Encoded`] for this message.
@@ -50,7 +50,7 @@ pub trait Encode {
 /// ```rust
 /// use imap_codec::{
 ///     codec::{Encode, Fragment},
-///     command::{Command, CommandBody},
+///     imap_types::command::{Command, CommandBody},
 /// };
 ///
 /// let cmd = Command::new("A", CommandBody::login("alice", "pass").unwrap()).unwrap();
@@ -1697,8 +1697,7 @@ mod utils {
 mod tests {
     use std::num::NonZeroU32;
 
-    use super::*;
-    use crate::{
+    use imap_types::{
         auth::AuthMechanism,
         command::{Command, CommandBody},
         core::{AString, Literal, NString, NonEmptyVec},
@@ -1707,6 +1706,8 @@ mod tests {
         utils::escape_byte_string,
     };
 
+    use super::*;
+
     #[test]
     fn test_api_encoder_usage() {
         let cmd = Command::new(
diff --git a/imap-codec/src/command.rs b/imap-codec/src/command.rs
index 39770e43..e2b2f584 100644
--- a/imap-codec/src/command.rs
+++ b/imap-codec/src/command.rs
@@ -1,5 +1,3 @@
-//! Please refer to [`imap_types::command`].
-
 #[cfg(feature = "ext_sasl_ir")]
 use std::borrow::Cow;
 
@@ -8,8 +6,14 @@ use abnf_core::streaming::crlf;
 #[cfg(feature = "quirk_crlf_relaxed")]
 use abnf_core::streaming::crlf_relaxed as crlf;
 use abnf_core::streaming::sp;
-/// Re-export everything from imap-types.
-pub use imap_types::command::*;
+use imap_types::{
+    auth::AuthMechanism,
+    command::{Command, CommandBody},
+    core::AString,
+    fetch::{Macro, MacroOrMessageDataItemNames},
+    flag::{Flag, StoreResponse, StoreType},
+    secret::Secret,
+};
 use nom::{
     branch::alt,
     bytes::streaming::{tag, tag_no_case},
@@ -31,15 +35,14 @@ use crate::extensions::quota::{getquota, getquotaroot, setquota};
 #[cfg(feature = "ext_move")]
 use crate::extensions::r#move::r#move;
 use crate::{
-    auth::{auth_type, AuthMechanism},
+    auth::auth_type,
     codec::IMAPResult,
-    core::{astring, literal, tag_imap, AString},
+    core::{astring, literal, tag_imap},
     datetime::date_time,
-    fetch::{fetch_att, Macro, MacroOrMessageDataItemNames},
-    flag::{flag, flag_list, Flag, StoreResponse, StoreType},
+    fetch::fetch_att,
+    flag::{flag, flag_list},
     mailbox::{list_mailbox, mailbox},
     search::search,
-    secret::Secret,
     sequence::sequence_set,
     status::status_att,
 };
@@ -561,12 +564,13 @@ pub(crate) fn uid(input: &[u8]) -> IMAPResult<&[u8], CommandBody> {
 mod tests {
     use std::num::NonZeroU32;
 
+    #[cfg(feature = "ext_sasl_ir")]
+    use imap_types::core::Tag;
+    use imap_types::fetch::{MessageDataItemName, Section};
+
     use super::*;
     #[cfg(feature = "ext_sasl_ir")]
     use crate::codec::Encode;
-    #[cfg(feature = "ext_sasl_ir")]
-    use crate::core::Tag;
-    use crate::{fetch::MessageDataItemName, section::Section};
 
     #[test]
     fn test_parse_fetch() {
diff --git a/imap-codec/src/core.rs b/imap-codec/src/core.rs
index d010a615..0c5e8dfa 100644
--- a/imap-codec/src/core.rs
+++ b/imap-codec/src/core.rs
@@ -1,5 +1,3 @@
-//! Please refer to [`imap_types::core`].
-
 use std::{borrow::Cow, num::NonZeroU32, str::from_utf8};
 
 #[cfg(not(feature = "quirk_crlf_relaxed"))]
@@ -8,8 +6,17 @@ use abnf_core::streaming::crlf;
 use abnf_core::streaming::crlf_relaxed as crlf;
 use abnf_core::{is_alpha, is_digit, streaming::dquote};
 use base64::{engine::general_purpose::STANDARD as _base64, Engine};
-/// Re-export everything from imap-types.
-pub use imap_types::core::*;
+#[cfg(feature = "ext_literal")]
+use imap_types::core::LiteralMode;
+use imap_types::{
+    core::{
+        AString, Atom, AtomExt, Charset, IString, Literal, NString, Quoted, QuotedChar, Tag, Text,
+    },
+    utils::{
+        indicators::{is_astring_char, is_atom_char, is_quoted_specials, is_text_char},
+        unescape_quoted,
+    },
+};
 #[cfg(feature = "ext_literal")]
 use nom::character::streaming::char;
 use nom::{
@@ -20,13 +27,7 @@ use nom::{
     sequence::{delimited, terminated, tuple},
 };
 
-use crate::{
-    codec::{IMAPErrorKind, IMAPParseError, IMAPResult},
-    utils::{
-        indicators::{is_astring_char, is_atom_char, is_quoted_specials, is_text_char},
-        unescape_quoted,
-    },
-};
+use crate::codec::{IMAPErrorKind, IMAPParseError, IMAPResult};
 
 // ----- number -----
 
diff --git a/imap-codec/src/datetime.rs b/imap-codec/src/datetime.rs
index 6b4d4377..624744b9 100644
--- a/imap-codec/src/datetime.rs
+++ b/imap-codec/src/datetime.rs
@@ -1,5 +1,3 @@
-//! Please refer to [`imap_types::datetime`].
-
 use abnf_core::{
     is_digit,
     streaming::{dquote, sp},
@@ -7,8 +5,7 @@ use abnf_core::{
 use chrono::{
     FixedOffset, LocalResult, NaiveDate as ChronoNaiveDate, NaiveDateTime, NaiveTime, TimeZone,
 };
-/// Re-export everything from imap-types.
-pub use imap_types::datetime::*;
+use imap_types::datetime::{DateTime, NaiveDate};
 use nom::{
     branch::alt,
     bytes::streaming::{tag, tag_no_case, take_while_m_n},
diff --git a/imap-codec/src/envelope.rs b/imap-codec/src/envelope.rs
index a50afaf9..59e3b96b 100644
--- a/imap-codec/src/envelope.rs
+++ b/imap-codec/src/envelope.rs
@@ -1,8 +1,8 @@
-//! Please refer to [`imap_types::envelope`].
-
 use abnf_core::streaming::sp;
-/// Re-export everything from imap-types.
-pub use imap_types::envelope::*;
+use imap_types::{
+    core::NString,
+    envelope::{Address, Envelope},
+};
 use nom::{
     branch::alt,
     bytes::streaming::tag,
@@ -13,7 +13,7 @@ use nom::{
 
 use crate::{
     codec::IMAPResult,
-    core::{nil, nstring, NString},
+    core::{nil, nstring},
 };
 
 /// ```abnf
@@ -239,8 +239,9 @@ pub(crate) fn addr_host(input: &[u8]) -> IMAPResult<&[u8], NString> {
 
 #[cfg(test)]
 mod tests {
+    use imap_types::core::{IString, NString};
+
     use super::*;
-    use crate::core::{IString, NString};
 
     #[test]
     fn test_parse_address() {
diff --git a/imap-codec/src/extensions.rs b/imap-codec/src/extensions.rs
index cdfe2d4a..271afb5e 100644
--- a/imap-codec/src/extensions.rs
+++ b/imap-codec/src/extensions.rs
@@ -1,5 +1,3 @@
-//! Please refer to [`imap_types::extensions`].
-
 #[cfg(feature = "ext_compress")]
 #[cfg_attr(docsrs, doc(cfg(feature = "ext_compress")))]
 pub mod compress;
diff --git a/imap-codec/src/extensions/compress.rs b/imap-codec/src/extensions/compress.rs
index 80c75339..ae68c21a 100644
--- a/imap-codec/src/extensions/compress.rs
+++ b/imap-codec/src/extensions/compress.rs
@@ -8,18 +8,14 @@
 
 use std::io::Write;
 
-/// Re-export everything from imap-types.
-pub use imap_types::extensions::compress::*;
+use imap_types::{command::CommandBody, extensions::compress::CompressionAlgorithm};
 use nom::{
     bytes::streaming::tag_no_case,
     combinator::{map, value},
     sequence::preceded,
 };
 
-use crate::{
-    codec::{EncodeContext, Encoder, IMAPResult},
-    command::CommandBody,
-};
+use crate::codec::{EncodeContext, Encoder, IMAPResult};
 
 /// `algorithm = "DEFLATE"`
 pub(crate) fn algorithm(input: &[u8]) -> IMAPResult<&[u8], CompressionAlgorithm> {
@@ -43,11 +39,10 @@ impl Encoder for CompressionAlgorithm {
 
 #[cfg(test)]
 mod tests {
+    use imap_types::command::{Command, CommandBody};
+
     use super::*;
-    use crate::{
-        command::{Command, CommandBody},
-        testing::kat_inverse_command,
-    };
+    use crate::testing::kat_inverse_command;
 
     #[test]
     fn test_parse_compress() {
diff --git a/imap-codec/src/extensions/enable.rs b/imap-codec/src/extensions/enable.rs
index 5d7913c4..c4ce087a 100644
--- a/imap-codec/src/extensions/enable.rs
+++ b/imap-codec/src/extensions/enable.rs
@@ -9,8 +9,11 @@
 use std::io::Write;
 
 use abnf_core::streaming::sp;
-/// Re-export everything from imap-types.
-pub use imap_types::extensions::enable::*;
+use imap_types::{
+    command::CommandBody,
+    extensions::enable::{CapabilityEnable, CapabilityEnableOther, Utf8Kind},
+    response::Data,
+};
 use nom::{
     bytes::streaming::tag_no_case,
     combinator::map,
@@ -20,9 +23,7 @@ use nom::{
 
 use crate::{
     codec::{EncodeContext, Encoder, IMAPResult},
-    command::CommandBody,
     core::atom,
-    response::Data,
 };
 
 /// `command-any =/ "ENABLE" 1*(SP capability)`
@@ -88,8 +89,14 @@ impl<'a> Encoder for CapabilityEnableOther<'a> {
 
 #[cfg(test)]
 mod tests {
+    use imap_types::{
+        command::Command,
+        core::Atom,
+        extensions::enable::{CapabilityEnable, Utf8Kind},
+    };
+
     use super::*;
-    use crate::{command::Command, core::Atom, testing::kat_inverse_command};
+    use crate::testing::kat_inverse_command;
 
     #[test]
     fn test_parse_enable() {
diff --git a/imap-codec/src/extensions/idle.rs b/imap-codec/src/extensions/idle.rs
index 5813daa6..d650705a 100644
--- a/imap-codec/src/extensions/idle.rs
+++ b/imap-codec/src/extensions/idle.rs
@@ -13,14 +13,10 @@ use std::io::Write;
 use abnf_core::streaming::crlf;
 #[cfg(feature = "quirk_crlf_relaxed")]
 use abnf_core::streaming::crlf_relaxed as crlf;
-/// Re-export everything from imap-types.
-pub use imap_types::extensions::idle::*;
+use imap_types::{command::CommandBody, extensions::idle::IdleDone};
 use nom::{bytes::streaming::tag_no_case, combinator::value, sequence::tuple};
 
-use crate::{
-    codec::{EncodeContext, Encoder, IMAPResult},
-    command::CommandBody,
-};
+use crate::codec::{EncodeContext, Encoder, IMAPResult};
 
 /// `idle = "IDLE" CRLF "DONE"` (edited)
 ///
@@ -63,10 +59,11 @@ impl Encoder for IdleDone {
 
 #[cfg(test)]
 mod tests {
+    use imap_types::command::{Command, CommandBody};
+
     use super::*;
     use crate::{
         codec::{Decode, DecodeError},
-        command::{Command, CommandBody},
         testing::kat_inverse_command,
     };
 
diff --git a/imap-codec/src/extensions/literal.rs b/imap-codec/src/extensions/literal.rs
index db6383f6..728ffcd6 100644
--- a/imap-codec/src/extensions/literal.rs
+++ b/imap-codec/src/extensions/literal.rs
@@ -1,7 +1,6 @@
 use std::io::Write;
 
-/// Re-export everything from imap-types.
-pub use imap_types::extensions::literal::*;
+use imap_types::extensions::literal::LiteralCapability;
 
 use crate::codec::{EncodeContext, Encoder};
 
@@ -16,14 +15,15 @@ impl Encoder for LiteralCapability {
 
 #[cfg(test)]
 mod tests {
-    use super::*;
-    use crate::{
+    use imap_types::{
         command::{Command, CommandBody},
         core::{Literal, NonEmptyVec},
         response::{Capability, Code, Greeting},
-        testing::{kat_inverse_command, kat_inverse_greeting},
     };
 
+    use super::*;
+    use crate::testing::{kat_inverse_command, kat_inverse_greeting};
+
     #[test]
     fn test_kat_inverse_command_login_literal_plus() {
         kat_inverse_command(&[
diff --git a/imap-codec/src/extensions/move.rs b/imap-codec/src/extensions/move.rs
index 947d1004..23f99c24 100644
--- a/imap-codec/src/extensions/move.rs
+++ b/imap-codec/src/extensions/move.rs
@@ -1,11 +1,10 @@
 //! IMAP - MOVE Extension
 
 use abnf_core::streaming::sp;
-/// Re-export everything from imap-types.
-pub use imap_types::extensions::r#move::*;
+use imap_types::command::CommandBody;
 use nom::{bytes::streaming::tag_no_case, sequence::tuple};
 
-use crate::{codec::IMAPResult, command::CommandBody, mailbox::mailbox, sequence::sequence_set};
+use crate::{codec::IMAPResult, mailbox::mailbox, sequence::sequence_set};
 
 /// ```abnf
 /// move = "MOVE" SP sequence-set SP mailbox
@@ -27,10 +26,9 @@ pub(crate) fn r#move(input: &[u8]) -> IMAPResult<&[u8], CommandBody> {
 
 #[cfg(test)]
 mod tests {
-    use crate::{
-        command::{Command, CommandBody},
-        testing::kat_inverse_command,
-    };
+    use imap_types::command::{Command, CommandBody};
+
+    use crate::testing::kat_inverse_command;
 
     #[test]
     fn test_kat_inverse_command_move() {
diff --git a/imap-codec/src/extensions/quota.rs b/imap-codec/src/extensions/quota.rs
index 2e722fe7..cf2d3a70 100644
--- a/imap-codec/src/extensions/quota.rs
+++ b/imap-codec/src/extensions/quota.rs
@@ -3,8 +3,12 @@
 use std::io::Write;
 
 use abnf_core::streaming::sp;
-/// Re-export everything from imap-types.
-use imap_types::extensions::quota::*;
+use imap_types::{
+    command::CommandBody,
+    core::{AString, NonEmptyVec},
+    extensions::quota::{QuotaGet, QuotaSet, Resource},
+    response::Data,
+};
 use nom::{
     bytes::streaming::{tag, tag_no_case},
     combinator::map,
@@ -14,10 +18,8 @@ use nom::{
 
 use crate::{
     codec::{EncodeContext, Encoder, IMAPResult},
-    command::CommandBody,
-    core::{astring, atom, number64, AString, NonEmptyVec},
+    core::{astring, atom, number64},
     mailbox::mailbox,
-    response::Data,
 };
 
 /// ```abnf
@@ -210,16 +212,18 @@ impl<'a> Encoder for QuotaSet<'a> {
 
 #[cfg(test)]
 mod tests {
-    use super::*;
-    use crate::{
+    use imap_types::{
         command::{Command, CommandBody},
         core::{IString, Tag},
+        extensions::quota::{QuotaGet, QuotaSet, Resource},
         mailbox::Mailbox,
         response::{Capability, Code, Response, Status},
         status::{StatusDataItem, StatusDataItemName},
-        testing::{kat_inverse_command, kat_inverse_response},
     };
 
+    use super::*;
+    use crate::testing::{kat_inverse_command, kat_inverse_response};
+
     #[test]
     fn test_parse_resource_name() {
         let tests = [
diff --git a/imap-codec/src/extensions/unselect.rs b/imap-codec/src/extensions/unselect.rs
index 54ba82f6..96ddcec3 100644
--- a/imap-codec/src/extensions/unselect.rs
+++ b/imap-codec/src/extensions/unselect.rs
@@ -1,9 +1,8 @@
 #[cfg(test)]
 mod tests {
-    use crate::{
-        command::{Command, CommandBody},
-        testing::kat_inverse_command,
-    };
+    use imap_types::command::{Command, CommandBody};
+
+    use crate::testing::kat_inverse_command;
 
     #[test]
     fn test_kat_inverse_command_unselect() {
diff --git a/imap-codec/src/fetch.rs b/imap-codec/src/fetch.rs
index f0b33f01..2f40b93e 100644
--- a/imap-codec/src/fetch.rs
+++ b/imap-codec/src/fetch.rs
@@ -1,10 +1,10 @@
-//! Please refer to [`imap_types::fetch`].
-
 use std::num::NonZeroU32;
 
 use abnf_core::streaming::sp;
-/// Re-export everything from imap-types.
-pub use imap_types::fetch::*;
+use imap_types::{
+    core::{AString, NonEmptyVec},
+    fetch::{MessageDataItem, MessageDataItemName, Part, PartSpecifier, Section},
+};
 use nom::{
     branch::alt,
     bytes::streaming::{tag, tag_no_case},
@@ -16,11 +16,10 @@ use nom::{
 use crate::{
     body::body,
     codec::IMAPResult,
-    core::{nstring, number, nz_number, NonEmptyVec},
+    core::{astring, nstring, number, nz_number},
     datetime::date_time,
     envelope::envelope,
     flag::flag_fetch,
-    section::section,
 };
 
 /// `fetch-att = "ENVELOPE" /
@@ -187,17 +186,111 @@ pub(crate) fn uniqueid(input: &[u8]) -> IMAPResult<&[u8], NonZeroU32> {
     nz_number(input)
 }
 
+/// `section = "[" [section-spec] "]"`
+pub(crate) fn section(input: &[u8]) -> IMAPResult<&[u8], Option<Section>> {
+    delimited(tag(b"["), opt(section_spec), tag(b"]"))(input)
+}
+
+/// `section-spec = section-msgtext / (section-part ["." section-text])`
+pub(crate) fn section_spec(input: &[u8]) -> IMAPResult<&[u8], Section> {
+    alt((
+        map(section_msgtext, |part_specifier| match part_specifier {
+            PartSpecifier::PartNumber(_) => unreachable!(),
+            PartSpecifier::Header => Section::Header(None),
+            PartSpecifier::HeaderFields(fields) => Section::HeaderFields(None, fields),
+            PartSpecifier::HeaderFieldsNot(fields) => Section::HeaderFieldsNot(None, fields),
+            PartSpecifier::Text => Section::Text(None),
+            PartSpecifier::Mime => unreachable!(),
+        }),
+        map(
+            tuple((section_part, opt(tuple((tag(b"."), section_text))))),
+            |(part_number, maybe_part_specifier)| {
+                if let Some((_, part_specifier)) = maybe_part_specifier {
+                    match part_specifier {
+                        PartSpecifier::PartNumber(_) => unreachable!(),
+                        PartSpecifier::Header => Section::Header(Some(Part(part_number))),
+                        PartSpecifier::HeaderFields(fields) => {
+                            Section::HeaderFields(Some(Part(part_number)), fields)
+                        }
+                        PartSpecifier::HeaderFieldsNot(fields) => {
+                            Section::HeaderFieldsNot(Some(Part(part_number)), fields)
+                        }
+                        PartSpecifier::Text => Section::Text(Some(Part(part_number))),
+                        PartSpecifier::Mime => Section::Mime(Part(part_number)),
+                    }
+                } else {
+                    Section::Part(Part(part_number))
+                }
+            },
+        ),
+    ))(input)
+}
+
+/// `section-msgtext = "HEADER" / "HEADER.FIELDS" [".NOT"] SP header-list / "TEXT"`
+///
+/// Top-level or MESSAGE/RFC822 part
+pub(crate) fn section_msgtext(input: &[u8]) -> IMAPResult<&[u8], PartSpecifier> {
+    alt((
+        map(
+            tuple((tag_no_case(b"HEADER.FIELDS.NOT"), sp, header_list)),
+            |(_, _, header_list)| PartSpecifier::HeaderFieldsNot(header_list),
+        ),
+        map(
+            tuple((tag_no_case(b"HEADER.FIELDS"), sp, header_list)),
+            |(_, _, header_list)| PartSpecifier::HeaderFields(header_list),
+        ),
+        value(PartSpecifier::Header, tag_no_case(b"HEADER")),
+        value(PartSpecifier::Text, tag_no_case(b"TEXT")),
+    ))(input)
+}
+
+#[inline]
+/// `section-part = nz-number *("." nz-number)`
+///
+/// Body part nesting
+pub(crate) fn section_part(input: &[u8]) -> IMAPResult<&[u8], NonEmptyVec<NonZeroU32>> {
+    map(
+        separated_list1(tag(b"."), nz_number),
+        NonEmptyVec::unvalidated,
+    )(input)
+}
+
+/// `section-text = section-msgtext / "MIME"`
+///
+/// Text other than actual body part (headers, etc.)
+pub(crate) fn section_text(input: &[u8]) -> IMAPResult<&[u8], PartSpecifier> {
+    alt((
+        section_msgtext,
+        value(PartSpecifier::Mime, tag_no_case(b"MIME")),
+    ))(input)
+}
+
+/// `header-list = "(" header-fld-name *(SP header-fld-name) ")"`
+pub(crate) fn header_list(input: &[u8]) -> IMAPResult<&[u8], NonEmptyVec<AString>> {
+    map(
+        delimited(tag(b"("), separated_list1(sp, header_fld_name), tag(b")")),
+        NonEmptyVec::unvalidated,
+    )(input)
+}
+
+#[inline]
+/// `header-fld-name = astring`
+pub(crate) fn header_fld_name(input: &[u8]) -> IMAPResult<&[u8], AString> {
+    astring(input)
+}
+
 #[cfg(test)]
 mod tests {
-    use super::*;
-    use crate::{
+    use imap_types::{
         body::{BasicFields, Body, BodyStructure, SpecificFields},
         core::{IString, NString},
         datetime::DateTime,
         envelope::Envelope,
-        testing::known_answer_test_encode,
     };
 
+    use super::*;
+    use crate::testing::known_answer_test_encode;
+
     #[test]
     fn test_encode_message_data_item_name() {
         let tests = [
@@ -329,4 +422,58 @@ mod tests {
             known_answer_test_encode(test);
         }
     }
+
+    #[test]
+    fn test_encode_section() {
+        let tests = [
+            (
+                Section::Part(Part(NonEmptyVec::from(NonZeroU32::try_from(1).unwrap()))),
+                b"1".as_ref(),
+            ),
+            (Section::Header(None), b"HEADER"),
+            (
+                Section::Header(Some(Part(NonEmptyVec::from(
+                    NonZeroU32::try_from(1).unwrap(),
+                )))),
+                b"1.HEADER",
+            ),
+            (
+                Section::HeaderFields(None, NonEmptyVec::from(AString::try_from("").unwrap())),
+                b"HEADER.FIELDS (\"\")",
+            ),
+            (
+                Section::HeaderFields(
+                    Some(Part(NonEmptyVec::from(NonZeroU32::try_from(1).unwrap()))),
+                    NonEmptyVec::from(AString::try_from("").unwrap()),
+                ),
+                b"1.HEADER.FIELDS (\"\")",
+            ),
+            (
+                Section::HeaderFieldsNot(None, NonEmptyVec::from(AString::try_from("").unwrap())),
+                b"HEADER.FIELDS.NOT (\"\")",
+            ),
+            (
+                Section::HeaderFieldsNot(
+                    Some(Part(NonEmptyVec::from(NonZeroU32::try_from(1).unwrap()))),
+                    NonEmptyVec::from(AString::try_from("").unwrap()),
+                ),
+                b"1.HEADER.FIELDS.NOT (\"\")",
+            ),
+            (Section::Text(None), b"TEXT"),
+            (
+                Section::Text(Some(Part(NonEmptyVec::from(
+                    NonZeroU32::try_from(1).unwrap(),
+                )))),
+                b"1.TEXT",
+            ),
+            (
+                Section::Mime(Part(NonEmptyVec::from(NonZeroU32::try_from(1).unwrap()))),
+                b"1.MIME",
+            ),
+        ];
+
+        for test in tests {
+            known_answer_test_encode(test)
+        }
+    }
 }
diff --git a/imap-codec/src/flag.rs b/imap-codec/src/flag.rs
index d00bcb5c..3d2a4dc4 100644
--- a/imap-codec/src/flag.rs
+++ b/imap-codec/src/flag.rs
@@ -1,8 +1,5 @@
-//! Please refer to [`imap_types::flag`].
-
 use abnf_core::streaming::sp;
-/// Re-export everything from imap-types.
-pub use imap_types::flag::*;
+use imap_types::flag::{Flag, FlagFetch, FlagNameAttribute, FlagPerm};
 use nom::{
     branch::alt,
     bytes::streaming::tag,
@@ -136,8 +133,12 @@ pub(crate) fn mbx_list_flags(input: &[u8]) -> IMAPResult<&[u8], Vec<FlagNameAttr
 
 #[cfg(test)]
 mod tests {
+    use imap_types::{
+        core::Atom,
+        flag::{Flag, FlagExtension, FlagFetch, FlagNameAttribute, FlagPerm},
+    };
+
     use super::*;
-    use crate::core::Atom;
 
     #[test]
     fn test_parse_flag_fetch() {
diff --git a/imap-codec/src/lib.rs b/imap-codec/src/lib.rs
index 696e6969..f054c947 100644
--- a/imap-codec/src/lib.rs
+++ b/imap-codec/src/lib.rs
@@ -8,7 +8,7 @@
 //! ```rust
 //! use imap_codec::{
 //!     codec::{Decode, Encode},
-//!     command::Command,
+//!     imap_types::command::Command,
 //! };
 //!
 //! // We assume here that the message is already complete.
@@ -31,9 +31,9 @@
 //!
 //! Parsing is implemented through the [`Decode`](crate::codec::Decode) trait.
 //! The main entry points for parsing are
-//! [`Greeting::decode(...)`](response::Greeting#method.decode) (to parse the first message from a server),
-//! [`Command::decode(...)`](command::Command#method.decode) (to parse commands from a client), and
-//! [`Response::decode(...)`](response::Response#method.decode) (to parse responses or results from a server).
+//! [`Greeting::decode(...)`](imap_types::response::Greeting#method.decode) (to parse the first message from a server),
+//! [`Command::decode(...)`](imap_types::command::Command#method.decode) (to parse commands from a client), and
+//! [`Response::decode(...)`](imap_types::response::Response#method.decode) (to parse responses or results from a server).
 //! Note, however, that certain message flows require other parsers as well.
 //! Every parser takes an input (`&[u8]`) and produces a remainder and a parsed value.
 //!
@@ -55,10 +55,10 @@
 //!
 //! ```rust
 //! #[cfg(feature = "ext_literal")]
-//! use imap_codec::core::LiteralMode;
+//! use imap_codec::imap_types::core::LiteralMode;
 //! use imap_codec::{
 //!     codec::{Decode, Encode, Fragment},
-//!     command::{Command, CommandBody},
+//!     imap_types::command::{Command, CommandBody},
 //! };
 //!
 //! let command = Command::new("A1", CommandBody::login("Alice", "Pa²²W0rD").unwrap()).unwrap();
@@ -108,7 +108,6 @@
 //! | quirk_crlf_relaxed    | Make `\r` in `\r\n` optional.  | No                 |
 //! | quirk_rectify_numbers | Rectify (invalid) numbers.     | No                 |
 //! | quirk_missing_text    | Rectify missing `text` element.| No                 |
-//! | tokio                 | Tokio support.                 | No                 |
 //!
 //! ## Quirks
 //!
@@ -119,30 +118,21 @@
 //! imap-codec can't otherwise access their emails, we may add a `quirk_` feature to quickly resolve the problem.
 //! Of course, imap-codec should never violate the IMAP standard itself. So, we need to do this carefully.
 //!
-//! ## Tokio support
-//!
-//! The `tokio` feature unlocks an implementation of [tokio_util::codec].
-//! See the [tokio client] and [tokio server] demos.
-//!
-//! [imap-types]: imap_types
-//! [imap-types features]: ../imap_types/index.html#features
+//! [imap-types]: https://docs.rs/imap-types/latest/imap_types
+//! [imap-types features]: https://docs.rs/imap-types/latest/imap_types/#features
 //! [IMAP4rev1]: https://tools.ietf.org/html/rfc3501
 //! [parse_command]: https://github.com/duesee/imap-codec/blob/main/examples/parse_command.rs
-//! [tokio_util::codec]: https://docs.rs/tokio-util/latest/tokio_util/codec/index.html
-//! [tokio client]: https://github.com/duesee/imap-codec/tree/main/assets/demos/tokio-client
-//! [tokio server]: https://github.com/duesee/imap-codec/tree/main/assets/demos/tokio-server
 
 #![forbid(unsafe_code)]
 #![deny(missing_debug_implementations)]
 #![cfg_attr(docsrs, feature(doc_cfg))]
 
-pub mod auth;
-pub mod body;
-pub mod codec;
-pub mod command;
-pub mod core;
-pub mod datetime;
-pub mod envelope;
+mod auth;
+mod body;
+mod command;
+mod core;
+mod datetime;
+mod envelope;
 #[cfg(any(
     feature = "ext_compress",
     feature = "ext_condstore_qresync",
@@ -154,18 +144,18 @@ pub mod envelope;
     feature = "ext_unselect",
 ))]
 #[cfg_attr(docsrs, doc(cfg(feature = "ext_*")))]
-pub mod extensions;
-pub mod fetch;
-pub mod flag;
-pub mod mailbox;
-pub mod response;
-pub mod search;
-pub mod section;
-pub mod sequence;
-pub mod status;
+mod extensions;
+mod fetch;
+mod flag;
+mod mailbox;
+mod response;
+mod search;
+mod sequence;
+mod status;
 #[cfg(test)]
 mod testing;
-#[cfg(feature = "tokio")]
-#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))]
-pub mod tokio;
-pub use imap_types::{secret, state, utils};
+
+pub mod codec;
+
+// Re-export.
+pub use imap_types;
diff --git a/imap-codec/src/mailbox.rs b/imap-codec/src/mailbox.rs
index f5d45a34..ad32b274 100644
--- a/imap-codec/src/mailbox.rs
+++ b/imap-codec/src/mailbox.rs
@@ -1,8 +1,11 @@
-//! Please refer to [`imap_types::mailbox`].
-
 use abnf_core::streaming::{dquote, sp};
-/// Re-export everything from imap-types.
-pub use imap_types::mailbox::*;
+use imap_types::{
+    core::QuotedChar,
+    flag::FlagNameAttribute,
+    mailbox::{ListCharString, ListMailbox, Mailbox},
+    response::Data,
+    utils::indicators::is_list_char,
+};
 use nom::{
     branch::alt,
     bytes::streaming::{tag, tag_no_case, take_while1},
@@ -15,11 +18,9 @@ use nom::{
 use crate::extensions::quota::{quota_response, quotaroot_response};
 use crate::{
     codec::IMAPResult,
-    core::{astring, nil, number, nz_number, quoted_char, string, QuotedChar},
-    flag::{flag_list, mbx_list_flags, FlagNameAttribute},
-    response::Data,
+    core::{astring, nil, number, nz_number, quoted_char, string},
+    flag::{flag_list, mbx_list_flags},
     status::status_att_list,
-    utils::indicators::is_list_char,
 };
 
 /// `list-mailbox = 1*list-char / string`
diff --git a/imap-codec/src/response.rs b/imap-codec/src/response.rs
index a07e905a..263f15cc 100644
--- a/imap-codec/src/response.rs
+++ b/imap-codec/src/response.rs
@@ -1,5 +1,3 @@
-//! Please refer to [`imap_types::response`].
-
 use std::str::from_utf8;
 
 #[cfg(not(feature = "quirk_crlf_relaxed"))]
@@ -8,8 +6,12 @@ use abnf_core::streaming::crlf;
 use abnf_core::streaming::crlf_relaxed as crlf;
 use abnf_core::streaming::sp;
 use base64::{engine::general_purpose::STANDARD as _base64, Engine};
-/// Re-export everything from imap-types.
-pub use imap_types::response::*;
+use imap_types::{
+    core::{NonEmptyVec, Text},
+    response::{
+        Capability, Code, CodeOther, Continue, Data, Greeting, GreetingKind, Response, Status,
+    },
+};
 #[cfg(feature = "quirk_missing_text")]
 use nom::combinator::peek;
 use nom::{
@@ -24,7 +26,7 @@ use nom::{
 use crate::extensions::enable::enable_data;
 use crate::{
     codec::IMAPResult,
-    core::{atom, charset, nz_number, tag_imap, text, NonEmptyVec, Text},
+    core::{atom, charset, nz_number, tag_imap, text},
     fetch::msg_att,
     flag::flag_perm,
     mailbox::mailbox_data,
@@ -410,18 +412,18 @@ pub(crate) fn message_data(input: &[u8]) -> IMAPResult<&[u8], Data> {
 mod tests {
     use std::num::NonZeroU32;
 
-    use super::*;
-    use crate::{
+    use imap_types::{
         body::{
             BasicFields, Body, BodyExtension, BodyStructure, Disposition, Language, Location,
             SinglePartExtensionData, SpecificFields,
         },
         core::{IString, NString, QuotedChar, Tag},
         flag::FlagNameAttribute,
-        testing::{
-            kat_inverse_continue, kat_inverse_greeting, kat_inverse_response,
-            known_answer_test_encode,
-        },
+    };
+
+    use super::*;
+    use crate::testing::{
+        kat_inverse_continue, kat_inverse_greeting, kat_inverse_response, known_answer_test_encode,
     };
 
     #[test]
diff --git a/imap-codec/src/search.rs b/imap-codec/src/search.rs
index d61d4341..ab77d7f7 100644
--- a/imap-codec/src/search.rs
+++ b/imap-codec/src/search.rs
@@ -1,8 +1,5 @@
-//! Please refer to [`imap_types::search`].
-
 use abnf_core::streaming::sp;
-/// Re-export everything from imap-types.
-pub use imap_types::search::*;
+use imap_types::{command::CommandBody, core::NonEmptyVec, search::SearchKey};
 use nom::{
     branch::alt,
     bytes::streaming::{tag, tag_no_case},
@@ -13,10 +10,9 @@ use nom::{
 
 use crate::{
     codec::{IMAPErrorKind, IMAPParseError, IMAPResult},
-    command::CommandBody,
-    core::{astring, atom, charset, number, NonEmptyVec},
+    core::{astring, atom, charset, number},
     datetime::date,
-    section::header_fld_name,
+    fetch::header_fld_name,
     sequence::sequence_set,
 };
 
@@ -227,17 +223,18 @@ fn search_key_limited<'a>(
 
 #[cfg(test)]
 mod tests {
-    use super::*;
-    use crate::{
+    use imap_types::{
         core::{AString, Atom},
         datetime::NaiveDate,
         sequence::{Sequence, SequenceSet},
-        testing::known_answer_test_encode,
     };
 
+    use super::*;
+    use crate::testing::known_answer_test_encode;
+
     #[test]
     fn test_parse_search() {
-        use crate::{
+        use imap_types::{
             search::SearchKey::*,
             sequence::{SeqOrUid::Value, Sequence::*, SequenceSet as SequenceSetData},
         };
diff --git a/imap-codec/src/section.rs b/imap-codec/src/section.rs
deleted file mode 100644
index 3169430d..00000000
--- a/imap-codec/src/section.rs
+++ /dev/null
@@ -1,172 +0,0 @@
-//! Please refer to [`imap_types::section`].
-
-use std::num::NonZeroU32;
-
-use abnf_core::streaming::sp;
-/// Re-export everything from imap-types.
-pub use imap_types::section::*;
-use nom::{
-    branch::alt,
-    bytes::streaming::{tag, tag_no_case},
-    combinator::{map, opt, value},
-    multi::separated_list1,
-    sequence::{delimited, tuple},
-};
-
-use crate::{
-    codec::IMAPResult,
-    core::{astring, nz_number, AString, NonEmptyVec},
-};
-
-/// `section = "[" [section-spec] "]"`
-pub(crate) fn section(input: &[u8]) -> IMAPResult<&[u8], Option<Section>> {
-    delimited(tag(b"["), opt(section_spec), tag(b"]"))(input)
-}
-
-/// `section-spec = section-msgtext / (section-part ["." section-text])`
-pub(crate) fn section_spec(input: &[u8]) -> IMAPResult<&[u8], Section> {
-    alt((
-        map(section_msgtext, |part_specifier| match part_specifier {
-            PartSpecifier::PartNumber(_) => unreachable!(),
-            PartSpecifier::Header => Section::Header(None),
-            PartSpecifier::HeaderFields(fields) => Section::HeaderFields(None, fields),
-            PartSpecifier::HeaderFieldsNot(fields) => Section::HeaderFieldsNot(None, fields),
-            PartSpecifier::Text => Section::Text(None),
-            PartSpecifier::Mime => unreachable!(),
-        }),
-        map(
-            tuple((section_part, opt(tuple((tag(b"."), section_text))))),
-            |(part_number, maybe_part_specifier)| {
-                if let Some((_, part_specifier)) = maybe_part_specifier {
-                    match part_specifier {
-                        PartSpecifier::PartNumber(_) => unreachable!(),
-                        PartSpecifier::Header => Section::Header(Some(Part(part_number))),
-                        PartSpecifier::HeaderFields(fields) => {
-                            Section::HeaderFields(Some(Part(part_number)), fields)
-                        }
-                        PartSpecifier::HeaderFieldsNot(fields) => {
-                            Section::HeaderFieldsNot(Some(Part(part_number)), fields)
-                        }
-                        PartSpecifier::Text => Section::Text(Some(Part(part_number))),
-                        PartSpecifier::Mime => Section::Mime(Part(part_number)),
-                    }
-                } else {
-                    Section::Part(Part(part_number))
-                }
-            },
-        ),
-    ))(input)
-}
-
-/// `section-msgtext = "HEADER" / "HEADER.FIELDS" [".NOT"] SP header-list / "TEXT"`
-///
-/// Top-level or MESSAGE/RFC822 part
-pub(crate) fn section_msgtext(input: &[u8]) -> IMAPResult<&[u8], PartSpecifier> {
-    alt((
-        map(
-            tuple((tag_no_case(b"HEADER.FIELDS.NOT"), sp, header_list)),
-            |(_, _, header_list)| PartSpecifier::HeaderFieldsNot(header_list),
-        ),
-        map(
-            tuple((tag_no_case(b"HEADER.FIELDS"), sp, header_list)),
-            |(_, _, header_list)| PartSpecifier::HeaderFields(header_list),
-        ),
-        value(PartSpecifier::Header, tag_no_case(b"HEADER")),
-        value(PartSpecifier::Text, tag_no_case(b"TEXT")),
-    ))(input)
-}
-
-#[inline]
-/// `section-part = nz-number *("." nz-number)`
-///
-/// Body part nesting
-pub(crate) fn section_part(input: &[u8]) -> IMAPResult<&[u8], NonEmptyVec<NonZeroU32>> {
-    map(
-        separated_list1(tag(b"."), nz_number),
-        NonEmptyVec::unvalidated,
-    )(input)
-}
-
-/// `section-text = section-msgtext / "MIME"`
-///
-/// Text other than actual body part (headers, etc.)
-pub(crate) fn section_text(input: &[u8]) -> IMAPResult<&[u8], PartSpecifier> {
-    alt((
-        section_msgtext,
-        value(PartSpecifier::Mime, tag_no_case(b"MIME")),
-    ))(input)
-}
-
-/// `header-list = "(" header-fld-name *(SP header-fld-name) ")"`
-pub(crate) fn header_list(input: &[u8]) -> IMAPResult<&[u8], NonEmptyVec<AString>> {
-    map(
-        delimited(tag(b"("), separated_list1(sp, header_fld_name), tag(b")")),
-        NonEmptyVec::unvalidated,
-    )(input)
-}
-
-#[inline]
-/// `header-fld-name = astring`
-pub(crate) fn header_fld_name(input: &[u8]) -> IMAPResult<&[u8], AString> {
-    astring(input)
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::testing::known_answer_test_encode;
-
-    #[test]
-    fn test_encode_section() {
-        let tests = [
-            (
-                Section::Part(Part(NonEmptyVec::from(NonZeroU32::try_from(1).unwrap()))),
-                b"1".as_ref(),
-            ),
-            (Section::Header(None), b"HEADER"),
-            (
-                Section::Header(Some(Part(NonEmptyVec::from(
-                    NonZeroU32::try_from(1).unwrap(),
-                )))),
-                b"1.HEADER",
-            ),
-            (
-                Section::HeaderFields(None, NonEmptyVec::from(AString::try_from("").unwrap())),
-                b"HEADER.FIELDS (\"\")",
-            ),
-            (
-                Section::HeaderFields(
-                    Some(Part(NonEmptyVec::from(NonZeroU32::try_from(1).unwrap()))),
-                    NonEmptyVec::from(AString::try_from("").unwrap()),
-                ),
-                b"1.HEADER.FIELDS (\"\")",
-            ),
-            (
-                Section::HeaderFieldsNot(None, NonEmptyVec::from(AString::try_from("").unwrap())),
-                b"HEADER.FIELDS.NOT (\"\")",
-            ),
-            (
-                Section::HeaderFieldsNot(
-                    Some(Part(NonEmptyVec::from(NonZeroU32::try_from(1).unwrap()))),
-                    NonEmptyVec::from(AString::try_from("").unwrap()),
-                ),
-                b"1.HEADER.FIELDS.NOT (\"\")",
-            ),
-            (Section::Text(None), b"TEXT"),
-            (
-                Section::Text(Some(Part(NonEmptyVec::from(
-                    NonZeroU32::try_from(1).unwrap(),
-                )))),
-                b"1.TEXT",
-            ),
-            (
-                Section::Mime(Part(NonEmptyVec::from(NonZeroU32::try_from(1).unwrap()))),
-                b"1.MIME",
-            ),
-        ];
-
-        for test in tests {
-            known_answer_test_encode(test)
-        }
-    }
-}
diff --git a/imap-codec/src/sequence.rs b/imap-codec/src/sequence.rs
index 800a1ede..9965c1f8 100644
--- a/imap-codec/src/sequence.rs
+++ b/imap-codec/src/sequence.rs
@@ -1,7 +1,7 @@
-//! Please refer to [`imap_types::sequence`].
-
-/// Re-export everything from imap-types.
-pub use imap_types::sequence::*;
+use imap_types::{
+    core::NonEmptyVec,
+    sequence::{SeqOrUid, Sequence, SequenceSet},
+};
 use nom::{
     branch::alt,
     bytes::streaming::tag,
@@ -10,10 +10,7 @@ use nom::{
     sequence::tuple,
 };
 
-use crate::{
-    codec::IMAPResult,
-    core::{nz_number, NonEmptyVec},
-};
+use crate::{codec::IMAPResult, core::nz_number};
 
 /// `sequence-set = (seq-number / seq-range) ["," sequence-set]`
 ///
diff --git a/imap-codec/src/status.rs b/imap-codec/src/status.rs
index cebcf889..dca56add 100644
--- a/imap-codec/src/status.rs
+++ b/imap-codec/src/status.rs
@@ -1,8 +1,5 @@
-//! Please refer to [`imap_types::status`].
-
 use abnf_core::streaming::sp;
-/// Re-export everything from imap-types.
-pub use imap_types::status::*;
+use imap_types::status::{StatusDataItem, StatusDataItemName};
 use nom::{
     branch::alt,
     bytes::streaming::tag_no_case,
diff --git a/imap-types/Cargo.toml b/imap-types/Cargo.toml
index f3deb570..cdf33337 100644
--- a/imap-types/Cargo.toml
+++ b/imap-types/Cargo.toml
@@ -3,7 +3,7 @@ name = "imap-types"
 description = "Misuse-resistant data structures for IMAP"
 keywords = ["email", "imap", "types"]
 categories = ["email", "data-structures", "network-programming"]
-version = "0.10.0"
+version = "0.11.0"
 authors = ["Damian Poddebniak <poddebniak@mailbox.org>"]
 repository = "https://github.com/duesee/imap-codec"
 license = "MIT OR Apache-2.0"
diff --git a/imap-types/README.md b/imap-types/README.md
index 8cf15f46..259e976b 100644
--- a/imap-types/README.md
+++ b/imap-types/README.md
@@ -1,6 +1,6 @@
-# Misuse-resistant IMAP types
+# imap-types
 
-This crate provides a complete set of well-designed types for the [IMAP4rev1] protocol and various [extensions].
+This crate provides a complete set of well-designed, misuse-resistant types for the [IMAP4rev1] protocol and various [extensions].
 Notably, it does *not* provide parsers, nor serializers, but tries to become the "standard library" for IMAP in Rust that is
 useful for a broad range of crates.
 
@@ -121,6 +121,7 @@ This crate is dual-licensed under Apache 2.0 and MIT terms.
 
 [IMAP4rev1]: https://datatracker.ietf.org/doc/html/rfc3501
 [extensions]: https://docs.rs/imap-codec/latest/imap_codec/#features
-[imap-codec]: https://github.com/duesee/imap-codec
+[imap-codec]: https://docs.rs/imap-types/latest/imap_codec/
 [cargo fuzz]: https://github.com/rust-fuzz/cargo-fuzz
 [core]: https://docs.rs/imap-types/latest/imap_types/core/index.html
+
diff --git a/imap-types/fuzz/Cargo.toml b/imap-types/fuzz/Cargo.toml
index b4086b30..4e707990 100644
--- a/imap-types/fuzz/Cargo.toml
+++ b/imap-types/fuzz/Cargo.toml
@@ -47,9 +47,8 @@ ext = [
 debug = []
 
 [dependencies]
-bounded-static = "0.5.0"
 libfuzzer-sys = "0.4"
-imap-types = { path = "..", features = ["arbitrary", "bounded-static"] }
+imap-types = { path = "..", default-features = false, features = ["arbitrary", "bounded-static", "unvalidated"] }
 
 [[bin]]
 name = "into_static"
diff --git a/imap-types/fuzz/fuzz_targets/into_static.rs b/imap-types/fuzz/fuzz_targets/into_static.rs
index cd77f707..0a5ba179 100644
--- a/imap-types/fuzz/fuzz_targets/into_static.rs
+++ b/imap-types/fuzz/fuzz_targets/into_static.rs
@@ -1,11 +1,17 @@
 #![no_main]
 
-use bounded_static::IntoBoundedStatic;
-use imap_types::{command::Command, response::Response};
+use imap_types::{
+    bounded_static::IntoBoundedStatic,
+    command::Command,
+    response::{Greeting, Response},
+};
 use libfuzzer_sys::fuzz_target;
 
-fuzz_target!(|tuple: (Command, Response)| {
-    let (cmd, rsp) = tuple;
+fuzz_target!(|tuple: (Greeting, Command, Response)| {
+    let (grt, cmd, rsp) = tuple;
+
+    let got = grt.clone().into_static();
+    assert_eq!(grt, got);
 
     let got = cmd.clone().into_static();
     assert_eq!(cmd, got);
diff --git a/imap-types/fuzz/fuzz_targets/to_static.rs b/imap-types/fuzz/fuzz_targets/to_static.rs
index bae7a6c8..152c9c67 100644
--- a/imap-types/fuzz/fuzz_targets/to_static.rs
+++ b/imap-types/fuzz/fuzz_targets/to_static.rs
@@ -1,11 +1,17 @@
 #![no_main]
 
-use bounded_static::ToBoundedStatic;
-use imap_types::{command::Command, response::Response};
+use imap_types::{
+    bounded_static::ToBoundedStatic,
+    command::Command,
+    response::{Greeting, Response},
+};
 use libfuzzer_sys::fuzz_target;
 
-fuzz_target!(|tuple: (Command, Response)| {
-    let (cmd, rsp) = tuple;
+fuzz_target!(|tuple: (Greeting, Command, Response)| {
+    let (grt, cmd, rsp) = tuple;
+
+    let got = grt.to_static();
+    assert_eq!(grt, got);
 
     let got = cmd.to_static();
     assert_eq!(cmd, got);
diff --git a/imap-types/src/auth.rs b/imap-types/src/auth.rs
index ffda1457..ad97a081 100644
--- a/imap-types/src/auth.rs
+++ b/imap-types/src/auth.rs
@@ -1,3 +1,5 @@
+//! Authentication-related types.
+
 use std::{
     borrow::Cow,
     fmt::{Display, Formatter},
@@ -135,6 +137,9 @@ enum Inner<'a> {
     Other(Atom<'a>),
 }
 
+/// Data line used, e.g., during AUTHENTICATE.
+///
+/// Holds the raw binary data, i.e., a `Vec<u8>`, *not* the BASE64 string.
 #[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
 #[cfg_attr(feature = "bounded-static", derive(ToStatic))]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
diff --git a/imap-types/src/body.rs b/imap-types/src/body.rs
index 327166a9..e4028cf5 100644
--- a/imap-types/src/body.rs
+++ b/imap-types/src/body.rs
@@ -1,3 +1,5 @@
+//! Body(structure)-related types.
+
 #[cfg(feature = "arbitrary")]
 use arbitrary::Arbitrary;
 #[cfg(feature = "bounded-static")]
@@ -10,6 +12,7 @@ use crate::{
     envelope::Envelope,
 };
 
+/// Inner part of [`BodyStructure`].
 #[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
 #[cfg_attr(feature = "bounded-static", derive(ToStatic))]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@@ -21,7 +24,7 @@ pub struct Body<'a> {
     pub specific: SpecificFields<'a>,
 }
 
-/// The basic fields of a non-multipart body part.
+/// Basic fields of a non-multipart body part.
 #[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
 #[cfg_attr(feature = "bounded-static", derive(ToStatic))]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@@ -46,6 +49,7 @@ pub struct BasicFields<'a> {
     pub size: u32,
 }
 
+/// Specific fields of a non-multipart body part.
 #[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
 #[cfg_attr(feature = "bounded-static", derive(ToStatic))]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@@ -156,15 +160,15 @@ pub enum SpecificFields<'a> {
     ///     | md5
     /// )
     /// ```
-    ///
-    /// A body type of type TEXT contains, immediately after the basic fields,
     Text {
+        /// Subtype.
         subtype: IString<'a>,
-        /// the size of the body in text lines.
+        /// Size of the body in text lines.
         number_of_lines: u32,
     },
 }
 
+/// The BODY(STRUCTURE).
 #[cfg_attr(feature = "bounded-static", derive(ToStatic))]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -176,6 +180,7 @@ pub enum BodyStructure<'a> {
     /// ("TEXT" "PLAIN" ("CHARSET" "US-ASCII") NIL NIL "7BIT" 2279 48)
     /// ```
     Single {
+        /// Inner body.
         body: Body<'a>,
         /// Extension data
         ///
@@ -237,8 +242,11 @@ pub enum BodyStructure<'a> {
     /// )
     /// ```
     Multi {
+        /// Inner bodies.
         bodies: NonEmptyVec<BodyStructure<'a>>,
+        /// Subtype.
         subtype: IString<'a>,
+        /// Extension data.
         extension_data: Option<MultiPartExtensionData<'a>>,
     },
 }
@@ -284,6 +292,7 @@ pub struct MultiPartExtensionData<'a> {
     pub tail: Option<Disposition<'a>>,
 }
 
+/// Helper to enforce correct usage of [`SinglePartExtensionData`] and [`MultiPartExtensionData`].
 #[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
 #[cfg_attr(feature = "bounded-static", derive(ToStatic))]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@@ -298,6 +307,7 @@ pub struct Disposition<'a> {
     pub tail: Option<Language<'a>>,
 }
 
+/// Helper to enforce correct usage of [`SinglePartExtensionData`] and [`MultiPartExtensionData`].
 #[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
 #[cfg_attr(feature = "bounded-static", derive(ToStatic))]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@@ -311,6 +321,7 @@ pub struct Language<'a> {
     pub tail: Option<Location<'a>>,
 }
 
+/// Helper to enforce correct usage of [`SinglePartExtensionData`] and [`MultiPartExtensionData`].
 #[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
 #[cfg_attr(feature = "bounded-static", derive(ToStatic))]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@@ -323,11 +334,15 @@ pub struct Location<'a> {
     pub extensions: Vec<BodyExtension<'a>>,
 }
 
+/// Helper to enforce correct usage of [`SinglePartExtensionData`] and [`MultiPartExtensionData`].
 #[cfg_attr(feature = "bounded-static", derive(ToStatic))]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub enum BodyExtension<'a> {
+    /// NString.
     NString(NString<'a>),
+    /// Number.
     Number(u32),
+    /// List.
     List(NonEmptyVec<BodyExtension<'a>>),
 }
diff --git a/imap-types/src/command.rs b/imap-types/src/command.rs
index b50c4b6e..46b46f4e 100644
--- a/imap-types/src/command.rs
+++ b/imap-types/src/command.rs
@@ -31,16 +31,20 @@ use crate::{
 #[cfg(feature = "ext_enable")]
 use crate::{core::NonEmptyVec, extensions::enable::CapabilityEnable};
 
+/// Command.
 #[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
 #[cfg_attr(feature = "bounded-static", derive(ToStatic))]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct Command<'a> {
+    /// Tag.
     pub tag: Tag<'a>,
+    /// Body, e.g., CAPABILITY, LOGIN, SELECT, etc.
     pub body: CommandBody<'a>,
 }
 
 impl<'a> Command<'a> {
+    /// Create a new command.
     pub fn new<T>(tag: T, body: CommandBody<'a>) -> Result<Self, T::Error>
     where
         T: TryInto<Tag<'a>>,
@@ -51,11 +55,15 @@ impl<'a> Command<'a> {
         })
     }
 
+    /// Get the command name.
     pub fn name(&self) -> &'static str {
         self.body.name()
     }
 }
 
+/// Command body.
+///
+/// This enum is used to encode all the different commands.
 #[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
 #[cfg_attr(feature = "bounded-static", derive(ToStatic))]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@@ -249,8 +257,13 @@ pub enum CommandBody<'a> {
     /// during the authentication exchange is interpreted by the server as
     /// the user name whose privileges the client is requesting.
     Authenticate {
+        /// Authentication mechanism.
         mechanism: AuthMechanism<'a>,
-        /// Already base64-decoded
+        /// Initial response (if any).
+        ///
+        /// This type holds the raw binary data, i.e., a `Vec<u8>`, *not* the BASE64 string.
+        ///
+        /// Note: Use this only when the server advertised the `SASL-IR` capability.
         #[cfg(feature = "ext_sasl_ir")]
         #[cfg_attr(docsrs, doc(cfg(feature = "ext_sasl_ir")))]
         initial_response: Option<Secret<Cow<'a, [u8]>>>,
@@ -295,7 +308,9 @@ pub enum CommandBody<'a> {
     ///   implementation MUST NOT send a LOGIN command if the
     ///   LOGINDISABLED capability is advertised.
     Login {
+        /// Username.
         username: AString<'a>,
+        /// Password.
         password: Secret<AString<'a>>,
     },
 
@@ -373,8 +388,14 @@ pub enum CommandBody<'a> {
     /// per-user (as opposed to global) basis.  Netnews messages marked in
     /// a server-based .newsrc file are an example of such per-user
     /// permanent state that can be modified with read-only mailboxes.
-    Select { mailbox: Mailbox<'a> },
+    Select {
+        /// Mailbox.
+        mailbox: Mailbox<'a>,
+    },
 
+    /// Unselect a mailbox.
+    ///
+    /// This should bring the client back to the AUTHENTICATED state.
     #[cfg(feature = "ext_unselect")]
     #[cfg_attr(docsrs, doc(cfg(feature = "ext_unselect")))]
     Unselect,
@@ -398,7 +419,10 @@ pub enum CommandBody<'a> {
     ///
     /// The text of the tagged OK response to the EXAMINE command MUST
     /// begin with the "[READ-ONLY]" response code.
-    Examine { mailbox: Mailbox<'a> },
+    Examine {
+        /// Mailbox.
+        mailbox: Mailbox<'a>,
+    },
 
     /// ### 6.3.3.  CREATE Command
     ///
@@ -443,7 +467,10 @@ pub enum CommandBody<'a> {
     ///   named "owatagusiam" with a member called "blurdybloop" is
     ///   created.  Otherwise, two mailboxes at the same hierarchy
     ///   level are created.
-    Create { mailbox: Mailbox<'a> },
+    Create {
+        /// Mailbox.
+        mailbox: Mailbox<'a>,
+    },
 
     /// 6.3.4.  DELETE Command
     ///
@@ -477,7 +504,10 @@ pub enum CommandBody<'a> {
     /// incarnation, UNLESS the new incarnation has a different unique
     /// identifier validity value.  See the description of the UID command
     /// for more detail.
-    Delete { mailbox: Mailbox<'a> },
+    Delete {
+        /// Mailbox.
+        mailbox: Mailbox<'a>,
+    },
 
     /// 6.3.5.  RENAME Command
     ///
@@ -519,7 +549,12 @@ pub enum CommandBody<'a> {
     /// leaving INBOX empty.  If the server implementation supports
     /// inferior hierarchical names of INBOX, these are unaffected by a
     /// rename of INBOX.
-    Rename { from: Mailbox<'a>, to: Mailbox<'a> },
+    Rename {
+        /// Current name.
+        from: Mailbox<'a>,
+        /// New name.
+        to: Mailbox<'a>,
+    },
 
     /// ### 6.3.6.  SUBSCRIBE Command
     ///
@@ -545,7 +580,10 @@ pub enum CommandBody<'a> {
     ///   name (e.g., "system-alerts") after its contents expire,
     ///   with the intention of recreating it when new contents
     ///   are appropriate.
-    Subscribe { mailbox: Mailbox<'a> },
+    Subscribe {
+        /// Mailbox.
+        mailbox: Mailbox<'a>,
+    },
 
     /// 6.3.7.  UNSUBSCRIBE Command
     ///
@@ -559,7 +597,10 @@ pub enum CommandBody<'a> {
     /// the server's set of "active" or "subscribed" mailboxes as returned
     /// by the LSUB command.  This command returns a tagged OK response
     /// only if the unsubscription is successful.
-    Unsubscribe { mailbox: Mailbox<'a> },
+    Unsubscribe {
+        /// Mailbox.
+        mailbox: Mailbox<'a>,
+    },
 
     /// ### 6.3.8.  LIST Command
     ///
@@ -693,7 +734,9 @@ pub enum CommandBody<'a> {
     /// failure; it is not relevant whether the user's real INBOX resides
     /// on this or some other server.
     List {
+        /// Reference.
         reference: Mailbox<'a>,
+        /// Mailbox (wildcard).
         mailbox_wildcard: ListMailbox<'a>,
     },
 
@@ -727,7 +770,9 @@ pub enum CommandBody<'a> {
     /// from the subscription list even if a mailbox by that name no
     /// longer exists.
     Lsub {
+        /// Reference.
         reference: Mailbox<'a>,
+        /// Mailbox (wildcard).
         mailbox_wildcard: ListMailbox<'a>,
     },
 
@@ -778,7 +823,9 @@ pub enum CommandBody<'a> {
     ///   issue many consecutive STATUS commands and obtain
     ///   reasonable performance.
     Status {
+        /// Mailbox.
         mailbox: Mailbox<'a>,
+        /// Status data items.
         item_names: Cow<'a, [StatusDataItemName]>,
     },
 
@@ -839,9 +886,13 @@ pub enum CommandBody<'a> {
     ///   because it does not provide a mechanism to transfer \[SMTP\]
     ///   envelope information.
     Append {
+        /// Mailbox.
         mailbox: Mailbox<'a>,
+        /// Flags.
         flags: Vec<Flag<'a>>,
+        /// Datetime.
         date: Option<DateTime>,
+        /// Message to append.
         message: Literal<'a>,
     },
 
@@ -966,8 +1017,11 @@ pub enum CommandBody<'a> {
     /// "XXXXXX" is a placeholder for what would be 6 octets of
     /// 8-bit data in an actual transaction.
     Search {
+        /// Charset.
         charset: Option<Charset<'a>>,
+        /// Criteria.
         criteria: SearchKey<'a>,
+        /// Use UID variant.
         uid: bool,
     },
 
@@ -996,8 +1050,11 @@ pub enum CommandBody<'a> {
     ///   message when it already knows the envelope, it can
     ///   safely ignore the newly transmitted envelope.
     Fetch {
+        /// Set of messages.
         sequence_set: SequenceSet,
+        /// Message data items (or a macro).
         macro_or_item_names: MacroOrMessageDataItemNames<'a>,
+        /// Use UID variant.
         uid: bool,
     },
 
@@ -1052,10 +1109,15 @@ pub enum CommandBody<'a> {
     /// -FLAGS.SILENT \<flag list\>
     ///    Equivalent to -FLAGS, but without returning a new value.
     Store {
+        /// Set of messages.
         sequence_set: SequenceSet,
+        /// Kind of storage, i.e., replace, add, or remove.
         kind: StoreType,
+        /// Kind of response, i.e., answer or silent.
         response: StoreResponse,
+        /// Flags.
         flags: Vec<Flag<'a>>, // FIXME(misuse): must not accept "\*" or "\Recent"
+        /// Use UID variant.
         uid: bool,
     },
 
@@ -1086,8 +1148,11 @@ pub enum CommandBody<'a> {
     /// implementations MUST restore the destination mailbox to its state
     /// before the COPY attempt.
     Copy {
+        /// Set of messages.
         sequence_set: SequenceSet,
+        /// Destination mailbox.
         mailbox: Mailbox<'a>,
+        /// Use UID variant.
         uid: bool,
     },
 
@@ -1177,19 +1242,26 @@ pub enum CommandBody<'a> {
     // send any such untagged responses, unless the client requested it
     // by issuing the associated experimental command.
     //X,
+    /// IDLE command.
     #[cfg(feature = "ext_idle")]
     #[cfg_attr(docsrs, doc(cfg(feature = "ext_idle")))]
     Idle,
 
+    /// ENABLE command.
     #[cfg(feature = "ext_enable")]
     #[cfg_attr(docsrs, doc(cfg(feature = "ext_enable")))]
     Enable {
+        /// Capabilities to enable.
         capabilities: NonEmptyVec<CapabilityEnable<'a>>,
     },
 
+    /// COMPRESS command.
     #[cfg(feature = "ext_compress")]
     #[cfg_attr(docsrs, doc(cfg(feature = "ext_compress")))]
-    Compress { algorithm: CompressionAlgorithm },
+    Compress {
+        /// Compression algorithm.
+        algorithm: CompressionAlgorithm,
+    },
 
     /// Takes the name of a quota root and returns the quota root's resource usage and limits in an untagged QUOTA response.
     ///
@@ -1302,16 +1374,21 @@ pub enum CommandBody<'a> {
         quotas: Vec<QuotaSet<'a>>,
     },
 
+    /// MOVE command.
     #[cfg(feature = "ext_move")]
     #[cfg_attr(docsrs, doc(cfg(feature = "ext_move")))]
     Move {
+        /// Set of messages.
         sequence_set: SequenceSet,
+        /// Destination mailbox.
         mailbox: Mailbox<'a>,
+        /// Use UID variant.
         uid: bool,
     },
 }
 
 impl<'a> CommandBody<'a> {
+    /// Prepend a tag to finalize the command body to a command.
     pub fn tag<T>(self, tag: T) -> Result<Command<'a>, T::Error>
     where
         T: TryInto<Tag<'a>>,
@@ -1324,6 +1401,7 @@ impl<'a> CommandBody<'a> {
 
     // ----- Constructors -----
 
+    /// Construct an AUTHENTICATE command.
     pub fn authenticate(mechanism: AuthMechanism<'a>) -> Self {
         #[cfg(feature = "ext_sasl_ir")]
         return CommandBody::Authenticate {
@@ -1335,6 +1413,9 @@ impl<'a> CommandBody<'a> {
         return CommandBody::Authenticate { mechanism };
     }
 
+    /// Construct an AUTHENTICATE command (with an initial response, SASL-IR).
+    ///
+    /// Note: Use this only when the server advertised the `SASL-IR` capability.
     #[cfg(feature = "ext_sasl_ir")]
     #[cfg_attr(docsrs, doc(cfg(feature = "ext_sasl_ir")))]
     pub fn authenticate_with_ir<I>(mechanism: AuthMechanism<'a>, initial_response: I) -> Self
@@ -1347,6 +1428,7 @@ impl<'a> CommandBody<'a> {
         }
     }
 
+    /// Construct a LOGIN command.
     pub fn login<U, P>(username: U, password: P) -> Result<Self, LoginError<U::Error, P::Error>>
     where
         U: TryInto<AString<'a>>,
@@ -1358,6 +1440,7 @@ impl<'a> CommandBody<'a> {
         })
     }
 
+    /// Construct a SELECT command.
     pub fn select<M>(mailbox: M) -> Result<Self, M::Error>
     where
         M: TryInto<Mailbox<'a>>,
@@ -1367,6 +1450,7 @@ impl<'a> CommandBody<'a> {
         })
     }
 
+    /// Construct an EXAMINE command.
     pub fn examine<M>(mailbox: M) -> Result<Self, M::Error>
     where
         M: TryInto<Mailbox<'a>>,
@@ -1376,6 +1460,7 @@ impl<'a> CommandBody<'a> {
         })
     }
 
+    /// Construct a CREATE command.
     pub fn create<M>(mailbox: M) -> Result<Self, M::Error>
     where
         M: TryInto<Mailbox<'a>>,
@@ -1385,6 +1470,7 @@ impl<'a> CommandBody<'a> {
         })
     }
 
+    /// Construct a DELETE command.
     pub fn delete<M>(mailbox: M) -> Result<Self, M::Error>
     where
         M: TryInto<Mailbox<'a>>,
@@ -1394,6 +1480,7 @@ impl<'a> CommandBody<'a> {
         })
     }
 
+    /// Construct a RENAME command.
     pub fn rename<F, T>(mailbox: F, new_mailbox: T) -> Result<Self, RenameError<F::Error, T::Error>>
     where
         F: TryInto<Mailbox<'a>>,
@@ -1405,6 +1492,7 @@ impl<'a> CommandBody<'a> {
         })
     }
 
+    /// Construct a SUBSCRIBE command.
     pub fn subscribe<M>(mailbox: M) -> Result<Self, M::Error>
     where
         M: TryInto<Mailbox<'a>>,
@@ -1414,6 +1502,7 @@ impl<'a> CommandBody<'a> {
         })
     }
 
+    /// Construct an UNSUBSCRIBE command.
     pub fn unsubscribe<M>(mailbox: M) -> Result<Self, M::Error>
     where
         M: TryInto<Mailbox<'a>>,
@@ -1423,6 +1512,7 @@ impl<'a> CommandBody<'a> {
         })
     }
 
+    /// Construct a LIST command.
     pub fn list<A, B>(
         reference: A,
         mailbox_wildcard: B,
@@ -1437,6 +1527,7 @@ impl<'a> CommandBody<'a> {
         })
     }
 
+    /// Construct a LSUB command.
     pub fn lsub<A, B>(
         reference: A,
         mailbox_wildcard: B,
@@ -1451,6 +1542,7 @@ impl<'a> CommandBody<'a> {
         })
     }
 
+    /// Construct a STATUS command.
     pub fn status<M, I>(mailbox: M, item_names: I) -> Result<Self, M::Error>
     where
         M: TryInto<Mailbox<'a>>,
@@ -1464,6 +1556,7 @@ impl<'a> CommandBody<'a> {
         })
     }
 
+    /// Construct an APPEND command.
     pub fn append<M, D>(
         mailbox: M,
         flags: Vec<Flag<'a>>,
@@ -1482,6 +1575,7 @@ impl<'a> CommandBody<'a> {
         })
     }
 
+    /// Construct a SEARCH command.
     pub fn search(charset: Option<Charset<'a>>, criteria: SearchKey<'a>, uid: bool) -> Self {
         CommandBody::Search {
             charset,
@@ -1490,6 +1584,7 @@ impl<'a> CommandBody<'a> {
         }
     }
 
+    /// Construct a FETCH command.
     pub fn fetch<S, I>(sequence_set: S, macro_or_item_names: I, uid: bool) -> Result<Self, S::Error>
     where
         S: TryInto<SequenceSet>,
@@ -1504,6 +1599,7 @@ impl<'a> CommandBody<'a> {
         })
     }
 
+    /// Construct a STORE command.
     pub fn store<S>(
         sequence_set: S,
         kind: StoreType,
@@ -1525,6 +1621,7 @@ impl<'a> CommandBody<'a> {
         })
     }
 
+    /// Construct a COPY command.
     pub fn copy<S, M>(
         sequence_set: S,
         mailbox: M,
@@ -1541,6 +1638,7 @@ impl<'a> CommandBody<'a> {
         })
     }
 
+    /// Get the name of the command.
     pub fn name(&self) -> &'static str {
         match self {
             Self::Capability => "CAPABILITY",
@@ -1639,12 +1737,11 @@ mod tests {
         auth::AuthMechanism,
         core::{AString, Charset, IString, Literal},
         datetime::DateTime,
-        fetch::{Macro, MacroOrMessageDataItemNames, MessageDataItemName},
+        fetch::{Macro, MacroOrMessageDataItemNames, MessageDataItemName, Part, Section},
         flag::{Flag, StoreType},
         mailbox::{ListMailbox, Mailbox},
         search::SearchKey,
         secret::Secret,
-        section::{Part, Section},
         sequence::{SeqOrUid, Sequence, SequenceSet},
         status::StatusDataItemName,
     };
diff --git a/imap-types/src/datetime.rs b/imap-types/src/datetime.rs
index 03aef53e..186bb59d 100644
--- a/imap-types/src/datetime.rs
+++ b/imap-types/src/datetime.rs
@@ -1,3 +1,5 @@
+//! Date and time-related types.
+
 use std::fmt::{Debug, Formatter};
 
 #[cfg(feature = "bounded-static")]
diff --git a/imap-types/src/envelope.rs b/imap-types/src/envelope.rs
index 9c372c7b..5fe22424 100644
--- a/imap-types/src/envelope.rs
+++ b/imap-types/src/envelope.rs
@@ -1,3 +1,5 @@
+//! Envelope-related types.
+
 #[cfg(feature = "arbitrary")]
 use arbitrary::Arbitrary;
 #[cfg(feature = "bounded-static")]
diff --git a/imap-types/src/fetch.rs b/imap-types/src/fetch.rs
index 3d362608..a247df35 100644
--- a/imap-types/src/fetch.rs
+++ b/imap-types/src/fetch.rs
@@ -1,3 +1,5 @@
+//! Fetch-related types.
+
 use std::num::NonZeroU32;
 
 #[cfg(feature = "arbitrary")]
@@ -8,8 +10,11 @@ use bounded_static::ToStatic;
 use serde::{Deserialize, Serialize};
 
 use crate::{
-    body::BodyStructure, core::NString, datetime::DateTime, envelope::Envelope, flag::FlagFetch,
-    section::Section,
+    body::BodyStructure,
+    core::{AString, NString, NonEmptyVec},
+    datetime::DateTime,
+    envelope::Envelope,
+    flag::FlagFetch,
 };
 
 /// Shorthands for commonly-used message data items.
@@ -86,7 +91,7 @@ pub enum MessageDataItemName<'a> {
         ///
         /// An empty section specification refers to the entire message, including the header.
         ///
-        /// See [`crate::section::Section`] and [`crate::section::PartSpecifier`].
+        /// See [`crate::fetch::Section`] and [`crate::fetch::PartSpecifier`].
         ///
         /// Every message has at least one part number.  Non-[MIME-IMB]
         /// messages, and non-multipart [MIME-IMB] messages with no
@@ -338,3 +343,105 @@ pub enum MessageDataItem<'a> {
     /// ```
     Uid(NonZeroU32),
 }
+
+/// A part specifier is either a part number or one of the following:
+/// `HEADER`, `HEADER.FIELDS`, `HEADER.FIELDS.NOT`, `MIME`, and `TEXT`.
+///
+/// The HEADER, HEADER.FIELDS, and HEADER.FIELDS.NOT part
+/// specifiers refer to the [RFC-2822] header of the message or of
+/// an encapsulated [MIME-IMT] MESSAGE/RFC822 message.
+/// HEADER.FIELDS and HEADER.FIELDS.NOT are followed by a list of
+/// field-name (as defined in [RFC-2822]) names, and return a
+/// subset of the header.
+///
+/// The field-matching is case-insensitive but otherwise exact.
+/// Subsetting does not exclude the [RFC-2822] delimiting blank line between the header
+/// and the body; the blank line is included in all header fetches,
+/// except in the case of a message which has no body and no blank
+/// line.
+///
+/// The HEADER, HEADER.FIELDS, HEADER.FIELDS.NOT, and TEXT part
+/// specifiers can be the sole part specifier or can be prefixed by
+/// one or more numeric part specifiers, provided that the numeric
+/// part specifier refers to a part of type MESSAGE/RFC822.
+///
+/// Here is an example of a complex message with some of its part specifiers:
+///
+/// ```text
+/// HEADER     ([RFC-2822] header of the message)
+/// TEXT       ([RFC-2822] text body of the message) MULTIPART/MIXED
+/// 1          TEXT/PLAIN
+/// 2          APPLICATION/OCTET-STREAM
+/// 3          MESSAGE/RFC822
+/// 3.HEADER   ([RFC-2822] header of the message)
+/// 3.TEXT     ([RFC-2822] text body of the message) MULTIPART/MIXED
+/// 3.1        TEXT/PLAIN
+/// 3.2        APPLICATION/OCTET-STREAM
+/// 4          MULTIPART/MIXED
+/// 4.1        IMAGE/GIF
+/// 4.1.MIME   ([MIME-IMB] header for the IMAGE/GIF)
+/// 4.2        MESSAGE/RFC822
+/// 4.2.HEADER ([RFC-2822] header of the message)
+/// 4.2.TEXT   ([RFC-2822] text body of the message) MULTIPART/MIXED
+/// 4.2.1      TEXT/PLAIN
+/// 4.2.2      MULTIPART/ALTERNATIVE
+/// 4.2.2.1    TEXT/PLAIN
+/// 4.2.2.2    TEXT/RICHTEXT
+/// ```
+#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
+#[cfg_attr(feature = "bounded-static", derive(ToStatic))]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum Section<'a> {
+    Part(Part),
+
+    Header(Option<Part>),
+
+    /// The subset returned by HEADER.FIELDS contains only those header fields with a field-name that
+    /// matches one of the names in the list.
+    HeaderFields(Option<Part>, NonEmptyVec<AString<'a>>), // TODO: what if none matches?
+
+    /// Similarly, the subset returned by HEADER.FIELDS.NOT contains only the header fields
+    /// with a non-matching field-name.
+    HeaderFieldsNot(Option<Part>, NonEmptyVec<AString<'a>>), // TODO: what if none matches?
+
+    /// The TEXT part specifier refers to the text body of the message, omitting the [RFC-2822] header.
+    Text(Option<Part>),
+
+    /// The MIME part specifier MUST be prefixed by one or more numeric part specifiers
+    /// and refers to the [MIME-IMB] header for this part.
+    Mime(Part),
+}
+
+#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
+#[cfg_attr(feature = "bounded-static", derive(ToStatic))]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct Part(pub NonEmptyVec<NonZeroU32>);
+
+/// A part specifier is either a part number or one of the following:
+/// `HEADER`, `HEADER.FIELDS`, `HEADER.FIELDS.NOT`, `MIME`, and `TEXT`.
+///
+/// The HEADER, HEADER.FIELDS, and HEADER.FIELDS.NOT part
+/// specifiers refer to the [RFC-2822] header of the message or of
+/// an encapsulated [MIME-IMT] MESSAGE/RFC822 message.
+/// HEADER.FIELDS and HEADER.FIELDS.NOT are followed by a list of
+/// field-name (as defined in [RFC-2822]) names, and return a
+/// subset of the header.
+///
+/// The field-matching is case-insensitive but otherwise exact.
+/// Subsetting does not exclude the [RFC-2822] delimiting blank line between the header
+/// and the body; the blank line is included in all header fetches,
+/// except in the case of a message which has no body and no blank
+/// line.
+#[cfg_attr(feature = "bounded-static", derive(ToStatic))]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum PartSpecifier<'a> {
+    PartNumber(u32),
+    Header,
+    HeaderFields(NonEmptyVec<AString<'a>>),
+    HeaderFieldsNot(NonEmptyVec<AString<'a>>),
+    Mime,
+    Text,
+}
diff --git a/imap-types/src/flag.rs b/imap-types/src/flag.rs
index cdf063a8..4c19204b 100644
--- a/imap-types/src/flag.rs
+++ b/imap-types/src/flag.rs
@@ -1,4 +1,4 @@
-// ### 2.3.2. Flags Message Attribute
+//! Flag-related types.
 
 #[cfg(feature = "arbitrary")]
 use arbitrary::Arbitrary;
diff --git a/imap-types/src/lib.rs b/imap-types/src/lib.rs
index b89a0258..94ed232a 100644
--- a/imap-types/src/lib.rs
+++ b/imap-types/src/lib.rs
@@ -155,6 +155,8 @@
 
 #![forbid(unsafe_code)]
 #![deny(missing_debug_implementations)]
+// TODO(#313)
+// #![deny(missing_docs)]
 #![cfg_attr(docsrs, feature(doc_cfg))]
 
 #[cfg(feature = "arbitrary")]
@@ -182,8 +184,11 @@ pub mod mailbox;
 pub mod response;
 pub mod search;
 pub mod secret;
-pub mod section;
 pub mod sequence;
 pub mod state;
 pub mod status;
 pub mod utils;
+
+#[cfg(feature = "bounded-static")]
+#[cfg_attr(docsrs, doc(cfg(feature = "ext_*")))]
+pub use bounded_static;
diff --git a/imap-types/src/mailbox.rs b/imap-types/src/mailbox.rs
index fc884ac6..229b9d81 100644
--- a/imap-types/src/mailbox.rs
+++ b/imap-types/src/mailbox.rs
@@ -1,3 +1,5 @@
+//! Mailbox-related types.
+
 use std::{borrow::Cow, str::from_utf8};
 
 #[cfg(feature = "arbitrary")]
diff --git a/imap-types/src/search.rs b/imap-types/src/search.rs
index 43c2e245..05734671 100644
--- a/imap-types/src/search.rs
+++ b/imap-types/src/search.rs
@@ -1,3 +1,5 @@
+//! Search-related types.
+
 #[cfg(feature = "bounded-static")]
 use bounded_static::ToStatic;
 #[cfg(feature = "serde")]
diff --git a/imap-types/src/section.rs b/imap-types/src/section.rs
deleted file mode 100644
index 72384d8c..00000000
--- a/imap-types/src/section.rs
+++ /dev/null
@@ -1,112 +0,0 @@
-use std::num::NonZeroU32;
-
-#[cfg(feature = "arbitrary")]
-use arbitrary::Arbitrary;
-#[cfg(feature = "bounded-static")]
-use bounded_static::ToStatic;
-#[cfg(feature = "serde")]
-use serde::{Deserialize, Serialize};
-
-use crate::core::{AString, NonEmptyVec};
-
-/// A part specifier is either a part number or one of the following:
-/// `HEADER`, `HEADER.FIELDS`, `HEADER.FIELDS.NOT`, `MIME`, and `TEXT`.
-///
-/// The HEADER, HEADER.FIELDS, and HEADER.FIELDS.NOT part
-/// specifiers refer to the [RFC-2822] header of the message or of
-/// an encapsulated [MIME-IMT] MESSAGE/RFC822 message.
-/// HEADER.FIELDS and HEADER.FIELDS.NOT are followed by a list of
-/// field-name (as defined in [RFC-2822]) names, and return a
-/// subset of the header.
-///
-/// The field-matching is case-insensitive but otherwise exact.
-/// Subsetting does not exclude the [RFC-2822] delimiting blank line between the header
-/// and the body; the blank line is included in all header fetches,
-/// except in the case of a message which has no body and no blank
-/// line.
-///
-/// The HEADER, HEADER.FIELDS, HEADER.FIELDS.NOT, and TEXT part
-/// specifiers can be the sole part specifier or can be prefixed by
-/// one or more numeric part specifiers, provided that the numeric
-/// part specifier refers to a part of type MESSAGE/RFC822.
-///
-/// Here is an example of a complex message with some of its part specifiers:
-///
-/// ```text
-/// HEADER     ([RFC-2822] header of the message)
-/// TEXT       ([RFC-2822] text body of the message) MULTIPART/MIXED
-/// 1          TEXT/PLAIN
-/// 2          APPLICATION/OCTET-STREAM
-/// 3          MESSAGE/RFC822
-/// 3.HEADER   ([RFC-2822] header of the message)
-/// 3.TEXT     ([RFC-2822] text body of the message) MULTIPART/MIXED
-/// 3.1        TEXT/PLAIN
-/// 3.2        APPLICATION/OCTET-STREAM
-/// 4          MULTIPART/MIXED
-/// 4.1        IMAGE/GIF
-/// 4.1.MIME   ([MIME-IMB] header for the IMAGE/GIF)
-/// 4.2        MESSAGE/RFC822
-/// 4.2.HEADER ([RFC-2822] header of the message)
-/// 4.2.TEXT   ([RFC-2822] text body of the message) MULTIPART/MIXED
-/// 4.2.1      TEXT/PLAIN
-/// 4.2.2      MULTIPART/ALTERNATIVE
-/// 4.2.2.1    TEXT/PLAIN
-/// 4.2.2.2    TEXT/RICHTEXT
-/// ```
-#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
-#[cfg_attr(feature = "bounded-static", derive(ToStatic))]
-#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub enum Section<'a> {
-    Part(Part),
-
-    Header(Option<Part>),
-
-    /// The subset returned by HEADER.FIELDS contains only those header fields with a field-name that
-    /// matches one of the names in the list.
-    HeaderFields(Option<Part>, NonEmptyVec<AString<'a>>), // TODO: what if none matches?
-
-    /// Similarly, the subset returned by HEADER.FIELDS.NOT contains only the header fields
-    /// with a non-matching field-name.
-    HeaderFieldsNot(Option<Part>, NonEmptyVec<AString<'a>>), // TODO: what if none matches?
-
-    /// The TEXT part specifier refers to the text body of the message, omitting the [RFC-2822] header.
-    Text(Option<Part>),
-
-    /// The MIME part specifier MUST be prefixed by one or more numeric part specifiers
-    /// and refers to the [MIME-IMB] header for this part.
-    Mime(Part),
-}
-
-#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
-#[cfg_attr(feature = "bounded-static", derive(ToStatic))]
-#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct Part(pub NonEmptyVec<NonZeroU32>);
-
-/// A part specifier is either a part number or one of the following:
-/// `HEADER`, `HEADER.FIELDS`, `HEADER.FIELDS.NOT`, `MIME`, and `TEXT`.
-///
-/// The HEADER, HEADER.FIELDS, and HEADER.FIELDS.NOT part
-/// specifiers refer to the [RFC-2822] header of the message or of
-/// an encapsulated [MIME-IMT] MESSAGE/RFC822 message.
-/// HEADER.FIELDS and HEADER.FIELDS.NOT are followed by a list of
-/// field-name (as defined in [RFC-2822]) names, and return a
-/// subset of the header.
-///
-/// The field-matching is case-insensitive but otherwise exact.
-/// Subsetting does not exclude the [RFC-2822] delimiting blank line between the header
-/// and the body; the blank line is included in all header fetches,
-/// except in the case of a message which has no body and no blank
-/// line.
-#[cfg_attr(feature = "bounded-static", derive(ToStatic))]
-#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub enum PartSpecifier<'a> {
-    PartNumber(u32),
-    Header,
-    HeaderFields(NonEmptyVec<AString<'a>>),
-    HeaderFieldsNot(NonEmptyVec<AString<'a>>),
-    Mime,
-    Text,
-}


Test patch:
diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml
index ec3d3e86..e2175f2b 100644
--- a/.github/workflows/build_and_test.yml
+++ b/.github/workflows/build_and_test.yml
@@ -36,7 +36,7 @@ jobs:
         run: |
           cargo hack check \
           --workspace \
-          --exclude tokio-client --exclude tokio-server \
+          --exclude tokio-client --exclude tokio-server --exclude tokio-support \
           --feature-powerset \
           --group-features starttls,ext_compress,ext_condstore_qresync,ext_enable,ext_idle,ext_literal,ext_login_referrals,ext_mailbox_referrals,ext_move,ext_quota,ext_sasl_ir,ext_unselect \
           --exclude-features ext,split
@@ -61,7 +61,7 @@ jobs:
           cargo install cargo-fuzz
 
       - name: Setup | Cache dependencies
-        uses: Swatinem/rust-cache@v2.2.1
+        uses: Swatinem/rust-cache@v2.5.1
         id: cache
         with:
           cache-all-crates: true
@@ -117,7 +117,7 @@ jobs:
           rustup toolchain install nightly --profile minimal
 
       - name: Setup | Cache dependencies
-        uses: Swatinem/rust-cache@v2.2.1
+        uses: Swatinem/rust-cache@v2.5.1
         id: cache
         with:
           cache-all-crates: true
diff --git a/imap-codec/src/testing.rs b/imap-codec/src/testing.rs
index c711aa90..c317e32f 100644
--- a/imap-codec/src/testing.rs
+++ b/imap-codec/src/testing.rs
@@ -1,13 +1,14 @@
 use std::fmt::Debug;
 
-use crate::{
+use imap_types::{
     auth::AuthenticateData,
-    codec::{Decode, Encode, IMAPResult},
     command::Command,
     response::{Continue, Greeting, Response},
     utils::escape_byte_string,
 };
 
+use crate::codec::{Decode, Encode, IMAPResult};
+
 pub(crate) fn known_answer_test_encode(
     (test_object, expected_bytes): (impl Encode, impl AsRef<[u8]>),
 ) {
@@ -72,8 +73,9 @@ impl_kat_inverse! {kat_inverse_authenticate_data, AuthenticateData}
 
 #[cfg(test)]
 mod tests {
+    use imap_types::command::{Command, CommandBody};
+
     use super::*;
-    use crate::command::{Command, CommandBody};
 
     #[test]
     #[should_panic]
diff --git a/imap-codec/tests/readme.rs b/imap-codec/tests/readme.rs
index a15e1aef..932a35a3 100644
--- a/imap-codec/tests/readme.rs
+++ b/imap-codec/tests/readme.rs
@@ -1,6 +1,6 @@
 use imap_codec::{
     codec::{Decode, Encode},
-    command::Command,
+    imap_types::command::Command,
 };
 
 #[test]
diff --git a/imap-codec/tests/trace.rs b/imap-codec/tests/trace.rs
index af0325d7..706985b1 100644
--- a/imap-codec/tests/trace.rs
+++ b/imap-codec/tests/trace.rs
@@ -1,16 +1,17 @@
 use imap_codec::{
-    auth::AuthMechanism,
-    body::{BasicFields, Body, BodyStructure, SpecificFields},
     codec::{Decode, Encode},
-    command::{Command, CommandBody},
-    core::{AString, IString, Literal, NString, Quoted, Tag},
-    datetime::DateTime,
-    envelope::{Address, Envelope},
-    fetch::{Macro, MessageDataItem, MessageDataItemName},
-    flag::{Flag, FlagFetch, FlagPerm, StoreResponse, StoreType},
-    response::{Capability, Code, Data, Greeting, Response, Status},
-    secret::Secret,
-    section::Section,
+    imap_types::{
+        auth::AuthMechanism,
+        body::{BasicFields, Body, BodyStructure, SpecificFields},
+        command::{Command, CommandBody},
+        core::{AString, IString, Literal, NString, Quoted, Tag},
+        datetime::DateTime,
+        envelope::{Address, Envelope},
+        fetch::{Macro, MessageDataItem, MessageDataItemName, Section},
+        flag::{Flag, FlagFetch, FlagPerm, StoreResponse, StoreType},
+        response::{Capability, Code, Data, Greeting, Response, Status},
+        secret::Secret,
+    },
 };
 
 enum Who {