Conflate multiple alias input files

This commit is contained in:
Gonne 2024-11-14 09:39:20 +01:00
parent c28ab701f5
commit b3f09cd22f

View file

@ -1,24 +1,36 @@
use std::io::{self};
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::env; use std::env;
use std::fs::File;
use std::io::{self, BufRead};
use std::path::Path;
#[derive(Debug)]
struct AliasFile {
content: io::Lines<io::BufReader<File>>,
default_domain: String,
}
fn main() { fn main() {
let args: Vec<String> = env::args().collect(); let args: Vec<String> = env::args().collect();
if args.len() < 2 { if args.len() < 2 || args.len() % 2 == 0{
print_help(); print_help();
return; return;
} }
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()})
}
}
let default_domain = &args[1]; let redirects = parse_alias_to_map(alias_files);
let redirects = parse_alias_to_map(default_domain);
let sieve_script = generate_sieve_script(redirects); let sieve_script = generate_sieve_script(redirects);
println!("{}", sieve_script); println!("{}", sieve_script);
} }
fn generate_sieve_script(redirects: BTreeMap<String, Vec<String>>) -> String { fn generate_sieve_script(redirects: BTreeMap<String, Vec<String>>) -> String {
// let mut script : String = "require [\"envelope\", \"copy\"];\n\n".to_string(); let mut script : String = "require [\"envelope\", \"copy\"];\n\n".to_string();
let mut script : String = "".to_string();
for (redirect, mut 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,
{ {
@ -34,24 +46,26 @@ fn generate_sieve_script(redirects: BTreeMap<String, Vec<String>>) -> String {
script script
} }
fn parse_alias_to_map(default_domain : &str) -> BTreeMap<String, Vec<String>> { fn parse_alias_to_map(alias_files: Vec<AliasFile>) -> BTreeMap<String, Vec<String>> {
// File must exist in the current path // File must exist in the current path
let mut redirect_map : BTreeMap<String, Vec<String>> = BTreeMap::new(); let mut redirect_map : BTreeMap<String, Vec<String>> = BTreeMap::new();
let mut destinations : Vec<String> = Vec::new(); let mut destinations : Vec<String> = Vec::new();
for line in io::stdin().lines() { for alias_file in alias_files.into_iter() {
let line = line.unwrap(); for line in alias_file.content {
let line = String::from(line.split_at(line.find("#").unwrap_or(line.len())).0); let line = line.unwrap();
let destination = line.split_at(line.find(char::is_whitespace).unwrap_or(0)).0; let line = String::from(line.split_at(line.find("#").unwrap_or(line.len())).0);
if destination.is_empty() { let destination = line.split_at(line.find(char::is_whitespace).unwrap_or(0)).0;
continue; if destination.is_empty() {
continue;
}
let redirects: Vec<String> = 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);
} }
let redirects: Vec<String> = 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, default_domain)).collect();
if redirects.is_empty() {
continue;
}
destinations.push(to_mailaddress(destination, default_domain));
redirect_map.insert(to_mailaddress(destination, default_domain), redirects);
} }
let mut changed = true; let mut changed = true;
while changed { while changed {
@ -74,7 +88,7 @@ fn parse_alias_to_map(default_domain : &str) -> BTreeMap<String, Vec<String>> {
redirect_map redirect_map
} }
fn to_mailaddress(local_part: &str, default_domain : &str) -> String { fn to_mailaddress(local_part: &str, default_domain : &String) -> String {
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("@") {
@ -83,7 +97,17 @@ fn to_mailaddress(local_part: &str, default_domain : &str) -> String {
addr + "@" + default_domain addr + "@" + default_domain
} }
fn print_help(){
print!("Reads a virtual alias file from STDIN and needs a default domain to append to local paths, e.g. // The output is wrapped in a Result to allow matching on errors.
cat virt_aliases | ./alias_to_sieve example.com"); // 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>, {
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 ");
} }