1#[cfg(feature = "serdex")]
2use serde::{Deserialize, Serialize};
3
4#[cfg_attr(feature = "serdex", derive(Serialize, Deserialize))]
6#[derive(Clone, Debug, Eq, PartialEq, Hash)]
7pub enum Command {
8 User(String),
12 Pass(String),
14 Stat,
17 ListAll,
19 List {
21 msg: u32,
22 },
23 Retr {
25 msg: u32,
26 },
27 Dele {
29 msg: u32,
30 },
31 Noop,
33 Rset,
35 Quit,
38
39 Apop {
43 name: String,
44 digest: String,
45 },
46 Top {
49 msg: u32,
50 n: u32,
51 },
52 UidlAll,
54 Uidl {
56 msg: u32,
57 },
58
59 Capa,
62
63 Stls,
66 AuthAll,
72 Auth {
73 mechanism: String,
74 initial_response: Option<String>,
75 },
76
77 Utf8,
79 LangAll,
80 Lang {
81 lang_or_wild: Language,
82 },
83}
84
85impl Command {
86 pub fn name(&self) -> &'static str {
87 match self {
88 Command::User(_) => "USER",
89 Command::Pass(_) => "PASS",
90 Command::Stat => "STAT",
91 Command::ListAll => "LISTALL",
92 Command::List { .. } => "LIST",
93 Command::Retr { .. } => "RETR",
94 Command::Dele { .. } => "DELE",
95 Command::Noop => "NOOP",
96 Command::Rset => "RSET",
97 Command::Quit => "QUIT",
98 Command::Apop { .. } => "APOP",
99 Command::Top { .. } => "TOP",
100 Command::UidlAll => "UIDLALL",
101 Command::Uidl { .. } => "UIDL",
102 Command::Capa => "CAPA",
103 Command::Stls => "STLS",
104 Command::AuthAll => "AUTHALL",
105 Command::Auth { .. } => "AUTH",
106 Command::Utf8 => "UTF8",
107 Command::LangAll => "LANGALL",
108 Command::Lang { .. } => "LANG",
109 }
110 }
111
112 pub fn serialize(&self) -> Vec<u8> {
113 match self {
114 Command::User(user) => format!("USER {}\r\n", user).into_bytes(),
115 Command::Pass(pass) => format!("PASS {}\r\n", pass).into_bytes(),
116 Command::Stat => b"STAT\r\n".to_vec(),
117 Command::ListAll => b"LIST\r\n".to_vec(),
118 Command::List { msg } => format!("LIST {}\r\n", msg).into_bytes(),
119 Command::Retr { msg } => format!("RETR {}\r\n", msg).into_bytes(),
120 Command::Dele { msg } => format!("DELE {}\r\n", msg).into_bytes(),
121 Command::Noop => b"NOOP\r\n".to_vec(),
122 Command::Rset => b"RSET\r\n".to_vec(),
123 Command::Quit => b"QUIT\r\n".to_vec(),
124 Command::Apop { name, digest } => format!("APOP {} {}\r\n", name, digest).into_bytes(),
125 Command::Top { msg, n } => format!("TOP {} {}\r\n", msg, n).into_bytes(),
126 Command::UidlAll => b"UIDL\r\n".to_vec(),
127 Command::Uidl { msg } => format!("UIDL {}\r\n", msg).into_bytes(),
128 Command::Capa => b"CAPA\r\n".to_vec(),
129 Command::Stls => b"STLS\r\n".to_vec(),
130 Command::AuthAll => b"AUTH\r\n".to_vec(),
131 Command::Auth {
132 mechanism,
133 initial_response,
134 } => match initial_response {
135 Some(initial_response) => {
136 format!("AUTH {} {}\r\n", mechanism, initial_response).into_bytes()
137 }
138 None => format!("AUTH {}\r\n", mechanism).into_bytes(),
139 },
140 Command::Utf8 => b"UTF8\r\n".to_vec(),
141 Command::LangAll => b"LANG\r\n".to_vec(),
142 Command::Lang { lang_or_wild } => {
143 format!("LANG {}\r\n", lang_or_wild.to_string()).into_bytes()
144 }
145 }
146 }
147}
148
149#[cfg_attr(feature = "serdex", derive(Serialize, Deserialize))]
150#[derive(Clone, Debug, Eq, PartialEq, Hash)]
151pub enum Language {
152 Lang(String),
153 Wild,
154}
155
156impl ToString for Language {
157 fn to_string(&self) -> String {
158 match self {
159 Language::Lang(lang) => lang.clone(),
160 Language::Wild => "*".to_owned(),
161 }
162 }
163}
164
165#[cfg(test)]
166mod test {
167 use super::Command;
168
169 #[test]
170 fn test_serialize() {
171 assert_eq!(Command::User("alice".into()).serialize(), b"USER alice\r\n");
172 assert_eq!(
173 Command::Pass("password".into()).serialize(),
174 b"PASS password\r\n"
175 );
176 assert_eq!(Command::Stat.serialize(), b"STAT\r\n");
177 assert_eq!(Command::ListAll.serialize(), b"LIST\r\n");
178 assert_eq!(Command::List { msg: 1 }.serialize(), b"LIST 1\r\n");
179 assert_eq!(Command::Retr { msg: 1 }.serialize(), b"RETR 1\r\n");
180 assert_eq!(Command::Dele { msg: 1 }.serialize(), b"DELE 1\r\n");
181 assert_eq!(Command::Noop.serialize(), b"NOOP\r\n");
182 assert_eq!(Command::Rset.serialize(), b"RSET\r\n");
183 assert_eq!(Command::Quit.serialize(), b"QUIT\r\n");
184 assert_eq!(
185 Command::Apop {
186 name: "alice".into(),
187 digest: "aabbccddeeff".into()
188 }
189 .serialize(),
190 b"APOP alice aabbccddeeff\r\n"
191 );
192 assert_eq!(Command::Top { msg: 1, n: 5 }.serialize(), b"TOP 1 5\r\n");
193 assert_eq!(Command::UidlAll.serialize(), b"UIDL\r\n");
194 assert_eq!(Command::Uidl { msg: 1 }.serialize(), b"UIDL 1\r\n");
195 assert_eq!(Command::Capa.serialize(), b"CAPA\r\n");
196 assert_eq!(Command::Stls.serialize(), b"STLS\r\n");
197 assert_eq!(
198 Command::Auth {
199 mechanism: "PLAIN".into(),
200 initial_response: None
201 }
202 .serialize(),
203 b"AUTH PLAIN\r\n"
204 );
205 assert_eq!(
206 Command::Auth {
207 mechanism: "PLAIN".into(),
208 initial_response: Some("XXX".into())
209 }
210 .serialize(),
211 b"AUTH PLAIN XXX\r\n"
212 );
213 assert_eq!(Command::AuthAll.serialize(), b"AUTH\r\n");
214 }
215}