summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock12
-rw-r--r--Cargo.toml1
-rw-r--r--src/cli.rs13
-rw-r--r--src/cmd/sync.rs5
-rw-r--r--src/lastfm/mod.rs49
-rw-r--r--src/main.rs1
-rw-r--r--src/paths.rs7
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)",
@@ -843,6 +844,16 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -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<String>,
- pub api_key: Option<String>,
}
pub fn get_options() -> failure::Fallible<Options> {
@@ -17,20 +16,9 @@ pub fn get_options() -> failure::Fallible<Options> {
.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<Options> {
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<LastFMClient> {
+ 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<i64>) -> failure::Fallible<u64> {
@@ -151,3 +156,39 @@ impl LastFMClient {
}
}
}
+
+fn find_api_key() -> failure::Fallible<String> {
+ 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<std::path::PathBuf> {
.data_dir()
.join("tracks.sqlite"))
}
+
+pub fn api_key_path() -> failure::Fallible<std::path::PathBuf> {
+ Ok(directories::ProjectDirs::from("", "", "lastfm-query")
+ .ok_or_else(|| failure::err_msg("couldn't determine config directory"))?
+ .config_dir()
+ .join("lastfm-api-key"))
+}