+209 -19 +/-4 browse
1 | diff --git a/Makefile b/Makefile |
2 | index 54adb8b..30457cd 100644 |
3 | --- a/Makefile |
4 | +++ b/Makefile |
5 | @@ -25,3 +25,7 @@ test: check lint |
6 | .PHONY: rustdoc |
7 | rustdoc: |
8 | @RUSTDOCFLAGS="--html-before-content ./.github/doc_extra.html" cargo doc --workspace --all-features --no-deps --document-private-items |
9 | + |
10 | + .PHONY: rustdoc-open |
11 | + rustdoc-open: |
12 | + @RUSTDOCFLAGS="--html-before-content ./.github/doc_extra.html" cargo doc --workspace --all-features --no-deps --document-private-items --open |
13 | diff --git a/core/src/connection.rs b/core/src/connection.rs |
14 | index 235e878..7d3e619 100644 |
15 | --- a/core/src/connection.rs |
16 | +++ b/core/src/connection.rs |
17 | @@ -138,6 +138,41 @@ impl Connection { |
18 | /// `Connection` supports a limited subset of operations by default (see |
19 | /// [`Connection::untrusted`]). |
20 | /// Use [`Connection::trusted`] to remove these limits. |
21 | + /// |
22 | + /// # Example |
23 | + /// |
24 | + /// ```rust |
25 | + /// use mailpot::{Connection, Configuration}; |
26 | + /// use melib::smtp::{SmtpServerConf, SmtpAuth, SmtpSecurity}; |
27 | + /// # |
28 | + /// # fn main() -> mailpot::Result<()> { |
29 | + /// # use tempfile::TempDir; |
30 | + /// # |
31 | + /// # let tmp_dir = TempDir::new()?; |
32 | + /// # let db_path = tmp_dir.path().join("mpot.db"); |
33 | + /// # let data_path = tmp_dir.path().to_path_buf(); |
34 | + /// let config = Configuration { |
35 | + /// send_mail: mailpot::SendMail::Smtp( |
36 | + /// SmtpServerConf { |
37 | + /// hostname: "127.0.0.1".into(), |
38 | + /// port: 25, |
39 | + /// envelope_from: "foo-chat@example.com".into(), |
40 | + /// auth: SmtpAuth::None, |
41 | + /// security: SmtpSecurity::None, |
42 | + /// extensions: Default::default(), |
43 | + /// } |
44 | + /// ), |
45 | + /// db_path, |
46 | + /// data_path, |
47 | + /// administrators: vec![], |
48 | + /// }; |
49 | + /// # assert_eq!(&Connection::open_db(config.clone()).unwrap_err().to_string(), "Database doesn't exist"); |
50 | + /// |
51 | + /// let db = Connection::open_or_create_db(config)?; |
52 | + /// # _ = db; |
53 | + /// # Ok(()) |
54 | + /// # } |
55 | + /// ``` |
56 | pub fn open_db(conf: Configuration) -> Result<Self> { |
57 | use std::sync::Once; |
58 | |
59 | diff --git a/core/src/doctests/db_setup.rs.inc b/core/src/doctests/db_setup.rs.inc |
60 | new file mode 100644 |
61 | index 0000000..bb38811 |
62 | --- /dev/null |
63 | +++ b/core/src/doctests/db_setup.rs.inc |
64 | @@ -0,0 +1,52 @@ |
65 | + # use mailpot::{*, models::*}; |
66 | + # use melib::smtp::{SmtpServerConf, SmtpAuth, SmtpSecurity}; |
67 | + # |
68 | + # use tempfile::TempDir; |
69 | + # |
70 | + # let tmp_dir = TempDir::new()?; |
71 | + # let db_path = tmp_dir.path().join("mpot.db"); |
72 | + # let data_path = tmp_dir.path().to_path_buf(); |
73 | + # let config = Configuration { |
74 | + # send_mail: mailpot::SendMail::Smtp( |
75 | + # SmtpServerConf { |
76 | + # hostname: "127.0.0.1".into(), |
77 | + # port: 25, |
78 | + # envelope_from: "foo-chat@example.com".into(), |
79 | + # auth: SmtpAuth::None, |
80 | + # security: SmtpSecurity::None, |
81 | + # extensions: Default::default(), |
82 | + # } |
83 | + # ), |
84 | + # db_path, |
85 | + # data_path, |
86 | + # administrators: vec![], |
87 | + # }; |
88 | + # let db = Connection::open_or_create_db(config)?.trusted(); |
89 | + # let list = db |
90 | + # .create_list(MailingList { |
91 | + # pk: 5, |
92 | + # name: "foobar chat".into(), |
93 | + # id: "foo-chat".into(), |
94 | + # address: "foo-chat@example.com".into(), |
95 | + # description: Some("Hello world, from foo-chat list".into()), |
96 | + # archive_url: Some("https://lists.example.com".into()), |
97 | + # }) |
98 | + # .unwrap(); |
99 | + # let sub_policy = SubscriptionPolicy { |
100 | + # pk: 1, |
101 | + # list: 5, |
102 | + # send_confirmation: true, |
103 | + # open: false, |
104 | + # manual: false, |
105 | + # request: true, |
106 | + # custom: false, |
107 | + # }; |
108 | + # let post_policy = PostPolicy { |
109 | + # pk: 1, |
110 | + # list: 5, |
111 | + # announce_only: false, |
112 | + # subscription_only: false, |
113 | + # approval_needed: false, |
114 | + # open: true, |
115 | + # custom: false, |
116 | + # }; |
117 | diff --git a/core/src/models.rs b/core/src/models.rs |
118 | index d743829..fbac235 100644 |
119 | --- a/core/src/models.rs |
120 | +++ b/core/src/models.rs |
121 | @@ -28,6 +28,18 @@ use std::borrow::Cow; |
122 | use melib::email::Address; |
123 | |
124 | /// A database entry and its primary key. Derefs to its inner type. |
125 | + /// |
126 | + /// # Example |
127 | + /// |
128 | + /// ```rust,no_run |
129 | + /// # use mailpot::{*, models::*}; |
130 | + /// # fn foo(db: &Connection) { |
131 | + /// let val: Option<DbVal<MailingList>> = db.list(5).unwrap(); |
132 | + /// if let Some(list) = val { |
133 | + /// assert_eq!(list.pk(), 5); |
134 | + /// } |
135 | + /// # } |
136 | + /// ``` |
137 | #[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] |
138 | #[serde(transparent)] |
139 | pub struct DbVal<T>(pub T, #[serde(skip)] pub i64); |
140 | @@ -105,13 +117,34 @@ impl std::fmt::Display for MailingList { |
141 | } |
142 | |
143 | impl MailingList { |
144 | - /// Mailing list display name (e.g. `list name <list_address@example.com>`). |
145 | + /// Mailing list display name. |
146 | + /// |
147 | + /// # Example |
148 | + /// |
149 | + /// ```rust |
150 | + /// # fn main() -> mailpot::Result<()> { |
151 | + #[doc = include_str!("./doctests/db_setup.rs.inc")] |
152 | + /// assert_eq!( |
153 | + /// &list.display_name(), |
154 | + /// "\"foobar chat\" <foo-chat@example.com>" |
155 | + /// ); |
156 | + /// # Ok(()) |
157 | + /// # } |
158 | pub fn display_name(&self) -> String { |
159 | format!("\"{}\" <{}>", self.name, self.address) |
160 | } |
161 | |
162 | #[inline] |
163 | /// Request subaddress. |
164 | + /// |
165 | + /// # Example |
166 | + /// |
167 | + /// ```rust |
168 | + /// # fn main() -> mailpot::Result<()> { |
169 | + #[doc = include_str!("./doctests/db_setup.rs.inc")] |
170 | + /// assert_eq!(&list.request_subaddr(), "foo-chat+request@example.com"); |
171 | + /// # Ok(()) |
172 | + /// # } |
173 | pub fn request_subaddr(&self) -> String { |
174 | let p = self.address.split('@').collect::<Vec<&str>>(); |
175 | format!("{}+request@{}", p[0], p[1]) |
176 | @@ -120,6 +153,17 @@ impl MailingList { |
177 | /// Value of `List-Id` header. |
178 | /// |
179 | /// See RFC2919 Section 3: <https://www.rfc-editor.org/rfc/rfc2919> |
180 | + /// |
181 | + /// # Example |
182 | + /// |
183 | + /// ```rust |
184 | + /// # fn main() -> mailpot::Result<()> { |
185 | + #[doc = include_str!("./doctests/db_setup.rs.inc")] |
186 | + /// assert_eq!( |
187 | + /// &list.id_header(), |
188 | + /// "Hello world, from foo-chat list <foo-chat.example.com>"); |
189 | + /// # Ok(()) |
190 | + /// # } |
191 | pub fn id_header(&self) -> String { |
192 | let p = self.address.split('@').collect::<Vec<&str>>(); |
193 | format!( |
194 | @@ -134,6 +178,18 @@ impl MailingList { |
195 | /// Value of `List-Help` header. |
196 | /// |
197 | /// See RFC2369 Section 3.1: <https://www.rfc-editor.org/rfc/rfc2369#section-3.1> |
198 | + /// |
199 | + /// # Example |
200 | + /// |
201 | + /// ```rust |
202 | + /// # fn main() -> mailpot::Result<()> { |
203 | + #[doc = include_str!("./doctests/db_setup.rs.inc")] |
204 | + /// assert_eq!( |
205 | + /// &list.help_header().unwrap(), |
206 | + /// "<mailto:foo-chat+request@example.com?subject=help>" |
207 | + /// ); |
208 | + /// # Ok(()) |
209 | + /// # } |
210 | pub fn help_header(&self) -> Option<String> { |
211 | Some(format!("<mailto:{}?subject=help>", self.request_subaddr())) |
212 | } |
213 | @@ -141,6 +197,19 @@ impl MailingList { |
214 | /// Value of `List-Post` header. |
215 | /// |
216 | /// See RFC2369 Section 3.4: <https://www.rfc-editor.org/rfc/rfc2369#section-3.4> |
217 | + /// |
218 | + /// # Example |
219 | + /// |
220 | + /// ```rust |
221 | + /// # fn main() -> mailpot::Result<()> { |
222 | + #[doc = include_str!("./doctests/db_setup.rs.inc")] |
223 | + /// assert_eq!(&list.post_header(None).unwrap(), "NO"); |
224 | + /// assert_eq!( |
225 | + /// &list.post_header(Some(&post_policy)).unwrap(), |
226 | + /// "<mailto:foo-chat@example.com>" |
227 | + /// ); |
228 | + /// # Ok(()) |
229 | + /// # } |
230 | pub fn post_header(&self, policy: Option<&PostPolicy>) -> Option<String> { |
231 | Some(policy.map_or_else( |
232 | || "NO".to_string(), |
233 | @@ -157,18 +226,26 @@ impl MailingList { |
234 | /// Value of `List-Unsubscribe` header. |
235 | /// |
236 | /// See RFC2369 Section 3.2: <https://www.rfc-editor.org/rfc/rfc2369#section-3.2> |
237 | + /// |
238 | + /// # Example |
239 | + /// |
240 | + /// ```rust |
241 | + /// # fn main() -> mailpot::Result<()> { |
242 | + #[doc = include_str!("./doctests/db_setup.rs.inc")] |
243 | + /// assert_eq!( |
244 | + /// &list.unsubscribe_header(Some(&sub_policy)).unwrap(), |
245 | + /// "<mailto:foo-chat+request@example.com?subject=unsubscribe>" |
246 | + /// ); |
247 | + /// # Ok(()) |
248 | + /// # } |
249 | pub fn unsubscribe_header(&self, policy: Option<&SubscriptionPolicy>) -> Option<String> { |
250 | policy.map_or_else( |
251 | || None, |
252 | - |p| { |
253 | - if p.open { |
254 | - None |
255 | - } else { |
256 | - Some(format!( |
257 | - "<mailto:{}?subject=unsubscribe>", |
258 | - self.request_subaddr() |
259 | - )) |
260 | - } |
261 | + |_| { |
262 | + Some(format!( |
263 | + "<mailto:{}?subject=unsubscribe>", |
264 | + self.request_subaddr() |
265 | + )) |
266 | }, |
267 | ) |
268 | } |
269 | @@ -176,18 +253,27 @@ impl MailingList { |
270 | /// Value of `List-Subscribe` header. |
271 | /// |
272 | /// See RFC2369 Section 3.3: <https://www.rfc-editor.org/rfc/rfc2369#section-3.3> |
273 | + /// |
274 | + /// # Example |
275 | + /// |
276 | + /// ```rust |
277 | + /// # fn main() -> mailpot::Result<()> { |
278 | + #[doc = include_str!("./doctests/db_setup.rs.inc")] |
279 | + /// assert_eq!( |
280 | + /// &list.subscribe_header(Some(&sub_policy)).unwrap(), |
281 | + /// "<mailto:foo-chat+request@example.com?subject=subscribe>", |
282 | + /// ); |
283 | + /// # Ok(()) |
284 | + /// # } |
285 | + /// ``` |
286 | pub fn subscribe_header(&self, policy: Option<&SubscriptionPolicy>) -> Option<String> { |
287 | policy.map_or_else( |
288 | || None, |
289 | - |p| { |
290 | - if p.open { |
291 | - None |
292 | - } else { |
293 | - Some(format!( |
294 | - "<mailto:{}?subject=subscribe>", |
295 | - self.request_subaddr() |
296 | - )) |
297 | - } |
298 | + |_| { |
299 | + Some(format!( |
300 | + "<mailto:{}?subject=subscribe>", |
301 | + self.request_subaddr() |
302 | + )) |
303 | }, |
304 | ) |
305 | } |
306 | @@ -195,6 +281,19 @@ impl MailingList { |
307 | /// Value of `List-Archive` header. |
308 | /// |
309 | /// See RFC2369 Section 3.6: <https://www.rfc-editor.org/rfc/rfc2369#section-3.6> |
310 | + /// |
311 | + /// # Example |
312 | + /// |
313 | + /// ```rust |
314 | + /// # fn main() -> mailpot::Result<()> { |
315 | + #[doc = include_str!("./doctests/db_setup.rs.inc")] |
316 | + /// assert_eq!( |
317 | + /// &list.archive_header().unwrap(), |
318 | + /// "<https://lists.example.com>" |
319 | + /// ); |
320 | + /// # Ok(()) |
321 | + /// # } |
322 | + /// ``` |
323 | pub fn archive_header(&self) -> Option<String> { |
324 | self.archive_url.as_ref().map(|url| format!("<{}>", url)) |
325 | } |