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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
//! Streaming message body source for [`SmtpClient::send_mail_stream`].
//!
//! [`MessageBody`] is a project-defined async read abstraction that keeps
//! `wasm-smtp` runtime-independent. Unlike `tokio::io::AsyncRead`, it has
//! no executor dependency and can be implemented for any byte source.
//!
//! ## Built-in implementations
//!
//! | Type | Source | Notes |
//! |---|---|---|
//! | [`SliceBody`] | `&[u8]` | Zero-copy; useful for pre-serialised byte payloads |
//! | [`StrBody`] | `&str` | Zero-copy; same as `SliceBody` but from a string slice |
//!
//! ## Custom implementation
//!
//! Implement `MessageBody` for any async byte source:
//!
//! ```rust
//! use wasm_smtp::message_body::MessageBody;
//! use wasm_smtp::IoError;
//!
//! struct MyBodySource {
//! data: Vec<u8>,
//! pos: usize,
//! }
//!
//! impl MessageBody for MyBodySource {
//! async fn read_chunk(&mut self, buf: &mut [u8]) -> Result<usize, IoError> {
//! if self.pos >= self.data.len() {
//! return Ok(0); // EOF
//! }
//! let n = buf.len().min(self.data.len() - self.pos);
//! buf[..n].copy_from_slice(&self.data[self.pos..self.pos + n]);
//! self.pos += n;
//! Ok(n)
//! }
//! }
//! ```
//!
//! ## Body requirements
//!
//! The bytes supplied by [`MessageBody::read_chunk`] must be a fully composed
//! RFC 5322 message with **CRLF line endings**. Dot-stuffing and the
//! end-of-data terminator (`\r\n.\r\n`) are applied automatically by
//! [`SmtpClient::send_mail_stream`]; the caller must not add them.
use crateIoError;
// ---------------------------------------------------------------------------
// Trait
// ---------------------------------------------------------------------------
/// A streaming source of message body bytes.
///
/// `read_chunk` is called repeatedly until it returns `Ok(0)` (end of body).
/// Each call may fill any number of bytes into `buf[..n]` where `0 < n <=
/// buf.len()`. Returning `Ok(0)` signals end-of-stream and must not occur
/// before the full body has been supplied.
///
/// ## Body contract
///
/// - The bytes must form a valid RFC 5322 message (headers + blank line +
/// content) with **CRLF** (`\r\n`) line endings.
/// - Do **not** include the end-of-data terminator (`\r\n.\r\n`);
/// it is added automatically.
/// - Do **not** pre-dot-stuff lines starting with `.`; that is also handled
/// automatically.
///
/// ## Error handling
///
/// Returning `Err(IoError)` from `read_chunk` aborts the DATA phase and
/// surfaces the error to the caller of `send_mail_stream`. The SMTP session
/// is moved to `Closed`.
// ---------------------------------------------------------------------------
// SliceBody
// ---------------------------------------------------------------------------
/// A [`MessageBody`] that reads from a `&[u8]` slice.
///
/// The slice is read in chunks of `buf.len()` bytes. No allocation occurs.
///
/// ```rust
/// use wasm_smtp::message_body::SliceBody;
///
/// let body = b"Subject: hi\r\n\r\nhello\r\n";
/// let mut source = SliceBody::new(body);
/// ```
// ---------------------------------------------------------------------------
// StrBody
// ---------------------------------------------------------------------------
/// A [`MessageBody`] that reads from a `&str` string slice.
///
/// Equivalent to `SliceBody::new(s.as_bytes())`.
///
/// ```rust
/// use wasm_smtp::message_body::StrBody;
///
/// let body = "Subject: hi\r\n\r\nhello\r\n";
/// let mut source = StrBody::new(body);
/// ```
;