Add some type safety

This commit is contained in:
Gonne 2024-11-25 20:19:09 +01:00 committed by Dennis Frieberg
parent e0eded2aba
commit 828fade1e6
Signed by: nerf
SSH key fingerprint: SHA256:zvrU0EwwaNK65M+AqL9IOTRawFq0JZ8QXBASxxGpxmg
4 changed files with 333 additions and 18 deletions

View file

@ -1,15 +1,19 @@
use std::collections::BTreeMap;
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::*;
use email_address_parser::EmailAddress;
#[derive(Debug)]
struct AliasFile {
content: io::Lines<io::BufReader<File>>,
default_domain: String,
default_domain: FQDN,
}
type AliasMap = HashMap<EmailAddress, Vec<EmailAddress>>;
fn main() {
let args: Vec<String> = env::args().collect();
@ -20,7 +24,7 @@ fn main() {
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: args[i+1].to_string()})
alias_files.push(AliasFile{content: lines, default_domain: FQDN::from_str(&args[i+1]).unwrap()})
}
}
@ -29,13 +33,12 @@ fn main() {
println!("{}", sieve_script);
}
fn generate_sieve_script(redirects: BTreeMap<String, Vec<String>>) -> String {
fn generate_sieve_script(redirects: AliasMap) -> String {
let mut script : String = "require [\"envelope\", \"copy\"];\n\n".to_string();
for (redirect, mut destinations) in redirects {
for (redirect, destinations) in redirects {
script += format!("if envelope :is \"to\" \"{}\" {{\n{}}}\n", redirect,
{
let mut subscript : String = "".to_string();
destinations.sort();
for destination in destinations.iter().rev().skip(1).rev() {
subscript += format!(" redirect :copy \"{}\";\n", destination).as_str();
}
@ -46,10 +49,10 @@ fn generate_sieve_script(redirects: BTreeMap<String, Vec<String>>) -> String {
script
}
fn parse_alias_to_map(alias_files: Vec<AliasFile>) -> BTreeMap<String, Vec<String>> {
fn parse_alias_to_map(alias_files: Vec<AliasFile>) -> AliasMap {
// File must exist in the current path
let mut redirect_map : BTreeMap<String, Vec<String>> = BTreeMap::new();
let mut destinations : Vec<String> = Vec::new();
let mut redirect_map : AliasMap = AliasMap::new();
let mut destinations : Vec<EmailAddress> = Vec::new();
for alias_file in alias_files.into_iter() {
for line in alias_file.content {
let line = line.unwrap();
@ -58,7 +61,7 @@ fn parse_alias_to_map(alias_files: Vec<AliasFile>) -> BTreeMap<String, Vec<Strin
if destination.is_empty() {
continue;
}
let redirects: Vec<String> = line.split_at(line.find(char::is_whitespace).unwrap_or(0)).1.split(" ")
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;
@ -70,7 +73,7 @@ fn parse_alias_to_map(alias_files: Vec<AliasFile>) -> BTreeMap<String, Vec<Strin
let mut changed = true;
while changed {
changed = false;
let mut all_new_redirects : BTreeMap<String, Vec<String>> = BTreeMap::new();
let mut all_new_redirects : AliasMap = AliasMap::new();
for destination in destinations.iter() {
for forward_to in redirect_map.get(destination).unwrap().iter() {
if let Some(new_redirects) = redirect_map.get(forward_to) {
@ -88,16 +91,15 @@ fn parse_alias_to_map(alias_files: Vec<AliasFile>) -> BTreeMap<String, Vec<Strin
redirect_map
}
fn to_mailaddress(local_part: &str, default_domain : &String) -> String {
fn to_mailaddress(local_part: &str, default_domain : &FQDN) -> EmailAddress {
let mut addr = local_part.trim().to_string();
addr = addr.replace(",", "");
if addr.contains("@") {
return addr;
return EmailAddress::parse(&addr, None).unwrap();
}
addr + "@" + default_domain
EmailAddress::new(&addr, &default_domain.to_string(), None).unwrap()
}
// 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>>>
@ -106,8 +108,7 @@ where P: AsRef<Path>, {
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 ");
./alias_to_sieve example.com.txt example.com");
}