1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
//! IMAP protocol state.
//!
//! "Once the connection between client and server is established, an IMAP4rev1 connection is in one of four states.
//! The initial state is identified in the server greeting.
//! Most commands are only valid in certain states.
//! It is a protocol error for the client to attempt a command while the connection is in an inappropriate state,
//! and the server will respond with a BAD or NO (depending upon server implementation) command completion result." ([RFC 3501](https://www.rfc-editor.org/rfc/rfc3501.html))
//!
//! ```text
//!           +----------------------+
//!           |connection established|
//!           +----------------------+
//!                      ||
//!                      \/
//!    +--------------------------------------+
//!    |          server greeting             |
//!    +--------------------------------------+
//!              || (1)       || (2)        || (3)
//!              \/           ||            ||
//!    +-----------------+    ||            ||
//!    |Not Authenticated|    ||            ||
//!    +-----------------+    ||            ||
//!     || (7)   || (4)       ||            ||
//!     ||       \/           \/            ||
//!     ||     +----------------+           ||
//!     ||     | Authenticated  |<=++       ||
//!     ||     +----------------+  ||       ||
//!     ||       || (7)   || (5)   || (6)   ||
//!     ||       ||       \/       ||       ||
//!     ||       ||    +--------+  ||       ||
//!     ||       ||    |Selected|==++       ||
//!     ||       ||    +--------+           ||
//!     ||       ||       || (7)            ||
//!     \/       \/       \/                \/
//!    +--------------------------------------+
//!    |               Logout                 |
//!    +--------------------------------------+
//!                      ||
//!                      \/
//!        +-------------------------------+
//!        |both sides close the connection|
//!        +-------------------------------+
//!
//! (1) connection without pre-authentication (OK greeting)
//! (2) pre-authenticated connection (PREAUTH greeting)
//! (3) rejected connection (BYE greeting)
//! (4) successful LOGIN or AUTHENTICATE command
//! (5) successful SELECT or EXAMINE command
//! (6) CLOSE command, or failed SELECT or EXAMINE command
//! (7) LOGOUT command, server shutdown, or connection closed
//! ```

#[cfg(feature = "bounded-static")]
use bounded_static::ToStatic;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

use crate::{core::Tag, mailbox::Mailbox};

/// State of the IMAP4rev1 connection.
#[cfg_attr(feature = "bounded-static", derive(ToStatic))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum State<'a> {
    Greeting,

    /// The client MUST supply authentication credentials before most commands will be permitted.
    /// This state is entered when a connection starts unless the connection has been pre-authenticated.
    NotAuthenticated,

    /// The client is authenticated and MUST select a mailbox to access before commands that affect messages will be permitted.
    /// This state is entered when a pre-authenticated connection starts, when acceptable authentication credentials have been provided,
    /// after an error in selecting a mailbox, or after a successful CLOSE command.
    Authenticated,

    /// A mailbox has been selected to access.
    /// This state is entered when a mailbox has been successfully selected.
    Selected(Mailbox<'a>),

    /// The connection is being terminated.
    /// This state can be entered as a result of a client request (via the LOGOUT command) or by unilateral action on the part of either the client or server.
    ///
    /// If the client requests the logout state, the server MUST send an untagged BYE response and a tagged OK response to the LOGOUT command before the server closes the connection;
    /// and the client MUST read the tagged OK response to the LOGOUT command before the client closes the connection.
    ///
    /// A server MUST NOT unilaterally close the connection without sending an untagged BYE response that contains the reason for having done so.
    /// A client SHOULD NOT unilaterally close the connection, and instead SHOULD issue a LOGOUT command.
    /// If the server detects that the client has unilaterally closed the connection, the server MAY omit the untagged BYE response and simply close its connection.
    Logout,

    IdleAuthenticated(Tag<'a>),

    IdleSelected(Tag<'a>, Mailbox<'a>),
}

#[cfg(test)]
mod tests {
    #[cfg(feature = "bounded-static")]
    use bounded_static::{IntoBoundedStatic, ToBoundedStatic};

    use super::*;
    use crate::{core::Tag, mailbox::Mailbox};

    #[test]
    fn test_conversion() {
        let tests = [
            State::Greeting,
            State::NotAuthenticated,
            State::Authenticated,
            State::Selected(Mailbox::Inbox),
            State::Logout,
            State::IdleAuthenticated(Tag::try_from("A").unwrap()),
            State::IdleSelected(Tag::try_from("A").unwrap(), Mailbox::Inbox),
        ];

        for _test in tests {
            #[cfg(feature = "bounded-static")]
            {
                let test_to_static = _test.to_static();
                assert_eq!(_test, test_to_static);

                let test_into_static = _test.into_static();
                assert_eq!(test_to_static, test_into_static);
            }
        }
    }
}