From 6c6fa0f2ff92e13c506f636ff5cf9afb66e2c370 Mon Sep 17 00:00:00 2001 From: Zeeshan Ali Date: Thu, 6 Oct 2022 21:37:59 +0200 Subject: [PATCH] Add typed support for `SIZE` parameter (RFC 1870) This also makes `Parameter` type an enum so future type support for other parameters can be more easily added. Note that the enum is non-exhaustive so that future variant addtions would not cause API breaks. --- src/parse/command.rs | 22 +++++++++++++++------- src/types.rs | 35 ++++++++++++++++++++--------------- 2 files changed, 35 insertions(+), 22 deletions(-) diff --git a/src/parse/command.rs b/src/parse/command.rs index f38ad8f..a66ea92 100644 --- a/src/parse/command.rs +++ b/src/parse/command.rs @@ -88,10 +88,18 @@ pub fn Mail_parameters(input: &[u8]) -> IResult<&[u8], Vec> { /// esmtp-param = esmtp-keyword ["=" esmtp-value] pub fn esmtp_param(input: &[u8]) -> IResult<&[u8], Parameter> { - map( - tuple((esmtp_keyword, opt(preceded(tag(b"="), esmtp_value)))), - |(keyword, value)| Parameter::new(keyword, value), - )(input) + alt(( + map_res(tuple((tag_no_case(b"SIZE="), esmtp_value)), |(_, value)| { + value.parse().map(Parameter::Size) + }), + map( + tuple((esmtp_keyword, opt(preceded(tag(b"="), esmtp_value)))), + |(keyword, value)| Parameter::Other { + keyword: keyword.to_owned(), + value: value.map(String::from), + }, + ), + ))(input) } /// esmtp-keyword = (ALPHA / DIGIT) *(ALPHA / DIGIT / "-") @@ -353,7 +361,7 @@ pub fn Dot_string(input: &[u8]) -> IResult<&[u8], &str> { #[cfg(test)] mod test { - use super::{ehlo, helo, mail}; + use super::{ehlo, helo, mail, Parameter}; use crate::types::{Command, DomainOrAddress}; #[test] @@ -382,12 +390,12 @@ mod test { #[test] fn test_mail() { - let (rem, parsed) = mail(b"MAIL FROM:\r\n???").unwrap(); + let (rem, parsed) = mail(b"MAIL FROM: size=12345\r\n???").unwrap(); assert_eq!( parsed, Command::Mail { reverse_path: "userx@y.foo.org".into(), - parameters: Vec::default(), + parameters: vec![Parameter::Size(12345)], } ); assert_eq!(rem, b"???"); diff --git a/src/types.rs b/src/types.rs index c7739bb..a9c897b 100644 --- a/src/types.rs +++ b/src/types.rs @@ -110,9 +110,14 @@ impl DomainOrAddress { } #[derive(Clone, Debug, PartialEq, Eq)] -pub struct Parameter { - keyword: String, - value: Option, +#[non_exhaustive] +pub enum Parameter { + /// Message size declaration [RFC1870] + Size(u32), + Other { + keyword: String, + value: Option, + }, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -244,20 +249,20 @@ impl Command { } impl Parameter { - pub fn new, V: Into>(keyword: K, value: Option) -> Parameter { - Parameter { - keyword: keyword.into(), - value: value.map(Into::into), - } - } - pub fn serialize(&self, writer: &mut impl Write) -> std::io::Result<()> { - writer.write_all(self.keyword.as_bytes())?; + match self { + Parameter::Size(size) => { + write!(writer, "SIZE={}", size)?; + } + Parameter::Other { keyword, value } => { + writer.write_all(keyword.as_bytes())?; - if let Some(ref value) = self.value { - writer.write_all(b"=")?; - writer.write_all(value.as_bytes())?; - } + if let Some(ref value) = value { + writer.write_all(b"=")?; + writer.write_all(value.as_bytes())?; + } + } + }; Ok(()) }