1use core::{fmt, marker::PhantomData, mem};
6
7use alloc::{string::String, vec::Vec};
8
9use bounded_static::IntoBoundedStatic;
10use log::{debug, trace};
11use thiserror::Error;
12
13use crate::{
14 coroutine::*,
15 rfc5321::SmtpResponse,
16 utils::{escape_byte_string, parsers::format_rich_errors},
17};
18
19#[derive(Clone, Debug, Error)]
21pub enum SmtpCommandSendError {
22 #[error("Reached unexpected EOF on SMTP stream")]
24 Eof,
25 #[error("Parse SMTP response error: {0}")]
27 ParseResponse(String),
28}
29
30pub struct SmtpCommandSendOk {
32 pub response: SmtpResponse<'static>,
34}
35
36enum State {
37 Write,
38 Read,
39 Parse,
40}
41
42impl fmt::Display for State {
43 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44 match self {
45 Self::Write => f.write_str("write command"),
46 Self::Read => f.write_str("read response"),
47 Self::Parse => f.write_str("parse response"),
48 }
49 }
50}
51
52pub struct SmtpCommandSend<Cmd> {
56 bytes: Option<Vec<u8>>,
57 state: State,
58 wants_read: bool,
59 buf: Vec<u8>,
60 _cmd: PhantomData<Cmd>,
61}
62
63impl<Cmd: Into<Vec<u8>>> SmtpCommandSend<Cmd> {
64 pub fn new(cmd: Cmd) -> Self {
66 Self {
67 bytes: Some(cmd.into()),
68 state: State::Write,
69 wants_read: false,
70 buf: Vec::new(),
71 _cmd: PhantomData,
72 }
73 }
74}
75
76impl<Cmd> SmtpCoroutine for SmtpCommandSend<Cmd> {
77 type Yield = SmtpYield;
78 type Return = Result<SmtpCommandSendOk, SmtpCommandSendError>;
79
80 fn resume(&mut self, mut arg: Option<&[u8]>) -> SmtpCoroutineState<Self::Yield, Self::Return> {
81 loop {
82 if mem::take(&mut self.wants_read) {
83 return SmtpCoroutineState::Yielded(SmtpYield::WantsRead);
84 }
85
86 match &mut self.state {
87 State::Write => {
88 let bytes = self.bytes.take().expect("command bytes taken twice");
89 self.state = State::Read;
90 debug!("command sent, awaiting response");
91 return SmtpCoroutineState::Yielded(SmtpYield::WantsWrite(bytes));
92 }
93 State::Read => match arg.take() {
94 Some(&[]) => {
95 return SmtpCoroutineState::Complete(Err(SmtpCommandSendError::Eof));
96 }
97 Some(data) => {
98 trace!("read bytes: {}", escape_byte_string(data));
99 self.buf.extend_from_slice(data);
100
101 if !SmtpResponse::is_complete(&self.buf) {
102 self.wants_read = true;
103 continue;
104 }
105
106 self.state = State::Parse;
107 debug!("response complete, parsing");
108 }
109 None => {
110 self.wants_read = true;
111 }
112 },
113 State::Parse => {
114 return match SmtpResponse::parse(&self.buf) {
115 Ok(response) => {
116 let response = response.into_static();
117 let _ = mem::take(&mut self.buf);
118 debug!("response parsed");
119 trace!("{response:?}");
120 SmtpCoroutineState::Complete(Ok(SmtpCommandSendOk { response }))
121 }
122 Err(errors) => {
123 let reason = format_rich_errors(errors);
124 let err = SmtpCommandSendError::ParseResponse(reason);
125 SmtpCoroutineState::Complete(Err(err))
126 }
127 };
128 }
129 }
130 }
131 }
132}