Reintroduce ordering

This commit is contained in:
Gonne 2024-11-25 21:26:39 +01:00
parent 2cc90cd920
commit 390e714233
2 changed files with 29 additions and 13 deletions

View file

@ -1,19 +1,34 @@
use email_address_parser::EmailAddress;
use fqdn::FQDN;
use std::collections::HashMap;
use std::cmp::Ordering;
use std::collections::BTreeMap;
use std::env;
use std::fs::File;
use std::io::{self, BufRead};
use std::path::Path;
use std::str::FromStr;
#[derive(Debug)]
struct AliasFile {
content: io::Lines<io::BufReader<File>>,
default_domain: FQDN,
}
type AliasMap = HashMap<EmailAddress, Vec<EmailAddress>>;
#[derive(PartialEq, Eq, Clone)]
struct OrdEmailAddress(EmailAddress);
impl PartialOrd for OrdEmailAddress {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.0.to_string().cmp(&other.0.to_string()))
}
}
impl Ord for OrdEmailAddress {
fn cmp(&self, other: &Self) -> Ordering {
self.0.to_string().cmp(&other.0.to_string())
}
}
type AliasMap = BTreeMap<OrdEmailAddress, Vec<OrdEmailAddress>>;
fn main() {
let args: Vec<String> = env::args().collect();
@ -35,16 +50,17 @@ fn main() {
fn generate_sieve_script(redirects: AliasMap) -> String {
let mut script: String = "require [\"envelope\", \"copy\"];\n\n".to_string();
for (redirect, destinations) in redirects {
script += format!("if envelope :is \"to\" \"{}\" {{\n{}}}\n", redirect, {
for (redirect, mut destinations) in redirects {
script += format!("if envelope :is \"to\" \"{}\" {{\n{}}}\n", redirect.0, {
let mut subscript: String = String::new();
destinations.sort();
for destination in destinations.iter().rev().skip(1).rev() {
subscript += format!(" redirect :copy \"{destination}\";\n").as_str();
subscript += format!(" redirect :copy \"{}\";\n", destination.0).as_str();
}
subscript
+ format!(
" redirect \"{}\";\n",
destinations.iter().next_back().unwrap()
destinations.iter().next_back().unwrap().0
)
.as_str()
})
@ -56,7 +72,7 @@ fn generate_sieve_script(redirects: AliasMap) -> String {
fn parse_alias_to_map(alias_files: Vec<AliasFile>) -> AliasMap {
// File must exist in the current path
let mut redirect_map: AliasMap = AliasMap::new();
let mut destinations: Vec<EmailAddress> = Vec::new();
let mut destinations: Vec<OrdEmailAddress> = Vec::new();
for alias_file in alias_files {
for line in alias_file.content {
let line = line.unwrap();
@ -65,7 +81,7 @@ fn parse_alias_to_map(alias_files: Vec<AliasFile>) -> AliasMap {
if destination.is_empty() {
continue;
}
let redirects: Vec<EmailAddress> = line
let redirects: Vec<OrdEmailAddress> = line
.split_at(line.find(char::is_whitespace).unwrap_or(0))
.1
.split(' ')
@ -107,13 +123,13 @@ fn parse_alias_to_map(alias_files: Vec<AliasFile>) -> AliasMap {
redirect_map
}
fn to_mailaddress(local_part: &str, default_domain: &FQDN) -> EmailAddress {
fn to_mailaddress(local_part: &str, default_domain: &FQDN) -> OrdEmailAddress {
let mut addr = local_part.trim().to_string();
addr = addr.replace(',', "");
if addr.contains('@') {
return EmailAddress::parse(&addr, None).unwrap();
return OrdEmailAddress(EmailAddress::parse(&addr, None).unwrap());
}
EmailAddress::new(&addr, &default_domain.to_string(), None).unwrap()
OrdEmailAddress(EmailAddress::new(&addr, &default_domain.to_string(), None).unwrap())
}
// The output is wrapped in a Result to allow matching on errors.