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

Issue:
docs(imap-types): Mark all extensions (with required capability)
We should mark all API surface that requires explicit support clearly.

For example, every `Command` should say what `Capability` the server potentially needs to advertize.

This is so that users don't use features by accident that a server doesn't support.

Action items:

* Define a documentation template
* Use documentation template to clarify when something potentially could not be supported


Discussion / hints:

Fix patch:
diff --git a/imap-types/src/command.rs b/imap-types/src/command.rs
index 9d7838fc..0047475a 100644
--- a/imap-types/src/command.rs
+++ b/imap-types/src/command.rs
@@ -166,6 +166,12 @@ pub enum CommandBody<'a> {
     /// the-middle attacks which alter the capabilities list prior to
     /// STARTTLS.  The server MAY advertise different capabilities after
     /// STARTTLS.
+    ///
+    /// <div class="warning">
+    /// This must only be used when the server advertised support for it sending the STARTTLS capability.
+    ///
+    /// Try to avoid STARTTLS using implicit TLS on port 993.
+    /// </div>
     #[cfg(feature = "starttls")]
     #[cfg_attr(docsrs, doc(cfg(feature = "starttls")))]
     StartTLS,
@@ -266,7 +272,9 @@ pub enum CommandBody<'a> {
         ///
         /// 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.
+        /// <div class="warning">
+        /// This extension must only be used when the server advertised support for it sending the SASL-IR capability.
+        /// </div>
         initial_response: Option<Secret<Cow<'a, [u8]>>>,
     },
 
@@ -397,6 +405,10 @@ pub enum CommandBody<'a> {
     /// Unselect a mailbox.
     ///
     /// This should bring the client back to the AUTHENTICATED state.
+    ///
+    /// <div class="warning">
+    /// This extension must only be used when the server advertised support for it sending the UNSELECT capability.
+    /// </div>
     Unselect,
 
     /// 6.3.2.  EXAMINE Command
@@ -897,7 +909,9 @@ pub enum CommandBody<'a> {
         #[cfg(feature = "ext_binary")]
         /// Message to append.
         ///
-        /// Note: Use [`LiteralOrLiteral8::Literal8`] only when the server advertised [`Capability::Binary`](crate::response::Capability::Binary).
+        /// <div class="warning">
+        /// Use [`LiteralOrLiteral8::Literal8`] only when the server advertised [`Capability::Binary`](crate::response::Capability::Binary).
+        /// </div>
         message: LiteralOrLiteral8<'a>,
     },
 
@@ -1071,6 +1085,10 @@ pub enum CommandBody<'a> {
     /// * OK - sort completed
     /// * NO - sort error: can't sort that charset or criteria
     /// * BAD - command unknown or arguments invalid
+    ///
+    /// <div class="warning">
+    /// This extension must only be used when the server advertised support for it sending the SORT capability.
+    /// </div>
     Sort {
         /// Sort criteria.
         sort_criteria: Vec1<SortCriterion>,
@@ -1094,6 +1112,10 @@ pub enum CommandBody<'a> {
     /// * OK - thread completed
     /// * NO - thread error: can't thread that charset or criteria
     /// * BAD - command unknown or arguments invalid
+    ///
+    /// <div class="warning">
+    /// This extension must only be used when the server advertised support for it sending the THREAD capability.
+    /// </div>
     Thread {
         /// Threading algorithm.
         algorithm: ThreadingAlgorithm<'a>,
@@ -1323,15 +1345,27 @@ pub enum CommandBody<'a> {
     // by issuing the associated experimental command.
     //X,
     /// IDLE command.
+    ///
+    /// <div class="warning">
+    /// This extension must only be used when the server advertised support for it sending the IDLE capability.
+    /// </div>
     Idle,
 
     /// ENABLE command.
+    ///
+    /// <div class="warning">
+    /// This extension must only be used when the server advertised support for it sending the ENABLE capability.
+    /// </div>
     Enable {
         /// Capabilities to enable.
         capabilities: Vec1<CapabilityEnable<'a>>,
     },
 
     /// COMPRESS command.
+    ///
+    /// <div class="warning">
+    /// This extension must only be used when the server advertised support for it sending the COMPRESS capability.
+    /// </div>
     Compress {
         /// Compression algorithm.
         algorithm: CompressionAlgorithm,
@@ -1359,6 +1393,10 @@ pub enum CommandBody<'a> {
     /// S: * QUOTA "!partition/sda4" (STORAGE 104 10923847)
     /// S: G0001 OK Getquota complete
     /// ```
+    ///
+    /// <div class="warning">
+    /// This extension must only be used when the server advertised support for it sending the QUOTA* capability.
+    /// </div>
     GetQuota {
         /// Name of quota root.
         root: AString<'a>,
@@ -1392,6 +1430,10 @@ pub enum CommandBody<'a> {
     /// S: * QUOTA "!partition/sda4" (STORAGE 104 10923847)
     /// S: G0002 OK Getquotaroot complete
     /// ```
+    ///
+    /// <div class="warning">
+    /// This extension must only be used when the server advertised support for it sending the QUOTA* capability.
+    /// </div>
     GetQuotaRoot {
         /// Name of mailbox.
         mailbox: Mailbox<'a>,
@@ -1435,6 +1477,10 @@ pub enum CommandBody<'a> {
     /// response here is entirely optional.
     /// S: S0002 NO Cannot change system limit
     /// ```
+    ///
+    /// <div class="warning">
+    /// This extension must only be used when the server advertised support for it sending the QUOTA* capability.
+    /// </div>
     SetQuota {
         /// Name of quota root.
         root: AString<'a>,
@@ -1443,6 +1489,10 @@ pub enum CommandBody<'a> {
     },
 
     /// MOVE command.
+    ///
+    /// <div class="warning">
+    /// This extension must only be used when the server advertised support for it sending the MOVE capability.
+    /// </div>
     Move {
         /// Set of messages.
         sequence_set: SequenceSet,
@@ -1454,18 +1504,32 @@ pub enum CommandBody<'a> {
 
     #[cfg(feature = "ext_id")]
     /// ID command.
+    ///
+    /// <div class="warning">
+    /// This extension must only be used when the server advertised support for it sending the ID capability.
+    /// </div>
     Id {
         /// Parameters.
         parameters: Option<Vec<(IString<'a>, NString<'a>)>>,
     },
 
     #[cfg(feature = "ext_metadata")]
+    /// Set annotation(s).
+    ///
+    /// <div class="warning">
+    /// This extension must only be used when the server advertised support for it sending the METADATA* capability.
+    /// </div>
     SetMetadata {
         mailbox: Mailbox<'a>,
         entry_values: Vec1<EntryValue<'a>>,
     },
 
     #[cfg(feature = "ext_metadata")]
+    /// Retrieve server or mailbox annotation(s).
+    ///
+    /// <div class="warning">
+    /// This extension must only be used when the server advertised support for it sending the METADATA* capability.
+    /// </div>
     GetMetadata {
         options: Vec<GetMetadataOption>,
         mailbox: Mailbox<'a>,
@@ -1498,6 +1562,10 @@ impl<'a> CommandBody<'a> {
     /// Construct an AUTHENTICATE command (with an initial response, SASL-IR).
     ///
     /// Note: Use this only when the server advertised the `SASL-IR` capability.
+    ///
+    /// <div class="warning">
+    /// This extension must only be used when the server advertised support for it sending the SASL-IR capability.
+    /// </div>
     pub fn authenticate_with_ir<I>(mechanism: AuthMechanism<'a>, initial_response: I) -> Self
     where
         I: Into<Cow<'a, [u8]>>,
diff --git a/imap-types/src/core.rs b/imap-types/src/core.rs
index eaa70381..ba2d1358 100644
--- a/imap-types/src/core.rs
+++ b/imap-types/src/core.rs
@@ -577,11 +577,17 @@ impl<'a> Literal<'a> {
         self.mode = mode;
     }
 
+    /// Turn literal into sync literal.
     pub fn into_sync(mut self) -> Self {
         self.mode = LiteralMode::Sync;
         self
     }
 
+    /// Turn literal into non-sync literal.
+    ///
+    /// <div class="warning">
+    /// This extension must only be used when the server advertised support for it sending the LITERAL+ or LITERAL- capability.
+    /// </div>
     pub fn into_non_sync(mut self) -> Self {
         self.mode = LiteralMode::NonSync;
         self
@@ -626,6 +632,10 @@ impl<'a> Literal<'a> {
     /// Do not call this constructor with untrusted data.
     ///
     /// Note: This method will `panic!` on wrong input in debug builds.
+    ///
+    /// <div class="warning">
+    /// This extension must only be used when the server advertised support for it sending the LITERAL+ or LITERAL- capability.
+    /// </div>
     #[cfg(feature = "unvalidated")]
     #[cfg_attr(docsrs, doc(cfg(feature = "unvalidated")))]
     pub fn unvalidated_non_sync<D>(data: D) -> Self
@@ -711,6 +721,10 @@ pub enum LiteralMode {
     /// A synchronizing literal, i.e., `{<n>}\r\n<data>`.
     Sync,
     /// A non-synchronizing literal according to RFC 7888, i.e., `{<n>+}\r\n<data>`.
+    ///
+    /// <div class="warning">
+    /// This extension must only be used when the server advertised support for it sending the LITERAL+ or LITERAL- capability.
+    /// </div>
     NonSync,
 }
 
diff --git a/imap-types/src/extensions/compress.rs b/imap-types/src/extensions/compress.rs
index efc60fba..27150aff 100644
--- a/imap-types/src/extensions/compress.rs
+++ b/imap-types/src/extensions/compress.rs
@@ -26,6 +26,9 @@ use crate::{
 };
 
 impl<'a> CommandBody<'a> {
+    /// <div class="warning">
+    /// This extension must only be used when the server advertised support for it sending the COMPRESS* capability.
+    /// </div>
     pub fn compress(algorithm: CompressionAlgorithm) -> Self {
         CommandBody::Compress { algorithm }
     }
diff --git a/imap-types/src/extensions/enable.rs b/imap-types/src/extensions/enable.rs
index db03047f..09b8a8a6 100644
--- a/imap-types/src/extensions/enable.rs
+++ b/imap-types/src/extensions/enable.rs
@@ -22,6 +22,9 @@ use crate::{
 };
 
 impl<'a> CommandBody<'a> {
+    /// <div class="warning">
+    /// This extension must only be used when the server advertised support for it sending the ENABLE capability.
+    /// </div>
     pub fn enable<C>(capabilities: C) -> Result<Self, C::Error>
     where
         C: TryInto<Vec1<CapabilityEnable<'a>>>,
diff --git a/imap-types/src/extensions/move.rs b/imap-types/src/extensions/move.rs
index a23b6a42..4c26eafa 100644
--- a/imap-types/src/extensions/move.rs
+++ b/imap-types/src/extensions/move.rs
@@ -6,6 +6,9 @@ use crate::{
 };
 
 impl<'a> CommandBody<'a> {
+    /// <div class="warning">
+    /// This extension must only be used when the server advertised support for it sending the MOVE capability.
+    /// </div>
     pub fn r#move<S, M>(
         sequence_set: S,
         mailbox: M,
diff --git a/imap-types/src/extensions/quota.rs b/imap-types/src/extensions/quota.rs
index a4e5f9ea..073b7c59 100644
--- a/imap-types/src/extensions/quota.rs
+++ b/imap-types/src/extensions/quota.rs
@@ -54,6 +54,9 @@ use crate::{
 };
 
 impl<'a> CommandBody<'a> {
+    /// <div class="warning">
+    /// This extension must only be used when the server advertised support for it sending the QUOTA capability.
+    /// </div>
     pub fn get_quota<A>(root: A) -> Result<Self, A::Error>
     where
         A: TryInto<AString<'a>>,
@@ -63,6 +66,9 @@ impl<'a> CommandBody<'a> {
         })
     }
 
+    /// <div class="warning">
+    /// This extension must only be used when the server advertised support for it sending the QUOTA capability.
+    /// </div>
     pub fn get_quota_root<M>(mailbox: M) -> Result<Self, M::Error>
     where
         M: TryInto<Mailbox<'a>>,
@@ -72,6 +78,9 @@ impl<'a> CommandBody<'a> {
         })
     }
 
+    /// <div class="warning">
+    /// This extension must only be used when the server advertised support for it sending the QUOTA capability.
+    /// </div>
     pub fn set_quota<R, S>(root: R, quotas: S) -> Result<Self, SetQuotaError<R::Error, S::Error>>
     where
         R: TryInto<AString<'a>>,
diff --git a/imap-types/src/extensions/unselect.rs b/imap-types/src/extensions/unselect.rs
index 14abb0cc..57adec3b 100644
--- a/imap-types/src/extensions/unselect.rs
+++ b/imap-types/src/extensions/unselect.rs
@@ -3,6 +3,9 @@
 use crate::command::CommandBody;
 
 impl CommandBody<'_> {
+    /// <div class="warning">
+    /// This extension must only be used when the server advertised support for it sending the UNSELECT capability.
+    /// </div>
     pub fn unselect() -> Self {
         CommandBody::Unselect
     }
diff --git a/imap-types/src/lib.rs b/imap-types/src/lib.rs
index 37fb1be5..28f2d121 100644
--- a/imap-types/src/lib.rs
+++ b/imap-types/src/lib.rs
@@ -99,43 +99,44 @@
 //!
 //! # Supported IMAP extensions
 //!
-//! |Description                                                  |
-//! |-------------------------------------------------------------|
-//! |IMAP4 Non-synchronizing Literals ([RFC 2088], [RFC 7888])    |
-//! |IMAP MOVE Extension ([RFC 6851])                             |
-//! |IMAP UNSELECT command ([RFC 3691])                           |
-//! |IMAP Extension for SASL Initial Client Response ([RFC 4959]) |
-//! |The IMAP COMPRESS Extension ([RFC 4978])                     |
-//! |The IMAP ENABLE Extension ([RFC 5161])                       |
-//! |IMAP4 IDLE command ([RFC 2177])                              |
-//! |IMAP QUOTA Extension ([RFC 9208])                            |
+//! | Description                                                                                             |
+//! |---------------------------------------------------------------------------------------------------------|
+//! | IMAP4 non-synchronizing literals ([RFC 2088], [RFC 7888])                                               |
+//! | Internet Message Access Protocol (IMAP) - MOVE Extension ([RFC 6851])                                   |
+//! | Internet Message Access Protocol (IMAP) UNSELECT command ([RFC 3691])                                   |
+//! | IMAP Extension for Simple Authentication and Security Layer (SASL) Initial Client Response ([RFC 4959]) |
+//! | The IMAP COMPRESS Extension ([RFC 4978])                                                                |
+//! | The IMAP ENABLE Extension ([RFC 5161])                                                                  |
+//! | IMAP4 IDLE command ([RFC 2177])                                                                         |
+//! | IMAP QUOTA Extension ([RFC 9208])                                                                       |
 //!
 //! # Features
 //!
 //! This crate uses the following features to enable experimental IMAP extensions:
 //!
-//! |Feature              |Description                                                                            |Status    |
-//! |---------------------|---------------------------------------------------------------------------------------|----------|
-//! |ext_id               |IMAP4 ID extension ([RFC 2971])                                                        |Unfinished|
-//! |ext_sort_thread      |Internet Message Access Protocol - SORT and THREAD Extensions ([RFC 5256] + [RFC 5957])|Unfinished|
-//! |ext_condstore_qresync|Quick Flag Changes Resynchronization and Quick Mailbox Resynchronization ([RFC 7162])  |Unfinished|
-//! |ext_login_referrals  |IMAP4 Login Referrals ([RFC 2221])                                                     |Unfinished|
-//! |ext_mailbox_referrals|IMAP4 Mailbox Referrals ([RFC 2193])                                                   |Unfinished|
-//! |ext_binary           |IMAP4 Binary Content Extension ([RFC 3516])                                            |Unfinished|
-//! |ext_metadata         |The IMAP METADATA Extension ([RFC 5464])                                               |Unfinished|
-//! |starttls             |IMAP4rev1 ([RFC 3501]; section 6.2.1)                                                  |          |
+//! | Feature               | Description                                                                                                                  | Status     |
+//! |-----------------------|------------------------------------------------------------------------------------------------------------------------------|------------|
+//! | ext_id                | IMAP4 ID extension ([RFC 2971])                                                                                              | Unfinished |
+//! | ext_sort_thread       | Internet Message Access Protocol - SORT and THREAD Extensions ([RFC 5256], [RFC 5957])                                       | Unfinished |
+//! | ext_condstore_qresync | IMAP Extensions: Quick Flag Changes Resynchronization (CONDSTORE) and Quick Mailbox Resynchronization (QRESYNC) ([RFC 7162]) | Unfinished |
+//! | ext_login_referrals   | IMAP4 Login Referrals ([RFC 2221])                                                                                           | Unfinished |
+//! | ext_mailbox_referrals | IMAP4 Mailbox Referrals ([RFC 2193])                                                                                         | Unfinished |
+//! | ext_binary            | IMAP4 Binary Content Extension ([RFC 3516])                                                                                  | Unfinished |
+//! | ext_metadata          | The IMAP METADATA Extension ([RFC 5464])                                                                                     | Unfinished |
+//! | ext_uidplus           | IMAP4 UIDPLUS extension ([RFC 2359], [RFC 4315])                                                                             | Unfinished |
+//! | starttls              | IMAP4rev1 ([RFC 3501]; section 6.2.1)                                                                                        |            |
 //!
 //! STARTTLS is not an IMAP extension but feature-gated because it [should be avoided](https://nostarttls.secvuln.info/).
 //! For better performance and security, use "implicit TLS", i.e., IMAP-over-TLS on port 993, and don't use STARTTLS at all.
 //!
 //! Furthermore, imap-types uses the following features to facilitate interoperability:
 //!
-//! | Feature          | Description                                                    | Enabled by default |
-//! |------------------|----------------------------------------------------------------|--------------------|
-//! | arbitrary        | Derive `Arbitrary` implementations.                            | No                 |
-//! | bounded-static   | Derive `ToStatic/IntoStatic` implementations.                  | No                 |
-//! | serde            | Derive `serde`s `Serialize` and `Deserialize` implementations. | No                 |
-//! | unvalidated      | Unlock `unvalidated` constructors.                             | No                 |
+//! | Feature          | Description                                                   | Enabled by default |
+//! |------------------|---------------------------------------------------------------|--------------------|
+//! | arbitrary        | Derive `Arbitrary` implementations                            | No                 |
+//! | bounded-static   | Derive `ToStatic/IntoStatic` implementations                  | No                 |
+//! | serde            | Derive `serde`s `Serialize` and `Deserialize` implementations | No                 |
+//! | unvalidated      | Unlock `unvalidated` constructors                             | No                 |
 //!
 //! When using `arbitrary`, all types defined in imap-types implement the [Arbitrary] trait to ease testing.
 //! This is used, for example, to generate instances during fuzz-testing.
@@ -151,10 +152,12 @@
 //! [RFC 2177]: https://datatracker.ietf.org/doc/html/rfc2177
 //! [RFC 2193]: https://datatracker.ietf.org/doc/html/rfc2193
 //! [RFC 2221]: https://datatracker.ietf.org/doc/html/rfc2221
+//! [RFC 2359]: https://datatracker.ietf.org/doc/html/rfc2359
 //! [RFC 2971]: https://datatracker.ietf.org/doc/html/rfc2971
 //! [RFC 3501]: https://datatracker.ietf.org/doc/html/rfc3501
 //! [RFC 3516]: https://datatracker.ietf.org/doc/html/rfc3516
 //! [RFC 3691]: https://datatracker.ietf.org/doc/html/rfc3691
+//! [RFC 4315]: https://datatracker.ietf.org/doc/html/rfc4315
 //! [RFC 4959]: https://datatracker.ietf.org/doc/html/rfc4959
 //! [RFC 4978]: https://datatracker.ietf.org/doc/html/rfc4978
 //! [RFC 5161]: https://datatracker.ietf.org/doc/html/rfc5161


Test patch: