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

@ -7,4 +7,4 @@ rust-version = "1.68.2"
[dependencies] [dependencies]
fqdn = {version = "0.4.2", features = ["domain-label-length-limited-to-63", "domain-name-without-special-chars"]} fqdn = {version = "0.4.2", features = ["domain-label-length-limited-to-63", "domain-name-without-special-chars"]}
email-address-parser = "2.0.0" email-address-parser = "2.0.0"

View file

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