+352 -107 +/-4 browse
1 | diff --git a/.github/workflows/grcov.yaml b/.github/workflows/grcov.yaml |
2 | index 6646048..ffa1c09 100644 |
3 | --- a/.github/workflows/grcov.yaml |
4 | +++ b/.github/workflows/grcov.yaml |
5 | @@ -38,8 +38,8 @@ jobs: |
6 | args: --all --all-features --no-fail-fast |
7 | env: |
8 | CARGO_INCREMENTAL: '0' |
9 | - RUSTFLAGS: '-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Cpanic=abort -Zpanic_abort_tests' |
10 | - RUSTDOCFLAGS: '-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Cpanic=abort -Zpanic_abort_tests' |
11 | + RUSTFLAGS: '-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Cpanic=abort -Zpanic_abort_tests -Cinstrument-coverage' |
12 | + RUSTDOCFLAGS: '-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Cpanic=abort -Zpanic_abort_tests -Cinstrument-coverage' |
13 | - uses: actions-rs/grcov@v0.1 |
14 | with: |
15 | config: .github/grcov.yml |
16 | diff --git a/core/src/connection.rs b/core/src/connection.rs |
17 | index 875d451..2c4580e 100644 |
18 | --- a/core/src/connection.rs |
19 | +++ b/core/src/connection.rs |
20 | @@ -141,7 +141,7 @@ impl Connection { |
21 | /// |
22 | /// # Example |
23 | /// |
24 | - /// ```rust |
25 | + /// ```rust,no_run |
26 | /// use mailpot::{Connection, Configuration}; |
27 | /// use melib::smtp::{SmtpServerConf, SmtpAuth, SmtpSecurity}; |
28 | /// # |
29 | @@ -807,3 +807,39 @@ pub mod transaction { |
30 | Panic, |
31 | } |
32 | } |
33 | + |
34 | + #[cfg(test)] |
35 | + mod tests { |
36 | + use super::*; |
37 | + |
38 | + #[test] |
39 | + fn test_new_connection() { |
40 | + use melib::smtp::{SmtpAuth, SmtpSecurity, SmtpServerConf}; |
41 | + use tempfile::TempDir; |
42 | + |
43 | + use crate::SendMail; |
44 | + |
45 | + let tmp_dir = TempDir::new().unwrap(); |
46 | + let db_path = tmp_dir.path().join("mpot.db"); |
47 | + let data_path = tmp_dir.path().to_path_buf(); |
48 | + let config = Configuration { |
49 | + send_mail: SendMail::Smtp(SmtpServerConf { |
50 | + hostname: "127.0.0.1".into(), |
51 | + port: 25, |
52 | + envelope_from: "foo-chat@example.com".into(), |
53 | + auth: SmtpAuth::None, |
54 | + security: SmtpSecurity::None, |
55 | + extensions: Default::default(), |
56 | + }), |
57 | + db_path, |
58 | + data_path, |
59 | + administrators: vec![], |
60 | + }; |
61 | + assert_eq!( |
62 | + &Connection::open_db(config.clone()).unwrap_err().to_string(), |
63 | + "Database doesn't exist" |
64 | + ); |
65 | + |
66 | + _ = Connection::open_or_create_db(config).unwrap(); |
67 | + } |
68 | + } |
69 | diff --git a/web/src/minijinja_utils.rs b/web/src/minijinja_utils.rs |
70 | index 5a1bdd0..684ffb6 100644 |
71 | --- a/web/src/minijinja_utils.rs |
72 | +++ b/web/src/minijinja_utils.rs |
73 | @@ -189,44 +189,6 @@ impl minijinja::value::StructObject for MailingList { |
74 | |
75 | /// Return a vector of weeks, with each week being a vector of 7 days and |
76 | /// corresponding sum of posts per day. |
77 | - /// |
78 | - /// |
79 | - /// # Example |
80 | - /// |
81 | - /// ```rust |
82 | - /// # use mailpot_web::minijinja_utils::calendarize; |
83 | - /// # use minijinja::Environment; |
84 | - /// # use minijinja::value::Value; |
85 | - /// # use std::collections::HashMap; |
86 | - /// |
87 | - /// let mut env = Environment::new(); |
88 | - /// env.add_function("calendarize", calendarize); |
89 | - /// |
90 | - /// let month = "2001-09"; |
91 | - /// let mut hist = [0usize; 31]; |
92 | - /// hist[15] = 5; |
93 | - /// hist[1] = 1; |
94 | - /// hist[0] = 512; |
95 | - /// hist[30] = 30; |
96 | - /// assert_eq!( |
97 | - /// &env.render_str( |
98 | - /// "{% set c=calendarize(month, hists) %}Month: {{ c.month }} Month Name: {{ \ |
99 | - /// c.month_name }} Month Int: {{ c.month_int }} Year: {{ c.year }} Sum: {{ c.sum }} {% \ |
100 | - /// for week in c.weeks %}{% for day in week %}{% set num = c.hist[day-1] %}({{ day }}, \ |
101 | - /// {{ num }}){% endfor %}{% endfor %}", |
102 | - /// minijinja::context! { |
103 | - /// month, |
104 | - /// hists => vec![(month.to_string(), hist)].into_iter().collect::<HashMap<String, [usize; |
105 | - /// 31]>>(), |
106 | - /// } |
107 | - /// ) |
108 | - /// .unwrap(), |
109 | - /// "Month: 2001-09 Month Name: September Month Int: 9 Year: 2001 Sum: 548 (0, 30)(0, 30)(0, \ |
110 | - /// 30)(0, 30)(0, 30)(1, 512)(2, 1)(3, 0)(4, 0)(5, 0)(6, 0)(7, 0)(8, 0)(9, 0)(10, 0)(11, \ |
111 | - /// 0)(12, 0)(13, 0)(14, 0)(15, 0)(16, 5)(17, 0)(18, 0)(19, 0)(20, 0)(21, 0)(22, 0)(23, \ |
112 | - /// 0)(24, 0)(25, 0)(26, 0)(27, 0)(28, 0)(29, 0)(30, 0)" |
113 | - /// ); |
114 | - /// ``` |
115 | pub fn calendarize( |
116 | _state: &minijinja::State, |
117 | args: Value, |
118 | @@ -294,7 +256,7 @@ pub fn calendarize( |
119 | /// |
120 | /// # Examples |
121 | /// |
122 | - /// ```rust |
123 | + /// ```rust,no_run |
124 | /// # use mailpot_web::pluralize; |
125 | /// # use minijinja::Environment; |
126 | /// |
127 | @@ -436,26 +398,6 @@ pub fn pluralize( |
128 | /// `strip_carets` filter for [`minijinja`]. |
129 | /// |
130 | /// Removes `[<>]` from message ids. |
131 | - /// |
132 | - /// # Examples |
133 | - /// |
134 | - /// ```rust |
135 | - /// # use mailpot_web::strip_carets; |
136 | - /// # use minijinja::Environment; |
137 | - /// |
138 | - /// let mut env = Environment::new(); |
139 | - /// env.add_filter("strip_carets", strip_carets); |
140 | - /// assert_eq!( |
141 | - /// &env.render_str( |
142 | - /// "{{ msg_id | strip_carets }}", |
143 | - /// minijinja::context! { |
144 | - /// msg_id => "<hello1@example.com>", |
145 | - /// } |
146 | - /// ) |
147 | - /// .unwrap(), |
148 | - /// "hello1@example.com", |
149 | - /// ); |
150 | - /// ``` |
151 | pub fn strip_carets(_state: &minijinja::State, arg: Value) -> std::result::Result<Value, Error> { |
152 | Ok(Value::from( |
153 | arg.as_str() |
154 | @@ -475,7 +417,7 @@ pub fn strip_carets(_state: &minijinja::State, arg: Value) -> std::result::Resul |
155 | /// |
156 | /// # Examples |
157 | /// |
158 | - /// ```rust |
159 | + /// ```rust,no_run |
160 | /// # use mailpot_web::urlize; |
161 | /// # use minijinja::Environment; |
162 | /// # use minijinja::value::Value; |
163 | @@ -505,7 +447,7 @@ pub fn urlize(state: &minijinja::State, arg: Value) -> std::result::Result<Value |
164 | /// Make an html heading: `h1, h2, h3` etc. |
165 | /// |
166 | /// # Example |
167 | - /// ``` |
168 | + /// ```rust,no_run |
169 | /// use mailpot_web::minijinja_utils::heading; |
170 | /// use minijinja::value::Value; |
171 | /// |
172 | @@ -601,3 +543,227 @@ pub fn heading(level: Value, text: Value, id: Option<Value>) -> std::result::Res |
173 | ))) |
174 | } |
175 | } |
176 | + |
177 | + #[cfg(test)] |
178 | + mod tests { |
179 | + use super::*; |
180 | + |
181 | + #[test] |
182 | + fn test_pluralize() { |
183 | + let mut env = Environment::new(); |
184 | + env.add_filter("pluralize", pluralize); |
185 | + for (num, s) in [ |
186 | + (0, "You have 0 messages."), |
187 | + (1, "You have 1 message."), |
188 | + (10, "You have 10 messages."), |
189 | + ] { |
190 | + assert_eq!( |
191 | + &env.render_str( |
192 | + "You have {{ num_messages }} message{{ num_messages|pluralize }}.", |
193 | + minijinja::context! { |
194 | + num_messages => num, |
195 | + } |
196 | + ) |
197 | + .unwrap(), |
198 | + s |
199 | + ); |
200 | + } |
201 | + |
202 | + for (num, s) in [ |
203 | + (0, "You have 0 walruses."), |
204 | + (1, "You have 1 walrus."), |
205 | + (10, "You have 10 walruses."), |
206 | + ] { |
207 | + assert_eq!( |
208 | + &env.render_str( |
209 | + r#"You have {{ num_walruses }} walrus{{ num_walruses|pluralize(None, "es") }}."#, |
210 | + minijinja::context! { |
211 | + num_walruses => num, |
212 | + } |
213 | + ) |
214 | + .unwrap(), |
215 | + s |
216 | + ); |
217 | + } |
218 | + |
219 | + for (num, s) in [ |
220 | + (0, "You have 0 cherries."), |
221 | + (1, "You have 1 cherry."), |
222 | + (10, "You have 10 cherries."), |
223 | + ] { |
224 | + assert_eq!( |
225 | + &env.render_str( |
226 | + r#"You have {{ num_cherries }} cherr{{ num_cherries|pluralize("y", "ies") }}."#, |
227 | + minijinja::context! { |
228 | + num_cherries => num, |
229 | + } |
230 | + ) |
231 | + .unwrap(), |
232 | + s |
233 | + ); |
234 | + } |
235 | + |
236 | + assert_eq!( |
237 | + &env.render_str( |
238 | + r#"You have {{ num_cherries|length }} cherr{{ num_cherries|pluralize("y", "ies") }}."#, |
239 | + minijinja::context! { |
240 | + num_cherries => vec![(); 5], |
241 | + } |
242 | + ) |
243 | + .unwrap(), |
244 | + "You have 5 cherries." |
245 | + ); |
246 | + |
247 | + assert_eq!( |
248 | + &env.render_str( |
249 | + r#"You have {{ num_cherries }} cherr{{ num_cherries|pluralize("y", "ies") }}."#, |
250 | + minijinja::context! { |
251 | + num_cherries => "5", |
252 | + } |
253 | + ) |
254 | + .unwrap(), |
255 | + "You have 5 cherries." |
256 | + ); |
257 | + assert_eq!( |
258 | + &env.render_str( |
259 | + r#"You have 1 cherr{{ num_cherries|pluralize("y", "ies") }}."#, |
260 | + minijinja::context! { |
261 | + num_cherries => true, |
262 | + } |
263 | + ) |
264 | + .unwrap(), |
265 | + "You have 1 cherry.", |
266 | + ); |
267 | + assert_eq!( |
268 | + &env.render_str( |
269 | + r#"You have {{ num_cherries }} cherr{{ num_cherries|pluralize("y", "ies") }}."#, |
270 | + minijinja::context! { |
271 | + num_cherries => 0.5f32, |
272 | + } |
273 | + ) |
274 | + .unwrap_err() |
275 | + .to_string(), |
276 | + "invalid operation: Pluralize argument is not an integer, or a sequence / object with \ |
277 | + a length but of type number (in <string>:1)", |
278 | + ); |
279 | + } |
280 | + |
281 | + #[test] |
282 | + fn test_urlize() { |
283 | + let mut env = Environment::new(); |
284 | + env.add_function("urlize", urlize); |
285 | + env.add_global( |
286 | + "root_url_prefix", |
287 | + Value::from_safe_string("/lists/prefix/".to_string()), |
288 | + ); |
289 | + assert_eq!( |
290 | + &env.render_str( |
291 | + "<a href=\"{{ urlize(\"path/index.html\") }}\">link</a>", |
292 | + minijinja::context! {} |
293 | + ) |
294 | + .unwrap(), |
295 | + "<a href=\"/lists/prefix/path/index.html\">link</a>", |
296 | + ); |
297 | + } |
298 | + |
299 | + #[test] |
300 | + fn test_heading() { |
301 | + assert_eq!( |
302 | + "<h1 id=\"bl-bfa-b-ah-b-asdb-hadas-d\">bl bfa B AH bAsdb hadas d<a \ |
303 | + class=\"self-link\" href=\"#bl-bfa-b-ah-b-asdb-hadas-d\"></a></h1>", |
304 | + &heading(1.into(), "bl bfa B AH bAsdb hadas d".into(), None) |
305 | + .unwrap() |
306 | + .to_string() |
307 | + ); |
308 | + assert_eq!( |
309 | + "<h2 id=\"short\">bl bfa B AH bAsdb hadas d<a class=\"self-link\" \ |
310 | + href=\"#short\"></a></h2>", |
311 | + &heading( |
312 | + 2.into(), |
313 | + "bl bfa B AH bAsdb hadas d".into(), |
314 | + Some("short".into()) |
315 | + ) |
316 | + .unwrap() |
317 | + .to_string() |
318 | + ); |
319 | + assert_eq!( |
320 | + r#"invalid operation: first heading() argument must be an unsigned integer less than 7 and positive"#, |
321 | + &heading( |
322 | + 0.into(), |
323 | + "bl bfa B AH bAsdb hadas d".into(), |
324 | + Some("short".into()) |
325 | + ) |
326 | + .unwrap_err() |
327 | + .to_string() |
328 | + ); |
329 | + assert_eq!( |
330 | + r#"invalid operation: first heading() argument must be an unsigned integer less than 7 and positive"#, |
331 | + &heading( |
332 | + 8.into(), |
333 | + "bl bfa B AH bAsdb hadas d".into(), |
334 | + Some("short".into()) |
335 | + ) |
336 | + .unwrap_err() |
337 | + .to_string() |
338 | + ); |
339 | + assert_eq!( |
340 | + r#"invalid operation: first heading() argument is not an integer < 7 but of type sequence"#, |
341 | + &heading( |
342 | + Value::from(vec![Value::from(1)]), |
343 | + "bl bfa B AH bAsdb hadas d".into(), |
344 | + Some("short".into()) |
345 | + ) |
346 | + .unwrap_err() |
347 | + .to_string() |
348 | + ); |
349 | + } |
350 | + |
351 | + #[test] |
352 | + fn test_strip_carets() { |
353 | + let mut env = Environment::new(); |
354 | + env.add_filter("strip_carets", strip_carets); |
355 | + assert_eq!( |
356 | + &env.render_str( |
357 | + "{{ msg_id | strip_carets }}", |
358 | + minijinja::context! { |
359 | + msg_id => "<hello1@example.com>", |
360 | + } |
361 | + ) |
362 | + .unwrap(), |
363 | + "hello1@example.com", |
364 | + ); |
365 | + } |
366 | + |
367 | + #[test] |
368 | + fn test_calendarize() { |
369 | + use std::collections::HashMap; |
370 | + |
371 | + let mut env = Environment::new(); |
372 | + env.add_function("calendarize", calendarize); |
373 | + |
374 | + let month = "2001-09"; |
375 | + let mut hist = [0usize; 31]; |
376 | + hist[15] = 5; |
377 | + hist[1] = 1; |
378 | + hist[0] = 512; |
379 | + hist[30] = 30; |
380 | + assert_eq!( |
381 | + &env.render_str( |
382 | + "{% set c=calendarize(month, hists) %}Month: {{ c.month }} Month Name: {{ \ |
383 | + c.month_name }} Month Int: {{ c.month_int }} Year: {{ c.year }} Sum: {{ c.sum }} {% \ |
384 | + for week in c.weeks %}{% for day in week %}{% set num = c.hist[day-1] %}({{ day }}, \ |
385 | + {{ num }}){% endfor %}{% endfor %}", |
386 | + minijinja::context! { |
387 | + month, |
388 | + hists => vec![(month.to_string(), hist)].into_iter().collect::<HashMap<String, [usize; |
389 | + 31]>>(), |
390 | + } |
391 | + ) |
392 | + .unwrap(), |
393 | + "Month: 2001-09 Month Name: September Month Int: 9 Year: 2001 Sum: 548 (0, 30)(0, 30)(0, \ |
394 | + 30)(0, 30)(0, 30)(1, 512)(2, 1)(3, 0)(4, 0)(5, 0)(6, 0)(7, 0)(8, 0)(9, 0)(10, 0)(11, \ |
395 | + 0)(12, 0)(13, 0)(14, 0)(15, 0)(16, 5)(17, 0)(18, 0)(19, 0)(20, 0)(21, 0)(22, 0)(23, \ |
396 | + 0)(24, 0)(25, 0)(26, 0)(27, 0)(28, 0)(29, 0)(30, 0)" |
397 | + ); |
398 | + } |
399 | + } |
400 | diff --git a/web/src/utils.rs b/web/src/utils.rs |
401 | index b99b866..4bfa0a4 100644 |
402 | --- a/web/src/utils.rs |
403 | +++ b/web/src/utils.rs |
404 | @@ -66,7 +66,7 @@ impl Message { |
405 | /// |
406 | /// # Example |
407 | /// |
408 | - /// ```rust |
409 | + /// ```no_run |
410 | /// # use mailpot_web::utils::{Message, Level, SessionMessages}; |
411 | /// struct Session(Vec<Message>); |
412 | /// |
413 | @@ -135,17 +135,6 @@ impl SessionMessages for WritableSession { |
414 | |
415 | /// Deserialize a string integer into `i64`, because POST parameters are |
416 | /// strings. |
417 | - /// |
418 | - /// ``` |
419 | - /// # use mailpot_web::utils::IntPOST; |
420 | - /// # use mailpot::serde_json::{self, json}; |
421 | - /// assert_eq!( |
422 | - /// IntPOST(5), |
423 | - /// serde_json::from_str::<IntPOST>("\"5\"").unwrap() |
424 | - /// ); |
425 | - /// assert_eq!(IntPOST(5), serde_json::from_str::<IntPOST>("5").unwrap()); |
426 | - /// assert_eq!(&json! { IntPOST(5) }.to_string(), "5"); |
427 | - /// ``` |
428 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Hash)] |
429 | #[repr(transparent)] |
430 | pub struct IntPOST(pub i64); |
431 | @@ -201,20 +190,6 @@ impl<'de> serde::Deserialize<'de> for IntPOST { |
432 | |
433 | /// Deserialize a string integer into `bool`, because POST parameters are |
434 | /// strings. |
435 | - /// |
436 | - /// ``` |
437 | - /// # use mailpot_web::utils::BoolPOST; |
438 | - /// # use mailpot::serde_json::{self, json}; |
439 | - /// assert_eq!( |
440 | - /// BoolPOST(true), |
441 | - /// serde_json::from_str::<BoolPOST>("true").unwrap() |
442 | - /// ); |
443 | - /// assert_eq!( |
444 | - /// BoolPOST(true), |
445 | - /// serde_json::from_str::<BoolPOST>("\"true\"").unwrap() |
446 | - /// ); |
447 | - /// assert_eq!(&json! { BoolPOST(false) }.to_string(), "false"); |
448 | - /// ``` |
449 | #[derive(Clone, Copy, Default, Debug, PartialEq, Eq, PartialOrd, Hash)] |
450 | #[repr(transparent)] |
451 | pub struct BoolPOST(pub bool); |
452 | @@ -261,23 +236,6 @@ impl<'de> serde::Deserialize<'de> for BoolPOST { |
453 | } |
454 | } |
455 | |
456 | - /// ``` |
457 | - /// use axum::response::Redirect; |
458 | - /// use mailpot_web::Next; |
459 | - /// |
460 | - /// let next = Next { |
461 | - /// next: Some("foo".to_string()), |
462 | - /// }; |
463 | - /// assert_eq!( |
464 | - /// format!("{:?}", Redirect::to("foo")), |
465 | - /// format!("{:?}", next.or_else(|| "bar".to_string())) |
466 | - /// ); |
467 | - /// let next = Next { next: None }; |
468 | - /// assert_eq!( |
469 | - /// format!("{:?}", Redirect::to("bar")), |
470 | - /// format!("{:?}", next.or_else(|| "bar".to_string())) |
471 | - /// ); |
472 | - /// ``` |
473 | #[derive(Debug, Clone, serde::Deserialize)] |
474 | pub struct Next { |
475 | #[serde(default, deserialize_with = "empty_string_as_none")] |
476 | @@ -530,3 +488,88 @@ pub fn thread_roots( |
477 | ret.sort_by_key(|(_, _, key)| std::cmp::Reverse(*key)); |
478 | ret |
479 | } |
480 | + |
481 | + #[cfg(test)] |
482 | + mod tests { |
483 | + use super::*; |
484 | + |
485 | + #[test] |
486 | + fn test_session() { |
487 | + struct Session(Vec<Message>); |
488 | + |
489 | + impl SessionMessages for Session { |
490 | + type Error = std::convert::Infallible; |
491 | + fn drain_messages(&mut self) -> Vec<Message> { |
492 | + std::mem::take(&mut self.0) |
493 | + } |
494 | + |
495 | + fn add_message(&mut self, m: Message) -> Result<(), std::convert::Infallible> { |
496 | + self.0.push(m); |
497 | + Ok(()) |
498 | + } |
499 | + } |
500 | + let mut s = Session(vec![]); |
501 | + s.add_message(Message { |
502 | + message: "foo".into(), |
503 | + level: Level::default(), |
504 | + }) |
505 | + .unwrap(); |
506 | + s.add_message(Message { |
507 | + message: "bar".into(), |
508 | + level: Level::Error, |
509 | + }) |
510 | + .unwrap(); |
511 | + assert_eq!( |
512 | + s.drain_messages().as_slice(), |
513 | + [ |
514 | + Message { |
515 | + message: "foo".into(), |
516 | + level: Level::default(), |
517 | + }, |
518 | + Message { |
519 | + message: "bar".into(), |
520 | + level: Level::Error |
521 | + } |
522 | + ] |
523 | + .as_slice() |
524 | + ); |
525 | + assert!(s.0.is_empty()); |
526 | + } |
527 | + |
528 | + #[test] |
529 | + fn test_post_serde() { |
530 | + use mailpot::serde_json::{self, json}; |
531 | + assert_eq!( |
532 | + IntPOST(5), |
533 | + serde_json::from_str::<IntPOST>("\"5\"").unwrap() |
534 | + ); |
535 | + assert_eq!(IntPOST(5), serde_json::from_str::<IntPOST>("5").unwrap()); |
536 | + assert_eq!(&json! { IntPOST(5) }.to_string(), "5"); |
537 | + |
538 | + assert_eq!( |
539 | + BoolPOST(true), |
540 | + serde_json::from_str::<BoolPOST>("true").unwrap() |
541 | + ); |
542 | + assert_eq!( |
543 | + BoolPOST(true), |
544 | + serde_json::from_str::<BoolPOST>("\"true\"").unwrap() |
545 | + ); |
546 | + assert_eq!(&json! { BoolPOST(false) }.to_string(), "false"); |
547 | + } |
548 | + |
549 | + #[test] |
550 | + fn test_next() { |
551 | + let next = Next { |
552 | + next: Some("foo".to_string()), |
553 | + }; |
554 | + assert_eq!( |
555 | + format!("{:?}", Redirect::to("foo")), |
556 | + format!("{:?}", next.or_else(|| "bar".to_string())) |
557 | + ); |
558 | + let next = Next { next: None }; |
559 | + assert_eq!( |
560 | + format!("{:?}", Redirect::to("bar")), |
561 | + format!("{:?}", next.or_else(|| "bar".to_string())) |
562 | + ); |
563 | + } |
564 | + } |