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

Issue:
docs: Update outdated README and check all code examples automatically
Hello! I just downloaded imap-codec using `cargo add imap-codec`, and I want to run the examples from https://crates.io/crates/imap-codec, but I'm getting a bunch of errors.

Here are the error messages:
```bash
[coalooball /home/cyan/rs_test_project]
==># cargo t
   Compiling minimal-lexical v0.2.1
   Compiling base64 v0.21.5
   Compiling log v0.4.20
   Compiling num-traits v0.2.17
   Compiling thiserror v1.0.50
   Compiling nom v7.1.3
   Compiling chrono v0.4.31
   Compiling imap-types v1.0.0
   Compiling abnf-core v0.6.0
   Compiling imap-codec v1.0.0
   Compiling rs_test_project v0.1.0 (/home/cyan/rs_test_project)
error[E0432]: unresolved imports `imap_codec::codec::Decode`, `imap_codec::codec::Encode`
 --> src/main.rs:2:13
  |
2 |     codec::{Decode, Encode},
  |             ^^^^^^  ^^^^^^ no `Encode` in `codec`
  |             |
  |             no `Decode` in `codec`
  |
help: a similar name exists in the module
  |
2 |     codec::{decode, Encode},
  |             ~~~~~~
help: a similar name exists in the module
  |
2 |     codec::{Decode, encode},
  |                     ~~~~~~

error[E0603]: module `codec` is private
   --> src/main.rs:2:5
    |
2   |     codec::{Decode, Encode},
    |     ^^^^^ private module
    |
note: the module `codec` is defined here
   --> /root/.cargo/registry/src/index.crates.io-6f17d22bba15001f/imap-codec-1.0.0/src/lib.rs:109:1
    |
109 | mod codec;
    | ^^^^^^^^^

error[E0603]: module `command` is private
   --> src/main.rs:3:5
    |
3   |     command::Command,
    |     ^^^^^^^ private module
    |
note: the module `command` is defined here
   --> /root/.cargo/registry/src/index.crates.io-6f17d22bba15001f/imap-codec-1.0.0/src/lib.rs:110:1
    |
110 | mod command;
    | ^^^^^^^^^^^

Some errors have detailed explanations: E0432, E0603.
For more information about an error, try `rustc --explain E0432`.
error: could not compile `rs_test_project` (bin "rs_test_project" test) due to 3 previous errors
```

Here is my source code(from https://crates.io/crates/imap-codec) and Cargo.toml(generated by `cargo add imap-codec`) file:
```bash
[coalooball /home/cyan/rs_test_project]
==># cat src/main.rs
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));
}[coalooball /home/cyan/rs_test_project]
==># cat Cargo.toml
[package]
name = "rs_test_project"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
imap-codec = "1.0.0"
```


Discussion / hints:
Hello @coalooball! Thanks for submitting an issue!

That's what happens when code is not automatically checked... :-) The examples are outdated and need to be fixed.

In the meantime: Could you try the examples from https://docs.rs/imap-codec/latest/imap_codec/ and see if they work for you?
Alternatively, you might find these examples (https://github.com/duesee/imap-codec/tree/main/imap-codec/examples) helpful, too.
We could use https://doc.rust-lang.org/rustdoc/write-documentation/documentation-tests.html#include-items-only-when-collecting-doctests (see how to include README) to test the example in the README.
Hello @duesee! Thanks for your prompt response.

I tried the examples from the two links you provided. I was able to run all the examples in this link (https://github.com/duesee/imap-codec/tree/main/imap-codec/examples). However, I encountered compilation errors when trying the examples from this link (https://docs.rs/imap-codec/latest/imap_codec/).

I need to execute the `cargo add imap-types` command in the project's root directory, and I also need to modify the code of this example like this:
```rust
use imap_codec::{decode::Decoder, GreetingCodec};
use imap_types::response::{Code, Greeting, GreetingKind};
use imap_types::core::Text;
fn main() {
    let (remaining, greeting) = GreetingCodec::default()
        .decode(b"* OK [ALERT] Hello, World!\r\n<remaining>")
        .unwrap();

    assert_eq!(
        greeting,
        Greeting {
            kind: GreetingKind::Ok,
            code: Some(Code::Alert),
            text: Text::try_from("Hello, World!").unwrap(),
        }
    );
    assert_eq!(remaining, &b"<remaining>"[..])
}

```

I think it's worth reminding everyone to use `use imap_types::response::{Code, Greeting, GreetingKind}; ` cause I tend to instinctively use `use imap_codec::response::{Code, Greeting, GreetingKind};`.

By the way, my Cargo version is as follows:
```sh
cargo --version
cargo 1.75.0-nightly (6fa6fdc76 2023-10-10)
```
Thanks for your valuable input! I agree.

Currently, `imap-codec` [re-exports](https://docs.rs/imap-codec/latest/imap_codec/#reexports) `imap-types` and you could `use imap_codec::imap_type::"`. BUT: I think this is confusing, and like to remove the re-export at some point.

Thus, I completely agree that the examples should mention to `cargo add imap-types`, too, and tell what to `use`.

As you already fixed it... are you in a position to make a PR? :-) I can do it, too, but as you already figured this all out, you should take the credit!
We could make two PRs: One fixing the outdated README (and making it so that it is automatically checked) -- I can do this PR quickly -- and one improving the examples -- possibly you? :-)

Fix patch:
diff --git a/imap-codec/README.md b/imap-codec/README.md
index 84809e45..169f3a62 100644
--- a/imap-codec/README.md
+++ b/imap-codec/README.md
@@ -13,18 +13,16 @@ The complete [formal syntax] of IMAP4rev1 and several IMAP [extensions] are impl
 ## Usage
 
 ```rust
-use imap_codec::{
-    codec::{Decode, Encode},
-    command::Command,
-};
+use imap_codec::{decode::Decoder, encode::Encoder, CommandCodec};
 
 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 codec = CommandCodec::new();
+    let (remainder, command) = codec.decode(input).unwrap();
+    println!("# Parsed\n\n{:#?}\n\n", command);
 
-    let buffer = parsed.encode().dump();
+    let buffer = codec.encode(&command).dump();
 
     // Note: IMAP4rev1 may produce messages that are not valid UTF-8.
     println!("# Serialized\n\n{:?}", std::str::from_utf8(&buffer));
diff --git a/imap-codec/src/lib.rs b/imap-codec/src/lib.rs
index 383bcf56..14a9598a 100644
--- a/imap-codec/src/lib.rs
+++ b/imap-codec/src/lib.rs
@@ -104,6 +104,11 @@
 #![deny(missing_debug_implementations)]
 #![cfg_attr(docsrs, feature(doc_cfg))]
 
+// Test examples from imap-codec's README.
+#[doc = include_str!("../README.md")]
+#[cfg(doctest)]
+pub struct ReadmeDoctests;
+
 mod auth;
 mod body;
 mod codec;
diff --git a/imap-types/README.md b/imap-types/README.md
index 259e976b..72abf221 100644
--- a/imap-types/README.md
+++ b/imap-types/README.md
@@ -32,9 +32,11 @@ However, it's always possible to manually choose a specific representation.
 This ...
 
 ```rust
-Command::new(
+use imap_types::command::{Command, CommandBody};
+
+let cmd = Command::new(
     "A1",
-    CommandBody::login("alice", "password").unwrap(),
+    CommandBody::login("alice", "password").unwrap()
 ).unwrap();
 ```
 
@@ -47,11 +49,12 @@ A1 LOGIN alice password
 However, ...
 
 ```rust
-Command::new(
+use imap_types::command::{Command, CommandBody};
+
+let cmd = Command::new(
     "A1",
     CommandBody::login("alice\"", b"\xCA\xFE".as_ref()).unwrap(),
-)
-.unwrap();
+).unwrap();
 ```
 
 ... will produce ...
@@ -63,8 +66,10 @@ A1 LOGIN "alice\"" {2}
 
 Also, the construction ...
 
-```rust
-Command::new(
+```rust,should_panic
+use imap_types::command::{Command, CommandBody};
+
+let cmd = Command::new(
     "A1",
     CommandBody::login("alice\x00", "password").unwrap(),
 ).unwrap();
@@ -79,11 +84,15 @@ Command::new(
 You can also use ...
 
 ```rust
-Command::new(
+use imap_types::{
+    command::{Command, CommandBody},
+    core::Literal,
+};
+
+let cmd = Command::new(
     "A1",
     CommandBody::login(Literal::try_from("alice").unwrap(), "password").unwrap(),
-)
-.unwrap();
+).unwrap();
 ```
 
 ... to produce ...
@@ -98,17 +107,35 @@ alice password
 Also, you can use Rust literals and resort to `unvalidated` constructors when you are certain that your input is correct:
 
 ```rust
-// This could be provided by the email application.
-let tag = TagGenerator::random();
-
-Command {
-    tag,
-    body: CommandBody::Login {
-        // Note that the "unvalidated" feature must be activated.
-        username: AString::from(Atom::unvalidated("alice")),
-        password: Secret::new(AString::from(Atom::unvalidated("password"))),
-    },
-};
+// Note: "unvalidated" feature must be activated.
+#[cfg(feature = "unvalidated")]
+{
+  use imap_types::{
+      command::{Command, CommandBody},
+      core::{AString, Atom, Tag},
+      secret::Secret,
+  };
+
+  // This could be provided by the email application.
+  struct TagGenerator;
+
+  impl TagGenerator {
+      fn random() -> Tag<'static> {
+          // Make this random :-)
+          Tag::unvalidated("A1")
+      }
+  }
+
+  let tag = TagGenerator::random();
+
+  let cmd = Command {
+      tag,
+      body: CommandBody::Login {
+          username: AString::from(Atom::unvalidated("alice")),
+          password: Secret::new(AString::from(Atom::unvalidated("password"))),
+      },
+  };
+}
 ```
 
 In this case, imap-codec won't stand in your way.
diff --git a/imap-types/src/lib.rs b/imap-types/src/lib.rs
index 1c799be4..7c6bd5b3 100644
--- a/imap-types/src/lib.rs
+++ b/imap-types/src/lib.rs
@@ -163,6 +163,11 @@
 // #![deny(missing_docs)]
 #![cfg_attr(docsrs, feature(doc_cfg))]
 
+// Test examples from imap-types' README.
+#[doc = include_str!("../README.md")]
+#[cfg(doctest)]
+pub struct ReadmeDoctests;
+
 #[cfg(feature = "arbitrary")]
 mod arbitrary;
 pub mod auth;


Test patch:
diff --git a/imap-codec/tests/readme.rs b/imap-codec/tests/readme.rs
deleted file mode 100644
index 0d626fd0..00000000
--- a/imap-codec/tests/readme.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-use imap_codec::{decode::Decoder, encode::Encoder, CommandCodec};
-
-#[test]
-fn test_from_readme() {
-    let input = b"ABCD UID FETCH 1,2:* (BODY.PEEK[1.2.3.4.MIME]<42.1337>)\r\n";
-
-    let (_remainder, parsed) = CommandCodec::default().decode(input).unwrap();
-    println!("# Parsed\n\n{:#?}\n\n", parsed);
-
-    let buffer = CommandCodec::default().encode(&parsed).dump();
-
-    // Note: IMAP4rev1 may produce messages that are not valid UTF-8.
-    println!("# Serialized\n\n{:?}", std::str::from_utf8(&buffer));
-}