Skip to main content

io_smtp/rfc5321/types/
text.rs

1//! SMTP text string (RFC 5321 ยง4.2).
2//!
3//! The human-readable text following the reply code on a response
4//! line.
5
6use core::fmt;
7
8use alloc::{borrow::Cow, vec::Vec};
9
10use bounded_static_derive::ToStatic;
11use chumsky::prelude::*;
12
13/// A human-readable text string used in SMTP responses.
14#[derive(Clone, Debug, PartialEq, Eq, Hash, ToStatic)]
15pub struct SmtpText<'a>(pub(crate) Cow<'a, str>);
16
17impl SmtpText<'_> {
18    /// Parses a text string from raw bytes, consuming the whole
19    /// input.
20    pub fn parse<'a>(bytes: &'a [u8]) -> Result<SmtpText<'a>, Vec<Rich<'a, u8>>> {
21        parsers::text()
22            .then_ignore(end())
23            .parse(bytes)
24            .into_result()
25    }
26}
27
28impl fmt::Display for SmtpText<'_> {
29    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
30        write!(f, "{}", self.0.as_ref())
31    }
32}
33
34impl<'a> From<SmtpText<'a>> for Cow<'a, str> {
35    fn from(text: SmtpText<'a>) -> Self {
36        text.0
37    }
38}
39
40impl AsRef<str> for SmtpText<'_> {
41    fn as_ref(&self) -> &str {
42        self.0.as_ref()
43    }
44}
45
46pub(crate) mod parsers {
47    //! Chumsky parser for the SMTP text string.
48
49    use core::str::from_utf8;
50
51    use alloc::borrow::Cow;
52
53    use chumsky::prelude::*;
54
55    use crate::{rfc5321::SmtpText, utils::parsers::Extra};
56
57    /// SMTP text string parser.
58    ///
59    /// ```abnf
60    /// textstring     = 1*(%d09 / %d32-126)
61    ///                ; HT, SP, Printable US-ASCII
62    /// ```
63    pub(crate) fn text<'a>() -> impl Parser<'a, &'a [u8], SmtpText<'a>, Extra<'a>> + Clone {
64        any()
65            .filter(|b| matches!(*b, 0x09 | 0x20..=0x7e))
66            .repeated()
67            .at_least(1)
68            .to_slice()
69            .map(from_utf8)
70            .map(Result::unwrap)
71            .map(Cow::from)
72            .map(SmtpText)
73    }
74}