From e4e118b3b8afe583d50119378a9b90affdc3548e Mon Sep 17 00:00:00 2001 From: Franz Dietrich Date: Sat, 9 Dec 2023 14:32:36 +0100 Subject: [PATCH 1/6] additional metadata --- Cargo.toml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index b1f4858..a680147 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,12 @@ authors = ["Andy Grover "] edition = "2021" license = "MIT OR Apache-2.0" description = "Support for parsing ManageSieve protocol (RFC 5804)" +documentation = "https://docs.rs/managesieve/0.1.1/managesieve/" +readme = "README.md" +homepage = "https://github.com/agrover/managesieve" +repository = "https://github.com/agrover/managesieve" +keywords = ["email", "sieve", "managesieve", "filter", "parser"] +categories = ["parser-implementations", "parsing", "email", "api-bindings"] [dependencies] nom = "6.2.0" From 541fcd5e72de46e0160570c3c5701f5c52654c59 Mon Sep 17 00:00:00 2001 From: Franz Dietrich Date: Sat, 9 Dec 2023 14:33:17 +0100 Subject: [PATCH 2/6] update dependencies --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a680147..d793a08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,5 +13,5 @@ keywords = ["email", "sieve", "managesieve", "filter", "parser"] categories = ["parser-implementations", "parsing", "email", "api-bindings"] [dependencies] -nom = "6.2.0" -either = "1.6.1" +nom = "7.1" +either = "1.6" From 6ab446d28d5ec8ab8cb0c9327d363a217fd708a0 Mon Sep 17 00:00:00 2001 From: Franz Dietrich Date: Sat, 9 Dec 2023 14:42:41 +0100 Subject: [PATCH 3/6] removing redundant code and adding type aliases --- src/parser.rs | 34 +++++++++++++++++++--------------- src/types.rs | 15 ++++++++++----- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index 7fe8947..4123ca7 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -12,7 +12,10 @@ use nom::{ IResult, }; -use crate::types::{OkNoBye, QuotaVariant, Response, ResponseCode, SieveUrl}; +use crate::{ + types::{OkNoBye, QuotaVariant, Response, ResponseCode, SieveUrl}, + MSResult, MSResultList, +}; pub(crate) fn ok(input: &str) -> IResult<&str, OkNoBye> { value(OkNoBye::Ok, tag_no_case("OK"))(input) @@ -71,7 +74,7 @@ fn atom(input: &str) -> IResult<&str, ResponseCode> { #[test] fn test_atom() { assert!(matches!(atom("SASL"), Ok(("", ResponseCode::Sasl)))); - assert!(matches!(atom("ABCDE"), Err(_))); + assert!(atom("ABCDE").is_err()); } fn literal_s2c_len(input: &str) -> IResult<&str, usize> { @@ -89,7 +92,7 @@ fn literal_s2c_len(input: &str) -> IResult<&str, usize> { fn test_literal_s2c_len() { assert!(matches!(literal_s2c_len("{3}\r\n"), Ok(("", 3)))); assert!(matches!(literal_s2c_len("{0}\r\n"), Ok(("", 0)))); - assert!(matches!(literal_s2c_len("{3}"), Err(_))); + assert!(literal_s2c_len("{3}").is_err()); assert!(matches!(literal_s2c_len("{3}\r\nab"), Ok(("ab", 3)))); } @@ -295,11 +298,11 @@ fn test_response() { response("BYE\r\n").unwrap(); response("ok (QUOTA)\r\n").unwrap(); response("ok (QUOTA) \"hello\"\r\n").unwrap(); - assert!(matches!(response("ok"), Err(_))); - assert!(matches!(response(" ok\r\n"), Err(_))); - assert!(matches!(response("ok (\r\n"), Err(_))); - assert!(matches!(response("ok (QUOTA\r\n"), Err(_))); - assert!(matches!(response("ok (QUOTA/)\r\n"), Err(_))); + assert!(response("ok").is_err()); + assert!(response(" ok\r\n").is_err()); + assert!(response("ok (\r\n").is_err()); + assert!(response("ok (QUOTA\r\n").is_err()); + assert!(response("ok (QUOTA/)\r\n").is_err()); } pub fn response_getscript(input: &str) -> IResult<&str, (Option, Response)> { @@ -316,10 +319,10 @@ pub fn response_getscript(input: &str) -> IResult<&str, (Option, Respons fn test_response_getscript() { response_getscript("\"hello\"\r\nOK\r\n").unwrap(); response_getscript("NO\r\n").unwrap(); - assert!(matches!(response_getscript("\"hello\"\r\nBYE\r\n"), Err(_))); + assert!(response_getscript("\"hello\"\r\nBYE\r\n").is_err()); } -pub fn response_listscripts(input: &str) -> IResult<&str, (Vec<(String, bool)>, Response)> { +pub fn response_listscripts(input: &str) -> MSResultList<(String, bool)> { pair( many0(terminated( pair( @@ -357,7 +360,8 @@ fn test_single_capability() { pub fn response_capability( input: &str, -) -> IResult<&str, (Vec<(String, Option)>, Response)> { + //) -> IResult<&str, (Vec<(String, Option)>, Response)> { +) -> MSResultList<(String, Option)> { pair(many0(single_capability), response)(input) } @@ -372,7 +376,7 @@ fn test_response_capability_2() { response_capability(inc1).unwrap(); } -pub fn response_starttls(input: &str) -> IResult<&str, (Vec<(String, Option)>, Response)> { +pub fn response_starttls(input: &str) -> MSResultList<(String, Option)> { alt(( preceded(response_ok, response_capability), map(response_nobye, |r| (Vec::new(), r)), @@ -389,8 +393,8 @@ fn test_response_starttls() { /// response. pub fn response_authenticate_initial(input: &str) -> IResult<&str, Either> { alt(( - map(terminated(sievestring_s2c, crlf), |s| Either::Left(s)), - map(response_nobye, |r| Either::Right(r)), + map(terminated(sievestring_s2c, crlf), Either::Left), + map(response_nobye, Either::Right), ))(input) } @@ -404,7 +408,7 @@ fn test_response_authenticate_initial() { /// capabilities if OK. pub fn response_authenticate_complete( input: &str, -) -> IResult<&str, (Option)>>, Response)> { +) -> MSResult)>>> { alt(( map( pair(response_ok, opt(response_capability)), diff --git a/src/types.rs b/src/types.rs index f9fb35e..2f47f55 100644 --- a/src/types.rs +++ b/src/types.rs @@ -2,7 +2,7 @@ use std::convert::TryFrom; use std::io::{self, ErrorKind}; use std::string::ToString; -use nom; +use nom::IResult; use crate::parser as p; @@ -34,10 +34,10 @@ impl TryFrom<(&str, Option<&str>)> for Capability { let (cap, rest) = s; let err = || io::Error::new(ErrorKind::InvalidInput, "Invalid Capability"); - let unwrap_rest = || rest.map(|o| o.to_owned()).ok_or_else(|| err()); + let unwrap_rest = || rest.map(|o| o.to_owned()).ok_or_else(err); let unwrap_rest_vec = || { rest.map(|r| r.split(' ').map(|x| x.to_string()).collect()) - .ok_or_else(|| err()) + .ok_or_else(err) }; Ok(match cap { @@ -129,7 +129,7 @@ impl Command { } fn to_sieve_name(s: &str) -> Result { - if s.chars().find(|c| p::is_bad_sieve_name_char(*c)).is_some() { + if s.chars().any(p::is_bad_sieve_name_char) { return Err(Error::InvalidInput); } @@ -258,10 +258,12 @@ pub fn response_setactive(input: &str) -> Result<(&str, Response), Error> { response_oknobye(input) } +pub type ScriptList = Vec<(String, bool)>; + /// Parses text returned from the server in response to the LISTSCRIPTS command. /// Returns list of scripts and a bool indicating if that script is the active /// script. -pub fn response_listscripts(input: &str) -> Result<(&str, Vec<(String, bool)>, Response), Error> { +pub fn response_listscripts(input: &str) -> Result<(&str, ScriptList, Response), Error> { match p::response_listscripts(input) { Ok((left, (s, resp))) => { if s.iter().filter(|(_, is_active)| *is_active).count() > 1 { @@ -350,3 +352,6 @@ pub fn response_noop(input: &str) -> Result<(&str, Response), Error> { pub fn response_unauthenticate(input: &str) -> Result<(&str, Response), Error> { response_oknobye(input) } + +pub type MSResult<'a, T> = IResult<&'a str, (T, Response)>; +pub type MSResultList<'a, T> = IResult<&'a str, (Vec, Response)>; From ef2a1d554680e6c3d17bb1345380396260e9e862 Mon Sep 17 00:00:00 2001 From: Franz Dietrich Date: Sat, 9 Dec 2023 14:50:30 +0100 Subject: [PATCH 4/6] more consistent import --- src/parser.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index 4123ca7..cf5e83f 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -12,9 +12,8 @@ use nom::{ IResult, }; -use crate::{ - types::{OkNoBye, QuotaVariant, Response, ResponseCode, SieveUrl}, - MSResult, MSResultList, +use crate::types::{ + MSResult, MSResultList, OkNoBye, QuotaVariant, Response, ResponseCode, SieveUrl, }; pub(crate) fn ok(input: &str) -> IResult<&str, OkNoBye> { From 9673ad1290c90d66192ff87882e23b87a0ee844b Mon Sep 17 00:00:00 2001 From: Franz Dietrich Date: Tue, 12 Dec 2023 21:51:31 +0100 Subject: [PATCH 5/6] use thiserror --- Cargo.toml | 1 + src/types.rs | 27 +++++++++++++++++---------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d793a08..36d80b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,3 +15,4 @@ categories = ["parser-implementations", "parsing", "email", "api-bindings"] [dependencies] nom = "7.1" either = "1.6" +thiserror = "1.0" diff --git a/src/types.rs b/src/types.rs index 2f47f55..95edff0 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,4 +1,5 @@ use std::convert::TryFrom; +use std::fmt::Display; use std::io::{self, ErrorKind}; use std::string::ToString; @@ -6,14 +7,27 @@ use nom::IResult; use crate::parser as p; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, thiserror::Error)] pub enum Error { + #[error("incomplete response")] IncompleteResponse, + #[error("invalid response")] InvalidResponse, + #[error("invalid input")] InvalidInput, } -#[derive(Debug, PartialEq)] +impl From> for Error { + fn from(value: nom::Err) -> Self { + match value { + nom::Err::Incomplete(_) => Self::IncompleteResponse, + nom::Err::Error(_) => Self::InvalidInput, + nom::Err::Failure(_) => Self::InvalidInput, + } + } +} + +#[derive(Debug, PartialEq, Clone)] pub enum Capability { Implementation(String), Sasl(Vec), @@ -225,14 +239,7 @@ pub enum ResponseCode { } fn response_oknobye(input: &str) -> Result<(&str, Response), Error> { - match p::response(input) { - Ok((left, response)) => Ok((left, response)), - Err(e) => match e { - nom::Err::Incomplete(_) => Err(Error::IncompleteResponse), - nom::Err::Error(_) => Err(Error::InvalidResponse), - nom::Err::Failure(_) => Err(Error::InvalidResponse), - }, - } + p::response(input).map_err(Error::from) } pub fn response_authenticate(_input: &str) -> Result { From 80e9115be2a15208aa6b5d455b2183b89ef402e1 Mon Sep 17 00:00:00 2001 From: Franz Dietrich Date: Tue, 12 Dec 2023 21:52:27 +0100 Subject: [PATCH 6/6] add Cargo.lock to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index ea8c4bf..869df07 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +Cargo.lock \ No newline at end of file