bearer 0.2.3

Command line utility to generate HTTP Authorization header with bearer tokens. This is achieved with OAuth2 authorization code and refresh token workflow.
use std::fs;
use std::path::Path;
use std::vec::Vec;
use tilde_expand::tilde_expand;

use super::super::results::{BearerResult, BearerError};


fn expand_path(config_dir: &str) -> BearerResult<String> {
    let config_dir_expanded = tilde_expand(config_dir.as_bytes());
    let config_dir_expanded = String::from_utf8(config_dir_expanded);
    if let Err(err) = config_dir_expanded {
        return Err(BearerError::UTF8EncodingError(format!(r#"Cannot build path from config dir \
{}: {:?}"#,
                                                          config_dir,
                                                          err)));
    }
    Ok(config_dir_expanded.unwrap())
}


pub fn build_path(config_dir: &str, client_name: &str) -> BearerResult<(String, bool)> {

    let config_dir_expanded = expand_path(config_dir)?;
    let path = config_dir_expanded.clone();
    let path = Path::new(path.as_str());
    if !path.exists() {
        debug!("Creating the config dir {}, path not exists", config_dir);
        let res = fs::create_dir_all(&config_dir_expanded);
        if let Err(err) = res {
            return Err(BearerError::IOError(format!("Could not create directory {}: {}",
                                                    config_dir_expanded,
                                                    err)));
        }
    } else if !path.is_dir() {
        return Err(BearerError::ValueError(format!("Path {} is not a directory",
                                                   config_dir_expanded)));
    }


    let path = path.join(format!("{}.toml", client_name));

    match path.to_str() {
        Some(string) => Ok((string.to_string(), path.is_file())),
        None => {
            Err(BearerError::UTF8EncodingError(format!(r#"Could not build path with config dir {} \
and client {}"#,
                                                       config_dir_expanded,
                                                       client_name)))
        }
    }
}


pub fn list_clients(config_dir: &str) -> BearerResult<Vec<String>> {
    let config_dir_expanded = expand_path(config_dir)?;
    let path = config_dir_expanded.clone();
    let path = Path::new(path.as_str());
    if !path.is_dir() {
        return Err(BearerError::ValueError(format!("Path {} is not a directory",
                                                   config_dir_expanded)));
    }
    let paths = fs::read_dir(path).unwrap();
    let mut ret =
        paths.map(|pth| pth.unwrap().path().file_name().unwrap().to_str().unwrap().to_string())
            .filter(|pth| pth.ends_with(".toml"))
            .map(|pth| pth.as_str()[..pth.len() - 5].to_string())
            .collect::<Vec<String>>();
    ret.sort();
    Ok(ret)
}


#[cfg(test)]
mod tests {
    use super::*;

    use std::fs;
    use std::path::Path;
    use rand::{thread_rng, Rng};

    #[test]
    fn test_build_path_exists() {
        let (path, exists) = build_path("src/tests/conf", "dummy_with_tokens").unwrap();
        assert_eq!(exists, true);
        assert!(path.ends_with("src/tests/conf/dummy_with_tokens.toml"));
    }

    #[test]
    fn test_build_path_not_exists() {
        let (path, exists) = build_path("src/tests/conf", "not_exists").unwrap();
        assert_eq!(exists, false);
        assert!(path.ends_with("src/tests/conf/not_exists.toml"));
    }

    #[test]
    fn test_build_path_create_dir() {
        let rnd: String = thread_rng().gen_ascii_chars().take(10).collect();

        let tmpdir = format!("/tmp/test-bearer-{}", rnd);

        let dirpath = Path::new(tmpdir.as_str());
        assert_eq!(dirpath.exists(), false);

        let (path, exists) = build_path(tmpdir.as_str(), "not_exists").unwrap();
        assert_eq!(exists, false);

        let tmpdir = format!("/tmp/test-bearer-{}", rnd);
        let filepath = format!("{}/not_exists.toml", tmpdir);

        assert_eq!(path, filepath);

        let filepath = Path::new(filepath.as_str());

        assert_eq!(dirpath.exists(), true);
        assert_eq!(filepath.exists(), false);

        fs::remove_dir_all(tmpdir).unwrap();
    }

    #[test]
    fn test_list_clients_ok() {
        let clients = list_clients("src/tests/conf").unwrap();
        assert_eq!(clients.as_slice(),
                   &["dummy", "dummy_with_tokens", "invalid"])
    }

    #[test]
    fn test_list_clients_err() {
        let err = list_clients("not/an/existings/directory");
        assert_eq!(err.is_err(), true);
        let err = err.unwrap_err();
        assert_eq!(err, BearerError::ValueError("".to_string()))
    }
}