#[macro_use]
pub extern crate log;
extern crate clap_verbosity_flag;
extern crate pretty_env_logger;
use atty::Stream;
use human_panic::setup_panic;
use serde_json::json;
use shellexpand;
use std::fs::OpenOptions;
use std::io::prelude::*;
use structopt::StructOpt;
use superkeyloader_lib::*;
#[derive(Debug, StructOpt)]
struct CliArgs {
username: String,
#[structopt(
short = "o",
long = "output",
required = false,
default_value = "~/.ssh/authorized_keys",
parse(from_os_str)
)]
path: std::path::PathBuf,
#[structopt(long = "token")]
token: Option<String>,
#[structopt(flatten)]
verbose: clap_verbosity_flag::Verbosity,
#[structopt(short = "m", long = "human", conflicts_with_all(&["json", "stdout"]))]
human: bool,
#[structopt(short = "j", long = "json", conflicts_with_all(&["human", "stdout"]))]
json: bool,
#[structopt(short = "p", long = "stdout", conflicts_with_all(&["human", "json"]))]
stdout: bool,
}
fn main() -> Result<(), ExitDisplay<String>> {
setup_panic!();
let args = CliArgs::from_args();
let log_level = match args.verbose.log_level() {
Some(level) => level.to_level_filter(),
None => log::LevelFilter::Off, };
let pkg_name = Option::from(env!("CARGO_PKG_NAME"));
pretty_env_logger::formatted_builder()
.filter(pkg_name, log_level)
.init();
info!("Human: {} - JSON: {}", &args.human, &args.json);
info!("Downloading keys for '{}' from GitHub...", &args.username);
let keys = error_handler_wrapper(gh::get_keys(&args.username, args.token))?;
let keys_number = keys.len();
info!("Downloaded {} keys.", keys_number);
let args_path_string = args.path.to_str().unwrap();
let authorized_keys_path = shellexpand::tilde(args_path_string).to_owned().to_string();
info!("Got 'authorized_keys' file path: {}", authorized_keys_path);
let authorized_keys_file = OpenOptions::new()
.append(true)
.create(true)
.open(authorized_keys_path)
.unwrap();
info!("Opened/Created 'authorized_keys' file in append mode");
for (i, key) in keys.iter().enumerate() {
match writeln!(&authorized_keys_file, "{}", key) {
Ok(..) => {
debug!("Wrote key {}/{} ({}...)", i, keys_number, &key[..48]);
}
Err(why) => {
return Err(format!(
"Error writing key {}/{} to 'authorized_keys' file. Caused by {}",
i, keys_number, why
)
.into());
}
};
}
let output: String;
let is_tty = atty::is(Stream::Stdout);
let human_output = !args.json && is_tty || args.human;
if human_output {
output = format!(
"Downloaded {} SSH keys for user '{}' \
from {} and appended to 'authorized_keys' file.",
keys_number, &args.username, "GitHub"
);
} else {
output = json!({ "keys": keys }).to_string();
}
if !args.verbose.is_silent() {
println!("{}", output);
}
Ok(())
}