cargo fmt
This commit is contained in:
parent
defa39de28
commit
2cc90cd920
1 changed files with 52 additions and 29 deletions
73
src/main.rs
73
src/main.rs
|
@ -1,11 +1,11 @@
|
||||||
|
use email_address_parser::EmailAddress;
|
||||||
|
use fqdn::FQDN;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
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;
|
||||||
use fqdn::FQDN;
|
|
||||||
use email_address_parser::EmailAddress;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct AliasFile {
|
struct AliasFile {
|
||||||
|
@ -17,39 +17,46 @@ type AliasMap = HashMap<EmailAddress, Vec<EmailAddress>>;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let args: Vec<String> = env::args().collect();
|
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();
|
print_help();
|
||||||
return;
|
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) {
|
for i in (1..args.len()).step_by(2) {
|
||||||
if let Ok(lines) = read_lines(&args[i]) {
|
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)));
|
println!("{}", generate_sieve_script(parse_alias_to_map(alias_files)));
|
||||||
}
|
}
|
||||||
|
|
||||||
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, destinations) in redirects {
|
||||||
script += format!("if envelope :is \"to\" \"{}\" {{\n{}}}\n", redirect,
|
script += format!("if envelope :is \"to\" \"{}\" {{\n{}}}\n", redirect, {
|
||||||
{
|
let mut subscript: String = String::new();
|
||||||
let mut subscript : String = String::new();
|
|
||||||
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 \"{destination}\";\n").as_str();
|
||||||
}
|
}
|
||||||
subscript + format!(" redirect \"{}\";\n", destinations.iter().next_back().unwrap()).as_str()
|
subscript
|
||||||
}
|
+ format!(
|
||||||
).as_str();
|
" redirect \"{}\";\n",
|
||||||
|
destinations.iter().next_back().unwrap()
|
||||||
|
)
|
||||||
|
.as_str()
|
||||||
|
})
|
||||||
|
.as_str();
|
||||||
}
|
}
|
||||||
script
|
script
|
||||||
}
|
}
|
||||||
|
|
||||||
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<EmailAddress> = 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();
|
||||||
|
@ -58,26 +65,38 @@ 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.split_at(line.find(char::is_whitespace).unwrap_or(0)).1.split(' ')
|
let redirects: Vec<EmailAddress> = line
|
||||||
.filter(|address| address.trim().to_string().replace(',',"") != "").map(|addr| to_mailaddress(addr, &alias_file.default_domain)).collect();
|
.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() {
|
if redirects.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
destinations.push(to_mailaddress(destination, &alias_file.default_domain));
|
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;
|
let mut changed = true;
|
||||||
while changed {
|
while changed {
|
||||||
changed = false;
|
changed = false;
|
||||||
let mut all_new_redirects : AliasMap = AliasMap::new();
|
let mut all_new_redirects: AliasMap = AliasMap::new();
|
||||||
for destination in &destinations {
|
for destination in &destinations {
|
||||||
for forward_to in redirect_map.get(destination).unwrap() {
|
for forward_to in redirect_map.get(destination).unwrap() {
|
||||||
if let Some(new_redirects) = redirect_map.get(forward_to) {
|
if let Some(new_redirects) = redirect_map.get(forward_to) {
|
||||||
changed = true;
|
changed = true;
|
||||||
all_new_redirects.entry(destination.clone()).or_insert(redirect_map.get(destination).unwrap().clone())
|
all_new_redirects
|
||||||
|
.entry(destination.clone())
|
||||||
|
.or_insert(redirect_map.get(destination).unwrap().clone())
|
||||||
.retain(|dest| *dest != *forward_to);
|
.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())
|
||||||
|
.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
|
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();
|
let mut addr = local_part.trim().to_string();
|
||||||
addr = addr.replace(',', "");
|
addr = addr.replace(',', "");
|
||||||
if addr.contains('@') {
|
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.
|
// The output is wrapped in a Result to allow matching on errors.
|
||||||
// Returns an Iterator to the Reader of the lines of the file.
|
// 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>>>
|
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)?;
|
let file = File::open(filename)?;
|
||||||
Ok(io::BufReader::new(file).lines())
|
Ok(io::BufReader::new(file).lines())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_help(){
|
fn print_help() {
|
||||||
print!("Reads a virtual alias file and needs a default domain to append to local paths, e.g.
|
print!(
|
||||||
./alias_to_sieve example.com.txt example.com");
|
"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"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue