From bd7c31233de96d96e16f9c15e22d014514f7a173 Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Thu, 8 Nov 2018 01:52:31 -0500 Subject: stop passing the api key on the command line --- Cargo.lock | 12 ++++++++++++ Cargo.toml | 1 + src/cli.rs | 13 ------------- src/cmd/sync.rs | 5 ++--- src/lastfm/mod.rs | 49 +++++++++++++++++++++++++++++++++++++++++++++---- src/main.rs | 1 + src/paths.rs | 7 +++++++ 7 files changed, 68 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f438daa..65f5ac3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -418,6 +418,7 @@ dependencies = [ "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "indicatif 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "reqwest 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rpassword 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "rusqlite 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", @@ -842,6 +843,16 @@ dependencies = [ "uuid 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rpassword" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rusqlite" version = "0.15.0" @@ -1466,6 +1477,7 @@ dependencies = [ "checksum regex-syntax 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "747ba3b235651f6e2f67dfa8bcdcd073ddb7c243cb21c442fc12395dfcac212d" "checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" "checksum reqwest 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)" = "00a5870d8edc74fc6e1eb58edbd2815d2243e1a2255d6bf9c82a7a875901b5db" +"checksum rpassword 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d127299b02abda51634f14025aec43ae87a7aa7a95202b6a868ec852607d1451" "checksum rusqlite 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "39bae767eb27866f5c0be918635ae54af705bc09db11be2c43a3c6b361cf3462" "checksum rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "bcfe5b13211b4d78e5c2cadfebd7769197d95c639c35a50057eb4c05de811395" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" diff --git a/Cargo.toml b/Cargo.toml index 1e5783a..c2bb578 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ directories = "1.0" failure = "0.1" indicatif = "0.9" reqwest = "0.9" +rpassword = "2.0" rusqlite = "0.15" serde = "1.0" serde_json = "1.0" diff --git a/src/cli.rs b/src/cli.rs index d296fea..0c140e7 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -7,7 +7,6 @@ pub enum Command { pub struct Options { pub command: Command, pub username: Option, - pub api_key: Option, } pub fn get_options() -> failure::Fallible { @@ -17,20 +16,9 @@ pub fn get_options() -> failure::Fallible { .about("Updates the local copy of track data from last.fm") .arg( clap::Arg::with_name("username") - .short("u") - .long("username") - .value_name("USERNAME") .required(true) .help("last.fm username to fetch tracks for") ) - .arg( - clap::Arg::with_name("api-key") - .short("k") - .long("api-key") - .value_name("API_KEY") - .required(true) - .help("last.fm api key") - ) ) .get_matches(); @@ -43,6 +31,5 @@ pub fn get_options() -> failure::Fallible { Ok(Options { command: command, username: sub_matches.value_of("username").map(|s| s.to_string()), - api_key: sub_matches.value_of("api-key").map(|s| s.to_string()), }) } diff --git a/src/cmd/sync.rs b/src/cmd/sync.rs index 65b48b7..9dfb11f 100644 --- a/src/cmd/sync.rs +++ b/src/cmd/sync.rs @@ -4,11 +4,10 @@ use lastfm; use paths; pub fn run(opts: &cli::Options) -> failure::Fallible<()> { - let db = db::DB::new(&paths::dbpath()?)?; + let db = db::DB::new(&paths::db_path()?)?; let lastfm = lastfm::LastFMClient::new( - opts.api_key.as_ref().unwrap(), opts.username.as_ref().unwrap() - ); + )?; let from = db.most_recent_timestamp()?.map(|x| x + 1); let to_fetch = lastfm.track_count(from)?; diff --git a/src/lastfm/mod.rs b/src/lastfm/mod.rs index fc2eb5b..b5888cc 100644 --- a/src/lastfm/mod.rs +++ b/src/lastfm/mod.rs @@ -1,3 +1,8 @@ +use paths; + +use failure::Fail; +use std::io::{Read, Write}; + mod api_types; const API_ROOT: &'static str = "https://ws.audioscrobbler.com/2.0/"; @@ -98,12 +103,12 @@ impl<'a> Iterator for Tracks<'a> { } impl LastFMClient { - pub fn new(api_key: &str, user: &str) -> LastFMClient { - LastFMClient { + pub fn new(user: &str) -> failure::Fallible { + Ok(LastFMClient { client: reqwest::Client::new(), - api_key: api_key.to_string(), + api_key: find_api_key()?, user: user.to_string(), - } + }) } pub fn track_count(&self, from: Option) -> failure::Fallible { @@ -151,3 +156,39 @@ impl LastFMClient { } } } + +fn find_api_key() -> failure::Fallible { + let api_key_path = paths::api_key_path() + .map_err(|e| e.context("failed to determine api key path"))?; + let api_key = if api_key_path.exists() { + let mut api_key = String::new(); + let mut f = std::fs::File::open(&api_key_path) + .map_err(|e| { + e.context(format!("failed to open {}", api_key_path.display())) + })?; + f.read_to_string(&mut api_key) + .map_err(|e| { + e.context(format!("failed to read from {}", api_key_path.display())) + })?; + api_key + } + else { + let api_key = rpassword::prompt_password_stderr( + &format!( + "last.fm api key (will be stored in {}): ", + api_key_path.display() + ) + )?; + std::fs::create_dir_all(api_key_path.parent().unwrap())?; + let mut f = std::fs::File::create(&api_key_path) + .map_err(|e| { + e.context(format!("failed to open {}", api_key_path.display())) + })?; + f.write_all(api_key.as_bytes()) + .map_err(|e| { + e.context(format!("failed to write to {}", api_key_path.display())) + })?; + api_key + }; + Ok(api_key.trim_end().to_string()) +} diff --git a/src/main.rs b/src/main.rs index 2518e55..d7d80fd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,7 @@ extern crate directories; extern crate failure; extern crate indicatif; extern crate reqwest; +extern crate rpassword; extern crate rusqlite; extern crate serde; extern crate serde_json; diff --git a/src/paths.rs b/src/paths.rs index a3f1eb8..badb783 100644 --- a/src/paths.rs +++ b/src/paths.rs @@ -4,3 +4,10 @@ pub fn db_path() -> failure::Fallible { .data_dir() .join("tracks.sqlite")) } + +pub fn api_key_path() -> failure::Fallible { + Ok(directories::ProjectDirs::from("", "", "lastfm-query") + .ok_or_else(|| failure::err_msg("couldn't determine config directory"))? + .config_dir() + .join("lastfm-api-key")) +} -- cgit v1.2.3