From 071b3b5b762e5966c78c0c435f69a29113ccb564 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodolphe=20Br=C3=A9ard?= Date: Sun, 16 Jul 2023 12:28:48 +0200 Subject: [PATCH] Make use of the custom sub-address separator --- src/address.rs | 58 +++++++++++++++++++++++++++++--------------------- src/config.rs | 4 ++++ src/input.rs | 33 ++++++++++++++-------------- src/service.rs | 4 ++-- 4 files changed, 56 insertions(+), 43 deletions(-) diff --git a/src/address.rs b/src/address.rs index 7c1c973..d8b5d0b 100644 --- a/src/address.rs +++ b/src/address.rs @@ -11,16 +11,14 @@ pub struct CodedAddress { domain: Option, } -impl FromStr for CodedAddress { - type Err = Error; - - fn from_str(s: &str) -> Result { +impl CodedAddress { + pub fn parse(s: &str, separator: char) -> Result { let (local_part, domain) = split_local_part(s); ensure!(!local_part.is_empty(), "{s}: local part cannot be empty"); if let Some(dom) = &domain { ensure!(!dom.is_empty(), "{s}: domain cannot be empty"); } - let parts: Vec<&str> = local_part.split(crate::DEFAULT_SEPARATOR).collect(); + let parts: Vec<&str> = local_part.split(separator).collect(); let local_part = parts[0].to_string(); ensure!(!local_part.is_empty(), "{s}: local part cannot be empty"); let sub_addr = if parts.len() >= 2 { @@ -120,17 +118,17 @@ mod tests { #[test] fn parse_coded_addr_empty_email() { - assert!(CodedAddress::from_str("").is_err()); + assert!(CodedAddress::parse("", '+').is_err()); } #[test] fn parse_coded_addr_empty_local() { - assert!(CodedAddress::from_str("@example.com").is_err()); + assert!(CodedAddress::parse("@example.com", '+').is_err()); } #[test] fn parse_coded_addr_empty_domain() { - assert!(CodedAddress::from_str("derp@").is_err()); + assert!(CodedAddress::parse("derp@", '+').is_err()); } #[test] @@ -151,7 +149,7 @@ mod tests { #[test] fn parse_valid_coded_addr_with_domain() { let addr_str = "a+test+orsxg5a@example.org"; - let addr = CodedAddress::from_str(addr_str); + let addr = CodedAddress::parse(addr_str, '+'); assert!(addr.is_ok(), "unable to parse {addr_str}: {addr:?}"); let addr = addr.unwrap(); assert_eq!(addr.local_part, "a"); @@ -163,7 +161,7 @@ mod tests { #[test] fn parse_valid_coded_addr_without_domain() { let addr_str = "local.part+test+orsxg5a"; - let addr = CodedAddress::from_str(addr_str); + let addr = CodedAddress::parse(addr_str, '+'); assert!(addr.is_ok(), "unable to parse {addr_str}: {addr:?}"); let addr = addr.unwrap(); assert_eq!(addr.local_part, "local.part"); @@ -172,10 +170,22 @@ mod tests { assert!(addr.domain.is_none()); } + #[test] + fn parse_valid_coded_addr_alt_sep() { + let addr_str = "local-part.test.orsxg5a@example.org"; + let addr = CodedAddress::parse(addr_str, '.'); + assert!(addr.is_ok(), "unable to parse {addr_str}: {addr:?}"); + let addr = addr.unwrap(); + assert_eq!(addr.local_part, "local-part"); + assert_eq!(addr.sub_addr, Some("test".to_string())); + assert_eq!(addr.code, b"test"); + assert_eq!(addr.domain, Some("example.org".to_string())); + } + #[test] fn parse_valid_coded_addr_without_sub_addr() { let addr_str = "local.part@example.org"; - let addr = CodedAddress::from_str(addr_str); + let addr = CodedAddress::parse(addr_str, '+'); assert!(addr.is_ok(), "unable to parse {addr_str}: {addr:?}"); let addr = addr.unwrap(); assert_eq!(addr.local_part, "local.part"); @@ -232,29 +242,29 @@ mod tests { #[test] fn cmp_coded_addr_with_domain_eq() { - let addr_1 = CodedAddress::from_str("test+test+orsxg5a@example.org").unwrap(); - let addr_2 = CodedAddress::from_str("test+test+orsxg5a@example.org").unwrap(); + let addr_1 = CodedAddress::parse("test+test+orsxg5a@example.org", '+').unwrap(); + let addr_2 = CodedAddress::parse("test+test+orsxg5a@example.org", '+').unwrap(); assert_eq!(addr_1, addr_2); } #[test] fn cmp_coded_addr_without_domain_eq() { - let addr_1 = CodedAddress::from_str("test+test+orsxg5a").unwrap(); - let addr_2 = CodedAddress::from_str("test+test+orsxg5a").unwrap(); + let addr_1 = CodedAddress::parse("test+test+orsxg5a", '+').unwrap(); + let addr_2 = CodedAddress::parse("test+test+orsxg5a", '+').unwrap(); assert_eq!(addr_1, addr_2); } #[test] fn cmp_coded_addr_with_domain_ne() { - let addr_1 = CodedAddress::from_str("test+test+orsxg5a@example.org").unwrap(); - let addr_2 = CodedAddress::from_str("test2+test+orsxg5a@example.org").unwrap(); + let addr_1 = CodedAddress::parse("test+test+orsxg5a@example.org", '+').unwrap(); + let addr_2 = CodedAddress::parse("test2+test+orsxg5a@example.org", '+').unwrap(); assert_ne!(addr_1, addr_2); } #[test] fn cmp_coded_addr_without_domain_ne() { - let addr_1 = CodedAddress::from_str("test+test+orsxg5a").unwrap(); - let addr_2 = CodedAddress::from_str("test2+test+orsxg5a").unwrap(); + let addr_1 = CodedAddress::parse("test+test+orsxg5a", '+').unwrap(); + let addr_2 = CodedAddress::parse("test2+test+orsxg5a", '+').unwrap(); assert_ne!(addr_1, addr_2); } @@ -289,35 +299,35 @@ mod tests { #[test] fn cmp_addr_types_with_domain_eq() { let addr_1 = KeyedAddress::from_str("test@example.org:3d74YQqk").unwrap(); - let addr_2 = CodedAddress::from_str("test+test+orsxg5a@example.org").unwrap(); + let addr_2 = CodedAddress::parse("test+test+orsxg5a@example.org", '+').unwrap(); assert_eq!(addr_1, addr_2); } #[test] fn cmp_addr_types_without_domain_eq() { let addr_1 = KeyedAddress::from_str("test:3d74YQqk").unwrap(); - let addr_2 = CodedAddress::from_str("test+test+orsxg5a").unwrap(); + let addr_2 = CodedAddress::parse("test+test+orsxg5a", '+').unwrap(); assert_eq!(addr_1, addr_2); } #[test] fn cmp_addr_types_without_sub_addr() { let addr_1 = KeyedAddress::from_str("test@example.org:3d74YQqk").unwrap(); - let addr_2 = CodedAddress::from_str("test@example.org").unwrap(); + let addr_2 = CodedAddress::parse("test@example.org", '+').unwrap(); assert_eq!(addr_1, addr_2); } #[test] fn cmp_addr_types_with_domain_ne() { let addr_1 = KeyedAddress::from_str("test@example.org:3d74YQqk").unwrap(); - let addr_2 = CodedAddress::from_str("test+test+orsxg5a@example.com").unwrap(); + let addr_2 = CodedAddress::parse("test+test+orsxg5a@example.com", '+').unwrap(); assert_ne!(addr_1, addr_2); } #[test] fn cmp_addr_types_without_domain_ne() { let addr_1 = KeyedAddress::from_str("test:3d74YQqk").unwrap(); - let addr_2 = CodedAddress::from_str("test2+test+orsxg5a").unwrap(); + let addr_2 = CodedAddress::parse("test2+test+orsxg5a", '+').unwrap(); assert_ne!(addr_1, addr_2); } } diff --git a/src/config.rs b/src/config.rs index 7de7177..c10b832 100644 --- a/src/config.rs +++ b/src/config.rs @@ -37,4 +37,8 @@ impl Config { } Ok(addr_set) } + + pub fn get_separator(&self) -> char { + self.separator + } } diff --git a/src/input.rs b/src/input.rs index 34df005..859d14f 100644 --- a/src/input.rs +++ b/src/input.rs @@ -1,6 +1,5 @@ use crate::address::CodedAddress; use anyhow::{anyhow, ensure, Result}; -use std::str::FromStr; macro_rules! next_param { ($it: ident) => { @@ -25,7 +24,7 @@ impl Input { } } -pub fn parse_input(input: &str) -> Result { +pub fn parse_input(input: &str, separator: char) -> Result { let mut params_it = input.split(crate::PARAM_SEPARATOR); let stream = next_param!(params_it)?; ensure!(stream == "filter", "{stream}: invalid stream"); @@ -42,7 +41,7 @@ pub fn parse_input(input: &str) -> Result { let token = next_param!(params_it)?.to_string(); ensure!(!token.is_empty(), "empty token"); let address = next_param!(params_it)?.trim_end(); - let address = CodedAddress::from_str(address)?; + let address = CodedAddress::parse(address, separator)?; Ok(Input { session_id, token, @@ -57,7 +56,7 @@ mod tests { #[test] fn test_valid_input() { let input = "filter|0.5|1576146008.006099|smtp-in|rcpt-to|7641df9771b4ed00|1ef1c203cc576e5d|derp@example.com"; - let res = parse_input(input); + let res = parse_input(input, '+'); assert!(res.is_ok()); let inp = res.unwrap(); assert_eq!(inp.session_id, "7641df9771b4ed00"); @@ -66,83 +65,83 @@ mod tests { #[test] fn test_empty_input() { - assert!(parse_input("").is_err()); + assert!(parse_input("", '+').is_err()); } #[test] fn test_empty_stream() { let input = "|0.5|1576146008.006099|smtp-in|rcpt-to|7641df9771b4ed00|1ef1c203cc576e5d|derp@example.com"; - assert!(parse_input(input).is_err()); + assert!(parse_input(input, '+').is_err()); } #[test] fn test_invalid_stream() { let input = "invalid|0.5|1576146008.006099|smtp-in|rcpt-to|7641df9771b4ed00|1ef1c203cc576e5d|derp@example.com"; - assert!(parse_input(input).is_err()); + assert!(parse_input(input, '+').is_err()); } #[test] fn test_empty_version() { let input = "filter||1576146008.006099|smtp-in|rcpt-to|7641df9771b4ed00|1ef1c203cc576e5d|derp@example.com"; - assert!(parse_input(input).is_err()); + assert!(parse_input(input, '+').is_err()); } #[test] fn test_empty_timestamp() { let input = "filter|0.5||smtp-in|rcpt-to|7641df9771b4ed00|1ef1c203cc576e5d|derp@example.com"; - assert!(parse_input(input).is_err()); + assert!(parse_input(input, '+').is_err()); } #[test] fn test_empty_subsystem() { let input = "filter|0.5|1576146008.006099||rcpt-to|7641df9771b4ed00|1ef1c203cc576e5d|derp@example.com"; - assert!(parse_input(input).is_err()); + assert!(parse_input(input, '+').is_err()); } #[test] fn test_invalid_subsystem() { let input = "filter|0.5|1576146008.006099|invalid|rcpt-to|7641df9771b4ed00|1ef1c203cc576e5d|derp@example.com"; - assert!(parse_input(input).is_err()); + assert!(parse_input(input, '+').is_err()); } #[test] fn test_empty_phase() { let input = "filter|0.5|1576146008.006099|smtp-in||7641df9771b4ed00|1ef1c203cc576e5d|derp@example.com"; - assert!(parse_input(input).is_err()); + assert!(parse_input(input, '+').is_err()); } #[test] fn test_invalid_phase() { let input = "filter|0.5|1576146008.006099|smtp-in|invalid|7641df9771b4ed00|1ef1c203cc576e5d|derp@example.com"; - assert!(parse_input(input).is_err()); + assert!(parse_input(input, '+').is_err()); } #[test] fn test_empty_id() { let input = "filter|0.5|1576146008.006099|smtp-in|rcpt-to||1ef1c203cc576e5d|derp@example.com"; - assert!(parse_input(input).is_err()); + assert!(parse_input(input, '+').is_err()); } #[test] fn test_empty_token() { let input = "filter|0.5|1576146008.006099|smtp-in|rcpt-to|7641df9771b4ed00||derp@example.com"; - assert!(parse_input(input).is_err()); + assert!(parse_input(input, '+').is_err()); } #[test] fn test_empty_data() { let input = "filter|0.5|1576146008.006099|smtp-in|rcpt-to|7641df9771b4ed00|1ef1c203cc576e5d|"; - assert!(parse_input(input).is_err()); + assert!(parse_input(input, '+').is_err()); } #[test] fn test_missing_data() { let input = "filter|0.5|1576146008.006099|smtp-in|rcpt-to|7641df9771b4ed00|1ef1c203cc576e5d"; - assert!(parse_input(input).is_err()); + assert!(parse_input(input, '+').is_err()); } } diff --git a/src/service.rs b/src/service.rs index fd45829..1087b00 100644 --- a/src/service.rs +++ b/src/service.rs @@ -35,7 +35,7 @@ pub fn start_service() -> Result<()> { if buffer.is_empty() { continue; } - match parse_input(&buffer) { + match parse_input(&buffer, cfg.get_separator()) { Ok(input) => { if allow_email(&input, &addresses) { input.answer(ANSWER_OK); @@ -71,7 +71,7 @@ mod tests { fn run_test_with_addr(address: &str) -> bool { // Preparing the input let input_str = format!("filter|0.5|1576146008.006099|smtp-in|rcpt-to|7641df9771b4ed00|1ef1c203cc576e5d|{address}"); - let input = parse_input(&input_str); + let input = parse_input(&input_str, '+'); assert!(input.is_ok()); let input = input.unwrap();