| Age | Commit message (Collapse) | Author | 
|---|
|  | Well, after all that error handling I'm going to have to remove error
output. The problem was that this STDERR output interfered with the
output of the email in Mutt. When viewing a message, the screen would
become mangled (for certain messages), and I'd have to refresh (Ctrl-L)
the view.
After initially thinking this was because I was using `println!` instead
of flushing a full buffer to STDOUT, I just now realised that my output
to STDERR could be causing the problem. Sure enough, removing the STDERR
output fixed the issue.
This is certainly not ideal, so in the long term we'll probably want to
add some kind of logging mechanism so that users actually have
visibility into errors. But for now and for my own personal purposes, I
think this works. And at least it gets me around the immediate annoying
screen mangling issue. | 
|  |  | 
|  | Give us a better error message if a failure ever happens here. Was
inspired by my experience in 2d1f7031f03194fbceffc15b1d6376abea243e22,
where the `unwrap` calls gave no useful information. | 
|  | Take all of our Alias code, functions, errors, etc. and move them into
their own module. This removes some clutter from our `main.rs` file and
makes things better organised. Now all the alias code lives in its own
dedicated place.
Update our test file imports to match this change.
Updates to alias code:
* Reordered imports alphabetically
* Made `Alias` public
* Made `AliasSearchError` public
* Made all methods on `Alias` public | 
|  | Expand the responsibility of `Alias#write_to_file` so that it performs
what it used to do plus all of what `write_alias` did.
This allows us to consolidate the functionality into a single method,
and move it into the `Alias` implementation.
The change required some modification to our `write_to_file` test:
* Create our test alias as mutable
* Write a new alias to the test file based on our test alias but with a
  different email. This allows the `write_to_file` method to work
  without erroring with an `AliasSearchError::NotFound`.
* Needed to `derive(Clone)` on `Alias` in order to be able to easily
  clone it into the new near-duplicate alias.
* Change our `unwrap()` calls to `expect()` to make it easier to see
  where exactly we panicked. Otherwise I didn't really have any way of
  knowing.
* Add some comments for clarity | 
|  | Makes more sense for this function to live in a method on `Alias`
because it operates directly on an alias.
Refactor our tests and code to support this new organisation. | 
|  | As described in 0b12b2bae1130746ed49cc3c7a2daa819ede1b58, we don't need
to depend on 'getopts' because we don't have any command line options,
just a single required argument.
Rewrite our code to factor out getopts and assume that the first
argument to our program is an alias file path. | 
|  | * Add a dependency on 'getopts'
* Remove the hard-coded "testaliases" file used previously.
* write_alias: Update to include a `file` attribute that can reference
  an arbitrary file
* find_alias_in_file: Change the `file` parameter to be a Path reference
  instead of a string so that it can be called correctly from
  `write_alias`. Also because it matches the File module's signature.
Originally I planned to make the file argument available under a `-f`
command-line option. Later I decided instead to make it a required
argument, so it made more sense not to prefix it with an option flag.
Since I no longer need command line options—just the first argument—I
realised that I could get rid of the `getopts` dependency and use
`std::env::args`. Will do this in a later commit. | 
|  | Use the descriptions from our `error::Error` implementation. To do so
needed to `use std::error::Error`.
Change those `write!` calls to `writeln!` also so that we get decent
output on the command line.
We now output errors from `AliasSearchError::{NotFound, EmailExists}` to
STDERR for better error reporting. | 
|  | We use an `.ok()` call on the result of the write so that we can ignore
these errors. I think we shouldn't really worry too much about not being
to write to STDERR, and instead try to print out the full email message
as best we can.
In order to write the error (`e.to_string()`), we needed to implement
`fmt::Display` on `AliasSearchError`. While I was at it also implemented
the `error::Error` trait. | 
|  | I wrote it the way I originally did because I wanted to be more
explicit, but I don't think that was right. Doing it this way because it
makes the code much simpler. Also found out there's a `writeln!` macro
which I probably should have used instead of manually adding a newline
before. | 
|  | We need to write the full messgae to STDOUT so that Mutt can read it
back in. Remove our two return statements as this will interrupt the
message. | 
|  | Rename `handle_alias` to `write_alias` and implement the function. This
function integrates all our steps together and writes our new alias if
we have one.
Alter our `main` function to remove some of the old code. Don't convert
STDIN to a vector. That was naïve. Instead iterate over its lines
directly. Handle all our errors with either a "no news is good news" or
a panic. | 
|  | The program is nearly implemented and this information is no longer
needed. | 
|  | Changed the test to use a file with multiple alias lines for a more
real-world scenario. Achieving this by copying the `testdata/aliases`
file into a new temporary test file instead of creating a new blank
file.
Then needed to change our assertion so that we get the correct line from
the file to compare on. This is always the last line in the file. Note
that `.len() - 2` is used because `.len() - 1` is the final newline.
The test change revealed two errors in my code:
1. I needed to open the file for appending, not just for writing.
2. A newline needs to be appended to the end of the file (otherwise all
   our aliases get written to the same line).
Fix the errors. | 
|  | Writes the alias to a given file.
Thinking I should modify the test so that we can know it works with
multiple alias lines. | 
|  | This function takes a list of aliases and updates the current `Alias`'s
alias using an auto-incremented numeric id.
Not happy with the repetition in the tests. Need to figure out if
there's a way to abstract that. | 
|  | My next step is to get the list produced by this function and use it to
build a new alias of `#{alias}-#{id + 1}`.
In trying to figure out how best to do that, I realised that it would be
easier to do if I had actual aliases to work with instead of full Mutt
alias lines that I'd have to parse (again).
Update our function to give us a list of aliases instead of full alias
lines. | 
|  | This has been superseded by the `Alias::new` method (introduced in
b182ea18dd664bc36e56601635ceb5ffdd67dc69). We can now safely remove this
function. | 
|  | When the email of the alias we're looking is already in the file, expect
an `AliasSearchError::EmailExists` error.
Oh man, this was a tough one. After much searching, finally figured out
how to implement the `PartialEq` trait for my error type so that we
could actually test it.
Many thanks to @peterbudai for an example in the 'redux' project of how
to do this
(https://github.com/peterbudai/redux/blob/ef5d47a0a64cef9fa9e1e9c6f21badc46fa283fc/src/lib.rs):
    #[cfg(test)]
    impl PartialEq<Error> for Error {
        fn eq(&self, other: &Error) -> bool {
           match *self {
               Error::Eof => match *other { Error::Eof => true, _ => false },
               Error::InvalidInput => match *other { Error::InvalidInput => true, _ => false },
               Error::IoError(_)  => match *other { Error::IoError(_) => true, _ => false },
           }
        }
    }
With that example, I was able to correctly build an equality function to
get past my compiler errors which complained that
    an implementation of `std::cmp::PartialEq` might be missing for `std::io::error::Error`
when I tried to `#[derive(PartialEq)]` on my `AliasSearchError` type. | 
|  | Instead of returning a boolean value, get all lines that include the
alias being searched for. This will give us a list of values like:
    Ok([
        "alias farnsworth-hubert Hubert Farnsworth <professor@planetexpress.com>",
        "alias farnsworth-hubert-2 Hubert Farnsworth <other@planetexpress.com>",
        "alias farnsworth-hubert-3 Hubert Farnsworth <other2@planetexpress.com>"
    ])
Our list will contain all Mutt alias calls that start with the given
alias string.
Use my new learnings from the Error Handling section of the Rust book
(http://doc.rust-lang.org/book/error-handling.html). We return a Result
type so that we can communicate both File IO errors and our own custom
matching errors. We define a new error type that allows us to provide
information about the reason for our own errors.
Note that I haven't yet implemented the required `fmt::Display` and
`error::Error` methods on my custom error type yet as those didn't
appear to be crucial to just getting this working.
Change the `line.unwrap()` line to `try!(line)` so that we don't panic
in the event of an error, but instead bubble the error upstream.
If we come across a Mutt alias that has the same email as the inputted
search alias, we want to abort because we shouldn't add a new Mutt alias
line for an existing email address that's already been aliased. | 
|  | I think we should return a list of matched aliases from this function
instead of a simple boolean value. As such, we'll start by renaming the
function to something that makes sense in that context.
We now need to modify the function and return type in order to provide
that information. | 
|  | Got a compiler warning saying that this doesn't need to be mutable.
Sounds good to me, so let's do what it recommends. | 
|  |  | 
|  | This allows us to more easily compare parts of the alias line.
Update our test to pass an `Alias` also. | 
|  | Conflicts:
	src/tests.rs | 
|  | When we get a "From: " address that doesn't contain a name, the
resulting `Alias` object created by `Alias::new` will have an empty
name.
When `to_string` is called on these kinds of objects, an extra space
appears between the alias and the email address because of how the
format string is constructed.
This caused a test failure for the `new_alias_with_only_email` test
method in fc05eb9e2f111642dbec093218b7184f25740b90. Our change here
fixes and passes this test. | 
|  | This allows us to represent an alias not just as a string, but as an
entity where we can ask for and isolate different parts of the alias.
This makes it more convenient to get just the "alias" part, or just the
"email" part for example. Doing so is necessary for making accurate
comparisons/searches to find out whether this email or alias already
exists in our alias file.
Two functions are implemented on this type. One, `to_string`, creates a
Mutt alias line as a string. The other builds a new `Alias` object from
a "From: " string, taking nearly the same steps as `build_alias` does.
We'll want to transition `build_alias` to this `Alias::new` function,
using the new one in the future. | 
|  | This will allow us to pass an alias and grep for it in a given file.
Just a rough outline to start. Added a super basic test and a file in
"testdata" to operate on for testing. | 
|  | This functionality has been handled in
de3a9b7f68e1f1b368630f498dd338d8b50444c8. The comment is no longer
necessary. | 
|  | Make the function a bit more DRY by taking the `push_str` calls out of
the `if` block.
Add a new test and some code to remove commas and quotes from aliases. | 
|  | Copy of the functionality in W. Caleb McDaniel's Bash script implemented
in Rust:
    echo "${MESSAGE}" | grep ^"From: " | sed s/[\,\"\']//g | awk '{$1=""; if (NF == 3) {print "alias" $0;} else if (NF == 2) {print "alias" $0 $0;} else if (NF > 3) {print "alias", tolower($(NF-1))"-"tolower($2) $0;}}'
Doesn't currently include the [,"'] filtering.
Added a couple of new test cases to cover other address formats.
Not sure if I should use `if..else if` here or a `match`. I feel like I
should return an empty string at the end if we can't build an alias
line. | 
|  | Create a new `tests` module. This lives in its own file. Not sure if
this is idiomatic Rust, but I prefer the idea of my tests living in a
different file from my code. This module gets included in `main.rs`.
We `use` `build_alias` directly because it's currently a private
function, so doing `use super::*` didn't import it. Not sure if I should
be making it a public function in order to mitigate this.
Add a couple of variant test cases to check the string transformation
that this function should be doing. | 
|  | Stub out two new functions. The first, `handle_alias`, will manage the
alias appending process. The second, `build_alias`, will transform a
`From: ` string into a Mutt alias. | 
|  | Look for the line that begins "From: " in the email message given to us.
When we find it, we'll call a function that we're going to define soon
that handles adding an alias to our Mutt aliases file. | 
|  |  | 
|  | Tried a few different times, copying code from different places. Didn't
quite get it figured out at first.
Wanted to add STDIN lines to a string. Got that working with option 3,
but that `push_str` didn't appear to preserve newlines.
Ended up using this solution from StackOverflow:
http://stackoverflow.com/questions/13579266/how-to-read-user-input-in-rust/36374135#36374135
It adds the lines to a vector instead of a single string. Printing each
line individually then gives us out newlines. This way is probably nicer
also for getting the first line that starts with "From: ". | 
|  | Clarify what we want to do. | 
|  | Generated with
    $ cargo new mutt-alias-auto-add --bin |