cargo fmt

This commit is contained in:
Gonne 2024-11-25 20:40:40 +01:00
parent defa39de28
commit 2cc90cd920

View file

@ -1,11 +1,11 @@
use email_address_parser::EmailAddress;
use fqdn::FQDN;
use std::collections::HashMap;
use std::env;
use std::fs::File;
use std::io::{self, BufRead};
use std::path::Path;
use std::str::FromStr;
use fqdn::FQDN;
use email_address_parser::EmailAddress;
#[derive(Debug)]
struct AliasFile {
@ -17,39 +17,46 @@ type AliasMap = HashMap<EmailAddress, Vec<EmailAddress>>;
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 2 || args.len() % 2 == 0{
if args.len() < 2 || args.len() % 2 == 0 {
print_help();
return;
}
let mut alias_files : Vec<AliasFile> = Vec::new();
let mut alias_files: Vec<AliasFile> = Vec::new();
for i in (1..args.len()).step_by(2) {
if let Ok(lines) = read_lines(&args[i]) {
alias_files.push(AliasFile{content: lines, default_domain: FQDN::from_str(&args[i+1]).unwrap()});
alias_files.push(AliasFile {
content: lines,
default_domain: FQDN::from_str(&args[i + 1]).unwrap(),
});
}
}
println!("{}", generate_sieve_script(parse_alias_to_map(alias_files)));
}
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 {
script += format!("if envelope :is \"to\" \"{}\" {{\n{}}}\n", redirect,
{
let mut subscript : String = String::new();
for destination in destinations.iter().rev().skip(1).rev() {
subscript += format!(" redirect :copy \"{destination}\";\n").as_str();
}
subscript + format!(" redirect \"{}\";\n", destinations.iter().next_back().unwrap()).as_str()
script += format!("if envelope :is \"to\" \"{}\" {{\n{}}}\n", redirect, {
let mut subscript: String = String::new();
for destination in destinations.iter().rev().skip(1).rev() {
subscript += format!(" redirect :copy \"{destination}\";\n").as_str();
}
).as_str();
subscript
+ format!(
" redirect \"{}\";\n",
destinations.iter().next_back().unwrap()
)
.as_str()
})
.as_str();
}
script
}
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 redirect_map: AliasMap = AliasMap::new();
let mut destinations: Vec<EmailAddress> = Vec::new();
for alias_file in alias_files {
for line in alias_file.content {
let line = line.unwrap();
@ -58,26 +65,38 @@ fn parse_alias_to_map(alias_files: Vec<AliasFile>) -> AliasMap {
if destination.is_empty() {
continue;
}
let redirects: Vec<EmailAddress> = line.split_at(line.find(char::is_whitespace).unwrap_or(0)).1.split(' ')
.filter(|address| address.trim().to_string().replace(',',"") != "").map(|addr| to_mailaddress(addr, &alias_file.default_domain)).collect();
let redirects: Vec<EmailAddress> = line
.split_at(line.find(char::is_whitespace).unwrap_or(0))
.1
.split(' ')
.filter(|address| address.trim().to_string().replace(',', "") != "")
.map(|addr| to_mailaddress(addr, &alias_file.default_domain))
.collect();
if redirects.is_empty() {
continue;
}
destinations.push(to_mailaddress(destination, &alias_file.default_domain));
redirect_map.insert(to_mailaddress(destination, &alias_file.default_domain), redirects);
redirect_map.insert(
to_mailaddress(destination, &alias_file.default_domain),
redirects,
);
}
}
let mut changed = true;
while changed {
changed = false;
let mut all_new_redirects : AliasMap = AliasMap::new();
let mut all_new_redirects: AliasMap = AliasMap::new();
for destination in &destinations {
for forward_to in redirect_map.get(destination).unwrap() {
if let Some(new_redirects) = redirect_map.get(forward_to) {
changed = true;
all_new_redirects.entry(destination.clone()).or_insert(redirect_map.get(destination).unwrap().clone())
.retain(|dest| *dest != *forward_to);
all_new_redirects.entry(destination.clone()).and_modify(|d| d.extend(new_redirects.iter().cloned()));
all_new_redirects
.entry(destination.clone())
.or_insert(redirect_map.get(destination).unwrap().clone())
.retain(|dest| *dest != *forward_to);
all_new_redirects
.entry(destination.clone())
.and_modify(|d| d.extend(new_redirects.iter().cloned()));
}
}
}
@ -88,7 +107,7 @@ 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) -> EmailAddress {
let mut addr = local_part.trim().to_string();
addr = addr.replace(',', "");
if addr.contains('@') {
@ -100,12 +119,16 @@ fn to_mailaddress(local_part: &str, default_domain : &FQDN) -> EmailAddress {
// The output is wrapped in a Result to allow matching on errors.
// Returns an Iterator to the Reader of the lines of the file.
fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
where P: AsRef<Path>, {
where
P: AsRef<Path>,
{
let file = File::open(filename)?;
Ok(io::BufReader::new(file).lines())
}
fn print_help(){
print!("Reads a virtual alias file and needs a default domain to append to local paths, e.g.
./alias_to_sieve example.com.txt example.com");
fn print_help() {
print!(
"Reads a virtual alias file and needs a default domain to append to local paths, e.g.
./alias_to_sieve example.com.txt example.com"
);
}