aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTeddy Wing2016-04-24 05:46:32 -0400
committerTeddy Wing2016-04-24 05:46:32 -0400
commit99a0daa14750b901b6d8974bc3fdada33d538acb (patch)
tree262d27a7ec2c80ab257f53008df57ac5fd16c20f
parent5059d4028035bc54849bf99dc3546363f5f28476 (diff)
parentafc01ecb67dd978879d423866d33ac0c526151f4 (diff)
downloadmutt-alias-auto-add-99a0daa14750b901b6d8974bc3fdada33d538acb.tar.bz2
Merge branch 'extract-alias-module'
-rw-r--r--src/alias.rs152
-rw-r--r--src/main.rs161
-rw-r--r--src/tests.rs81
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]);
}