lich 0.2.0

Minimal password management.
Documentation
use clap::{self, ErrorKind};
use entry;
use lich::{Data, OpenData};
use serde_json;
use std::error::Error;
use std::fs::File;
use std::io::{Read, Seek, SeekFrom};
use std::path::Path;

#[macro_export]
macro_rules! wlnerr {
    ($($args:tt)*) => {{
        use ::std::io::Write;
        let _ = writeln!(&mut ::std::io::stderr(), $($args)*);
    }}
}

pub fn decode<R: Read>(file: R, path: &Path) -> Result<Data, clap::Error> {
    serde_json::from_reader(file)
        .map_err(|e| read_error(path, e))
}

//TODO: Avoid universal lifetime quantification.
pub fn unlock<'a, F, T>(data: &'a mut Data, cb: F) -> T
    where F: for<'x> FnOnce(OpenData<'x>) -> T {
    loop {
        let password = entry::master_password();
        if let Ok(unlocked) = data.unlock(&password) {
            return cb(unlocked);
        }
        wlnerr!("Incorrect password; try again.");
    }
}

pub fn create_error<E: Error>(path: &Path, e: E) -> clap::Error {
    error(&format!(
        "could not create {} - {}",
        path.display(),
        e.description()
    ))
}

pub fn read_error<E: Error>(path: &Path, e: E) -> clap::Error {
    error(&format!(
        "could not read {} - {}",
        path.display(),
        e.description()
    ))
}

pub fn write_error<E: Error>(path: &Path, e: E) -> clap::Error {
    error(&format!(
        "could not update {} - {}",
        path.display(),
        e.description()
    ))
}

pub fn error(msg: &str) -> clap::Error {
    clap::Error::with_description(msg, ErrorKind::Io)
}

pub fn save(
    file: &mut File,
    path: &Path,
    data: &Data
) -> Result<(), clap::Error> {
    file.seek(SeekFrom::Start(0))
        .and_then(|_| file.set_len(0))
        .map_err(|e| write_error(path, e))?;

    serde_json::to_writer(file, data)
        .map_err(|e| write_error(path, e))?;

    Ok(())
}