diff options
| author | Teddy Wing | 2016-04-24 05:46:32 -0400 |
|---|---|---|
| committer | Teddy Wing | 2016-04-24 05:46:32 -0400 |
| commit | 99a0daa14750b901b6d8974bc3fdada33d538acb (patch) | |
| tree | 262d27a7ec2c80ab257f53008df57ac5fd16c20f | |
| parent | 5059d4028035bc54849bf99dc3546363f5f28476 (diff) | |
| parent | afc01ecb67dd978879d423866d33ac0c526151f4 (diff) | |
| download | mutt-alias-auto-add-99a0daa14750b901b6d8974bc3fdada33d538acb.tar.bz2 | |
Merge branch 'extract-alias-module'
| -rw-r--r-- | src/alias.rs | 152 | ||||
| -rw-r--r-- | src/main.rs | 161 | ||||
| -rw-r--r-- | src/tests.rs | 81 |
3 files changed, 203 insertions, 191 deletions
diff --git a/src/alias.rs b/src/alias.rs new file mode 100644 index 0000000..8a66db8 --- /dev/null +++ b/src/alias.rs @@ -0,0 +1,152 @@ +use std::error::{self, Error}; +use std::fs::{File, OpenOptions}; +use std::fmt; +use std::io::{self, BufRead, BufReader, Write}; +use std::path::Path; + +#[derive(Clone)] +pub struct Alias { + pub alias: String, + pub name: String, + pub email: String, +} + +impl Alias { + pub fn new(email: &str) -> Alias { + let mut split: Vec<&str> = email.split_whitespace().collect(); + + // Remove "From: " + split.remove(0); + + let mut alias = String::new(); + let mut name = String::new(); + let mut email = String::new(); + + if split.len() == 1 { + alias = split[0].to_lowercase().to_string(); + email = split[0].to_string(); + } else if split.len() == 2 { + alias = split[0].to_lowercase().to_string(); + name = split[0].to_string(); + email = split[1].to_string(); + } else if split.len() > 2 { + alias = format!("{}-{}", split[split.len() - 2], split[0]).to_lowercase().to_string(); + name = split[0..(split.len() - 1)].join(" "); + email = split[split.len() - 1].to_string(); + } + + alias = alias.replace(',', ""); + alias = alias.replace('\'', ""); + alias = alias.replace('"', ""); + + Alias { alias: alias, name: name, email: email } + } + + pub fn to_string(&self) -> String { + if self.name.is_empty() { + format!("alias {} {}", self.alias, self.email) + } else { + format!("alias {} {} {}", self.alias, self.name, self.email) + } + } + + pub fn find_in_file<P: AsRef<Path>>(&self, file: P) -> Result<Vec<String>, AliasSearchError> { + let mut matches = Vec::new(); + let f = try!(File::open(file)); + let file = BufReader::new(&f); + for line in file.lines() { + let line = try!(line); + let split: Vec<&str> = line.split_whitespace().collect(); + + if line.contains(&self.email) { + return Err(AliasSearchError::EmailExists) + } + + if split[1].starts_with(&self.alias) { + matches.push(split[1].to_owned()); + } + } + + if matches.is_empty() { + Err(AliasSearchError::NotFound) + } else { + Ok(matches) + } + } + + pub fn write_to_file<P: AsRef<Path>>(&mut self, file: P) -> Result<(), AliasSearchError> { + let similar_aliases = try!(self.find_in_file(&file)); + self.update_alias_id(similar_aliases); + + let mut f = try!(OpenOptions::new().append(true).open(file)); + try!(f.write_all(format!("{}\n", self.to_string()).as_bytes())); + + Ok(()) + } + + pub fn update_alias_id(&mut self, similar_aliases: Vec<String>) { + if !similar_aliases.is_empty() { + self.alias = format!("{}-{}", self.alias, similar_aliases.len() + 1); + } + } +} + +#[derive(Debug)] +pub enum AliasSearchError { + NotFound, + EmailExists, + Io(io::Error), +} + +impl fmt::Display for AliasSearchError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + AliasSearchError::NotFound => writeln!(f, "{}", self.description()), + AliasSearchError::EmailExists => writeln!(f, "{}", self.description()), + AliasSearchError::Io(ref err) => writeln!(f, "IO error: {}", err), + } + } +} + +impl error::Error for AliasSearchError { + fn description(&self) -> &str { + match *self { + AliasSearchError::NotFound => "Alias could not be found in aliases file", + AliasSearchError::EmailExists => "Email already exists in aliases file", + AliasSearchError::Io(ref err) => err.description(), + } + } + + fn cause(&self) -> Option<&error::Error> { + match *self { + AliasSearchError::Io(ref err) => Some(err), + _ => None, + } + } +} + +impl From<io::Error> for AliasSearchError { + fn from(err: io::Error) -> AliasSearchError { + AliasSearchError::Io(err) + } +} + +#[cfg(test)] +impl PartialEq<AliasSearchError> for AliasSearchError { + fn eq(&self, other: &AliasSearchError) -> bool { + match *self { + AliasSearchError::NotFound => match *other { + AliasSearchError::NotFound => true, + _ => false, + }, + AliasSearchError::EmailExists => match *other { + AliasSearchError::EmailExists => true, + _ => false, + }, + AliasSearchError::Io(_) => match *other { + AliasSearchError::Io(_) => true, + _ => false, + }, + } + } +} diff --git a/src/main.rs b/src/main.rs index 75e74ac..0732f6d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,162 +1,12 @@ use std::env; -use std::error::{self, Error}; -use std::io::{self, BufRead, BufReader, Write}; -use std::fmt; -use std::fs::{File, OpenOptions}; -use std::path::Path; +use std::io::{self, BufRead, Write}; -#[cfg(test)] -mod tests; - -struct Alias { - alias: String, - name: String, - email: String, -} - -impl Alias { - fn new(email: &str) -> Alias { - let mut split: Vec<&str> = email.split_whitespace().collect(); - - // Remove "From: " - split.remove(0); - - let mut alias = String::new(); - let mut name = String::new(); - let mut email = String::new(); - - if split.len() == 1 { - alias = split[0].to_lowercase().to_string(); - email = split[0].to_string(); - } else if split.len() == 2 { - alias = split[0].to_lowercase().to_string(); - name = split[0].to_string(); - email = split[1].to_string(); - } else if split.len() > 2 { - alias = format!("{}-{}", split[split.len() - 2], split[0]).to_lowercase().to_string(); - name = split[0..(split.len() - 1)].join(" "); - email = split[split.len() - 1].to_string(); - } - - alias = alias.replace(',', ""); - alias = alias.replace('\'', ""); - alias = alias.replace('"', ""); - - Alias { alias: alias, name: name, email: email } - } - - fn to_string(&self) -> String { - if self.name.is_empty() { - format!("alias {} {}", self.alias, self.email) - } else { - format!("alias {} {} {}", self.alias, self.name, self.email) - } - } - - fn write_to_file<P: AsRef<Path>>(&self, file: P) -> Result<(), io::Error> { - let mut f = try!(OpenOptions::new().append(true).open(file)); - try!(f.write_all(format!("{}\n", self.to_string()).as_bytes())); - Ok(()) - } - - fn update_alias_id(&mut self, similar_aliases: Vec<String>) { - if !similar_aliases.is_empty() { - self.alias = format!("{}-{}", self.alias, similar_aliases.len() + 1); - } - } -} - -fn write_alias<P: AsRef<Path>>(from: String, file: P) -> Result<(), AliasSearchError> { - let mut alias = Alias::new(&from); - let similar_aliases = try!(find_alias_in_file(&alias, &file)); - alias.update_alias_id(similar_aliases); - try!(alias.write_to_file(&file)); - Ok(()) -} - -#[derive(Debug)] -enum AliasSearchError { - NotFound, - EmailExists, - Io(io::Error), -} - -impl fmt::Display for AliasSearchError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - AliasSearchError::NotFound => writeln!(f, "{}", self.description()), - AliasSearchError::EmailExists => writeln!(f, "{}", self.description()), - AliasSearchError::Io(ref err) => writeln!(f, "IO error: {}", err), - } - } -} - -impl error::Error for AliasSearchError { - fn description(&self) -> &str { - match *self { - AliasSearchError::NotFound => "Alias could not be found in aliases file", - AliasSearchError::EmailExists => "Email already exists in aliases file", - AliasSearchError::Io(ref err) => err.description(), - } - } - - fn cause(&self) -> Option<&error::Error> { - match *self { - AliasSearchError::Io(ref err) => Some(err), - _ => None, - } - } -} - -impl From<io::Error> for AliasSearchError { - fn from(err: io::Error) -> AliasSearchError { - AliasSearchError::Io(err) - } -} +mod alias; #[cfg(test)] -impl PartialEq<AliasSearchError> for AliasSearchError { - fn eq(&self, other: &AliasSearchError) -> bool { - match *self { - AliasSearchError::NotFound => match *other { - AliasSearchError::NotFound => true, - _ => false, - }, - AliasSearchError::EmailExists => match *other { - AliasSearchError::EmailExists => true, - _ => false, - }, - AliasSearchError::Io(_) => match *other { - AliasSearchError::Io(_) => true, - _ => false, - }, - } - } -} - -fn find_alias_in_file<P: AsRef<Path>>(alias: &Alias, file: P) -> Result<Vec<String>, AliasSearchError> { - let mut matches = Vec::new(); - let f = try!(File::open(file)); - let file = BufReader::new(&f); - for line in file.lines() { - let line = try!(line); - let split: Vec<&str> = line.split_whitespace().collect(); - - if line.contains(&alias.email) { - return Err(AliasSearchError::EmailExists) - } - - if split[1].starts_with(&alias.alias) { - matches.push(split[1].to_owned()); - } - } +mod tests; - if matches.is_empty() { - Err(AliasSearchError::NotFound) - } else { - Ok(matches) - } -} +use alias::*; fn print_usage(program: &str) { println!("Usage: {} FILE", program); @@ -182,7 +32,8 @@ fn main() { println!("{}", line); if line.starts_with("From: ") { - match write_alias(line, &file) { + let mut alias = Alias::new(&line); + match alias.write_to_file(&file) { Ok(_) => continue, Err(e @ AliasSearchError::NotFound) | Err(e @ AliasSearchError::EmailExists) => io::stderr().write(e.to_string().as_bytes()).ok(), diff --git a/src/tests.rs b/src/tests.rs index 223c6ae..708121e 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -1,7 +1,7 @@ -use std::fs::{self, File}; -use std::io::Read; +use std::fs::{self, File, OpenOptions}; +use std::io::{Read, Write}; -use super::{Alias, AliasSearchError, find_alias_in_file}; +use alias::{Alias, AliasSearchError}; #[test] fn new_alias_with_only_email() { @@ -45,50 +45,47 @@ fn new_alias_with_special_characters() { #[test] -fn find_alias_in_file_email_already_exists() { +fn alias_find_in_file_email_already_exists() { + let alias = Alias { + alias: "farnsworth-hubert".to_owned(), + name: "Hubert Farnsworth".to_owned(), + email: "<professor@planetexpress.com>".to_owned() + }; + assert_eq!( Err(AliasSearchError::EmailExists), - find_alias_in_file( - &Alias { - alias: "farnsworth-hubert".to_owned(), - name: "Hubert Farnsworth".to_owned(), - email: "<professor@planetexpress.com>".to_owned() - }, - "./testdata/aliases" - ) + alias.find_in_file("./testdata/aliases") ); } #[test] -fn find_alias_in_file_alias_is_new() { +fn alias_find_in_file_alias_is_new() { + let alias = Alias { + alias: "fry-philip".to_owned(), + name: "Philip Fry".to_owned(), + email: "<fry@planetexpress.com>".to_owned() + }; + assert_eq!( Err(AliasSearchError::NotFound), - find_alias_in_file( - &Alias { - alias: "fry-philip".to_owned(), - name: "Philip Fry".to_owned(), - email: "<fry@planetexpress.com>".to_owned() - }, - "./testdata/aliases" - ) + alias.find_in_file("./testdata/aliases") ); } #[test] -fn find_alias_in_file_finds_a_match() { +fn alias_find_in_file_finds_a_match() { + let alias = Alias { + alias: "farnsworth-hubert".to_owned(), + name: "Hubert Farnsworth".to_owned(), + email: "<goodnewseveryone@planetexpress.com>".to_owned() + }; + assert_eq!( Ok(vec![ "farnsworth-hubert".to_owned(), "farnsworth-hubert-2".to_owned() ]), - find_alias_in_file( - &Alias { - alias: "farnsworth-hubert".to_owned(), - name: "Hubert Farnsworth".to_owned(), - email: "<goodnewseveryone@planetexpress.com>".to_owned() - }, - "./testdata/aliases" - ) + alias.find_in_file("./testdata/aliases") ); } @@ -127,17 +124,29 @@ fn update_alias_id_increments_alias() { #[test] fn alias_write_to_file_must_write_given_alias_to_file() { - let alias = update_alias_id_sample_alias(); + let mut alias = update_alias_id_sample_alias(); + // Create a new test file let test_file = "./testdata/write_to_file"; - fs::copy("./testdata/aliases", test_file).unwrap(); - alias.write_to_file(test_file).unwrap(); + fs::copy("./testdata/aliases", test_file).expect("Alias file copy failed"); + + // Write a duplicate alias so that `write_to_file` is able to append a + // new one + let mut f = OpenOptions::new().append(true).open(test_file) + .expect("Failed to open test file for appending"); + writeln!(f, "{}", Alias { email: "derpy@home.pv".to_owned(), .. alias.clone() } + .to_string()) + .expect("Failed to append matching alias"); + + // Write our new alias to the file + alias.write_to_file(test_file).expect("`write_to_file` failed"); - let mut f = File::open(test_file).unwrap(); + // Get the file's contents for testing + let mut f = File::open(test_file).expect("Failed to open test file"); let mut file_contents = String::new(); - f.read_to_string(&mut file_contents).unwrap(); + f.read_to_string(&mut file_contents).expect("Failed to read test file contents"); let file_contents: Vec<&str> = file_contents.split('\n').collect(); - fs::remove_file(test_file).unwrap(); + fs::remove_file(test_file).expect("Failed to delete test file"); assert_eq!(alias.to_string(), file_contents[file_contents.len() - 2]); } |
