aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fatcat-cli/src/commands.rs35
-rw-r--r--fatcat-cli/src/download.rs59
-rw-r--r--fatcat-cli/src/lib.rs5
-rw-r--r--fatcat-cli/src/main.rs82
-rw-r--r--fatcat-cli/src/specifier.rs34
5 files changed, 140 insertions, 75 deletions
diff --git a/fatcat-cli/src/commands.rs b/fatcat-cli/src/commands.rs
index 624374a..4633af6 100644
--- a/fatcat-cli/src/commands.rs
+++ b/fatcat-cli/src/commands.rs
@@ -12,7 +12,7 @@ use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
use crate::{
entity_model_from_json_str, read_entity_file, ApiModelSer, EntityType, FatcatApiClient,
- Mutation, Specifier, SearchEntityType, SearchResults,
+ Mutation, SearchEntityType, SearchResults, Specifier,
};
// Want to show:
@@ -285,22 +285,13 @@ pub fn print_search_table(results: SearchResults, entity_type: SearchEntityType)
let mut tw = TabWriter::new(std::io::stdout());
match entity_type {
SearchEntityType::Release => {
- writeln!(
- tw,
- "ident\ttype\tstage\tyear\tcontainer_name\ttitle"
- )?;
+ writeln!(tw, "ident\ttype\tstage\tyear\tcontainer_name\ttitle")?;
}
- SearchEntityType::Container=> {
- writeln!(
- tw,
- "ident\tissnl\tname"
- )?;
+ SearchEntityType::Container => {
+ writeln!(tw, "ident\tissnl\tname")?;
}
SearchEntityType::File => {
- writeln!(
- tw,
- "ident\tsha1\tsize_bytes\tmimetype"
- )?;
+ writeln!(tw, "ident\tsha1\tsize_bytes\tmimetype")?;
}
}
for hit in results {
@@ -313,7 +304,9 @@ pub fn print_search_table(results: SearchResults, entity_type: SearchEntityType)
hit["ident"].as_str().unwrap_or("-"),
hit["release_type"].as_str().unwrap_or("-"),
hit["release_stage"].as_str().unwrap_or("-"),
- hit["release_year"].as_u64().map_or("-".to_string(), |v| v.to_string()),
+ hit["release_year"]
+ .as_u64()
+ .map_or("-".to_string(), |v| v.to_string()),
hit["container_name"].as_str().unwrap_or("-"),
hit["title"].as_str().unwrap_or("-"),
)?;
@@ -333,7 +326,9 @@ pub fn print_search_table(results: SearchResults, entity_type: SearchEntityType)
"file_{}\t{}\t{}\t{}",
hit["ident"].as_str().unwrap_or("-"),
hit["sha1"].as_str().unwrap_or("-"),
- hit["size_bytes"].as_u64().map_or("-".to_string(), |v| v.to_string()),
+ hit["size_bytes"]
+ .as_u64()
+ .map_or("-".to_string(), |v| v.to_string()),
hit["mimetype"].as_str().unwrap_or("-"),
)?;
}
@@ -505,7 +500,9 @@ impl BatchGrouper {
mutations: Vec<Mutation>,
) -> Result<models::EntityEdit> {
let obj: serde_json::Value = serde_json::from_str(json_str)?;
- let ident = obj["ident"].as_str().ok_or(anyhow!("expect entity JSON to have 'ident' field"))?;
+ let ident = obj["ident"]
+ .as_str()
+ .ok_or(anyhow!("expect entity JSON to have 'ident' field"))?;
let editgroup_id = self.increment_editgroup(api_client)?;
let mut entity = entity_model_from_json_str(self.entity_type, &json_str)?;
entity.mutate(mutations)?;
@@ -522,7 +519,9 @@ impl BatchGrouper {
json_str: &str,
) -> Result<models::EntityEdit> {
let obj: serde_json::Value = serde_json::from_str(json_str)?;
- let ident = obj["ident"].as_str().ok_or(anyhow!("expect entity JSON to have 'ident' field"))?;
+ let ident = obj["ident"]
+ .as_str()
+ .ok_or(anyhow!("expect entity JSON to have 'ident' field"))?;
let editgroup_id = self.increment_editgroup(api_client)?;
api_client.delete_entity(
Specifier::from_ident(self.entity_type, ident.to_string()),
diff --git a/fatcat-cli/src/download.rs b/fatcat-cli/src/download.rs
index d0f6c2e..2fdfeb9 100644
--- a/fatcat-cli/src/download.rs
+++ b/fatcat-cli/src/download.rs
@@ -1,17 +1,17 @@
+use crate::{ApiModelIdent, Specifier};
use anyhow::{anyhow, Context, Result};
+use crossbeam_channel as channel;
use fatcat_openapi::models::{FileEntity, ReleaseEntity};
use indicatif::{ProgressBar, ProgressStyle};
-use log::{info, error};
+use log::{error, info};
use reqwest::header::USER_AGENT;
+use sha1::Sha1;
use std::fmt;
use std::fs::File;
use std::io::{self, BufRead};
use std::path::PathBuf;
-use url::Url;
-use crate::{ApiModelIdent, Specifier};
-use sha1::Sha1;
use std::thread;
-use crossbeam_channel as channel;
+use url::Url;
#[derive(Debug, PartialEq, Clone)]
pub enum DownloadStatus {
@@ -61,7 +61,10 @@ struct Sha1WriteWrapper<W> {
impl<W> Sha1WriteWrapper<W> {
fn new(writer: W) -> Self {
- Sha1WriteWrapper { writer, hasher: Sha1::new() }
+ Sha1WriteWrapper {
+ writer,
+ hasher: Sha1::new(),
+ }
}
fn into_hexdigest(self) -> String {
@@ -95,7 +98,6 @@ fn rewrite_wayback_url(url: Url) -> Result<Url> {
}
fn default_filename(specifier: &Specifier, fe: &FileEntity) -> Result<PathBuf> {
-
let file_suffix = match fe.mimetype.as_ref().map(String::as_str) {
Some("application/pdf") => ".pdf",
Some("application/postscript") => ".ps",
@@ -112,7 +114,12 @@ fn default_filename(specifier: &Specifier, fe: &FileEntity) -> Result<PathBuf> {
}
/// Attempts to download a file entity, including verifying checksum.
-pub fn download_file(fe: &FileEntity, specifier: &Specifier, output_path: Option<PathBuf>, show_progress: bool) -> Result<DownloadStatus> {
+pub fn download_file(
+ fe: &FileEntity,
+ specifier: &Specifier,
+ output_path: Option<PathBuf>,
+ show_progress: bool,
+) -> Result<DownloadStatus> {
let expected_sha1 = match &fe.sha1 {
Some(v) => v,
None => return Ok(DownloadStatus::FileMissingMetadata),
@@ -174,7 +181,10 @@ pub fn download_file(fe: &FileEntity, specifier: &Specifier, output_path: Option
let client = reqwest::blocking::Client::new();
let mut resp = match client
.get(url)
- .header(USER_AGENT, format!("fatcat-cli/{}", env!("CARGO_PKG_VERSION")))
+ .header(
+ USER_AGENT,
+ format!("fatcat-cli/{}", env!("CARGO_PKG_VERSION")),
+ )
.send()
{
Ok(r) => r,
@@ -206,13 +216,13 @@ pub fn download_file(fe: &FileEntity, specifier: &Specifier, output_path: Option
let result = resp.copy_to(&mut wrapped_file);
let out_sha1 = wrapped_file.into_hexdigest();
(result, out_sha1)
- },
+ }
false => {
let mut wrapped_file = Sha1WriteWrapper::new(download_file);
let result = resp.copy_to(&mut wrapped_file);
let out_sha1 = wrapped_file.into_hexdigest();
(result, out_sha1)
- },
+ }
};
let out_size = match write_result {
@@ -239,7 +249,11 @@ pub fn download_file(fe: &FileEntity, specifier: &Specifier, output_path: Option
))
}
-pub fn download_release(re: &ReleaseEntity, output_path: Option<PathBuf>, show_progress: bool) -> Result<DownloadStatus> {
+pub fn download_release(
+ re: &ReleaseEntity,
+ output_path: Option<PathBuf>,
+ show_progress: bool,
+) -> Result<DownloadStatus> {
let file_entities = match &re.files {
None => {
return Err(anyhow!(
@@ -261,7 +275,11 @@ pub fn download_release(re: &ReleaseEntity, output_path: Option<PathBuf>, show_p
}
/// Tries either file or release
-fn download_entity(json_str: String, output_path: Option<PathBuf>, show_progress: bool) -> Result<(DownloadStatus, String)> {
+fn download_entity(
+ json_str: String,
+ output_path: Option<PathBuf>,
+ show_progress: bool,
+) -> Result<(DownloadStatus, String)> {
let release_attempt = serde_json::from_str::<ReleaseEntity>(&json_str);
if let Ok(re) = release_attempt {
if re.ident.is_some() && (re.title.is_some() || re.files.is_some()) {
@@ -314,10 +332,14 @@ fn loop_printer(
Ok(())
}
-fn loop_download_tasks(task_receiver: channel::Receiver<DownloadTask>, output_sender: channel::Sender<String>) {
+fn loop_download_tasks(
+ task_receiver: channel::Receiver<DownloadTask>,
+ output_sender: channel::Sender<String>,
+) {
let thread_result: Result<()> = (|| {
for task in task_receiver {
- let (_, status_line) = download_entity(task.json_str, task.output_path, task.show_progress)?;
+ let (_, status_line) =
+ download_entity(task.json_str, task.output_path, task.show_progress)?;
output_sender.send(status_line)?;
}
Ok(())
@@ -328,7 +350,12 @@ fn loop_download_tasks(task_receiver: channel::Receiver<DownloadTask>, output_se
thread_result.unwrap()
}
-pub fn download_batch(input_path: Option<PathBuf>, output_dir: Option<PathBuf>, limit: Option<u64>, jobs: u64) -> Result<u64> {
+pub fn download_batch(
+ input_path: Option<PathBuf>,
+ output_dir: Option<PathBuf>,
+ limit: Option<u64>,
+ jobs: u64,
+) -> Result<u64> {
let mut count = 0;
assert!(jobs > 0 && jobs <= 12);
diff --git a/fatcat-cli/src/lib.rs b/fatcat-cli/src/lib.rs
index beb9c97..81dbb7d 100644
--- a/fatcat-cli/src/lib.rs
+++ b/fatcat-cli/src/lib.rs
@@ -14,8 +14,7 @@ mod specifier;
pub use api::FatcatApiClient;
pub use commands::{
edit_entity_locally, print_changelog_entries, print_editgroups, print_entity_histories,
- print_search_table,
- BatchGrouper, BatchOp, ClientStatus,
+ print_search_table, BatchGrouper, BatchOp, ClientStatus,
};
pub use download::{download_batch, download_file, download_release};
pub use entities::{
@@ -23,7 +22,7 @@ pub use entities::{
Mutation,
};
pub use search::{crude_search, SearchResults};
-pub use specifier::{Specifier, EditgroupSpecifier};
+pub use specifier::{EditgroupSpecifier, Specifier};
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum EntityType {
diff --git a/fatcat-cli/src/main.rs b/fatcat-cli/src/main.rs
index 4b8322a..f5f5d8b 100644
--- a/fatcat-cli/src/main.rs
+++ b/fatcat-cli/src/main.rs
@@ -1,5 +1,6 @@
use crate::{path_or_stdin, BatchGrouper, BatchOp};
use anyhow::{anyhow, Context, Result};
+use colored_json::to_colored_json_auto;
use fatcat_cli::*;
#[allow(unused_imports)]
use log::{self, debug, info};
@@ -7,7 +8,6 @@ use std::io::Write;
use std::path::PathBuf;
use structopt::StructOpt;
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
-use colored_json::to_colored_json_auto;
#[derive(StructOpt)]
#[structopt(rename_all = "kebab-case", about = "CLI interface to Fatcat API")]
@@ -145,7 +145,6 @@ enum BatchCommand {
#[derive(StructOpt)]
enum Command {
-
/// Fetch a single entity, by "ident" or external identifier
Get {
specifier: Specifier,
@@ -387,7 +386,11 @@ fn run(opt: Opt) -> Result<()> {
if toml {
writeln!(&mut std::io::stdout(), "{}", result.to_toml_string()?)?
} else if json || true {
- writeln!(&mut std::io::stdout(), "{}", to_colored_json_auto(&result.to_json_value()?)?)?
+ writeln!(
+ &mut std::io::stdout(),
+ "{}",
+ to_colored_json_auto(&result.to_json_value()?)?
+ )?
}
}
Command::Create {
@@ -396,8 +399,16 @@ fn run(opt: Opt) -> Result<()> {
editgroup_id,
} => {
let json_str = read_entity_file(input_path)?;
- let ee = api_client.create_entity_from_json(entity_type, &json_str, editgroup_id.as_string())?;
- writeln!(&mut std::io::stdout(), "{}", to_colored_json_auto(&serde_json::to_value(&ee)?)?)?
+ let ee = api_client.create_entity_from_json(
+ entity_type,
+ &json_str,
+ editgroup_id.as_string(),
+ )?;
+ writeln!(
+ &mut std::io::stdout(),
+ "{}",
+ to_colored_json_auto(&serde_json::to_value(&ee)?)?
+ )?
}
Command::Update {
specifier,
@@ -419,9 +430,16 @@ fn run(opt: Opt) -> Result<()> {
(entity.to_json_string()?, entity.specifier())
}
};
- let ee =
- api_client.update_entity_from_json(exact_specifier, &json_str, editgroup_id.as_string())?;
- writeln!(&mut std::io::stdout(), "{}", to_colored_json_auto(&serde_json::to_value(&ee)?)?)?
+ let ee = api_client.update_entity_from_json(
+ exact_specifier,
+ &json_str,
+ editgroup_id.as_string(),
+ )?;
+ writeln!(
+ &mut std::io::stdout(),
+ "{}",
+ to_colored_json_auto(&serde_json::to_value(&ee)?)?
+ )?
}
Command::Edit {
specifier,
@@ -437,7 +455,11 @@ fn run(opt: Opt) -> Result<()> {
json,
editing_command,
)?;
- writeln!(&mut std::io::stdout(), "{}", to_colored_json_auto(&serde_json::to_value(&ee)?)?)?
+ writeln!(
+ &mut std::io::stdout(),
+ "{}",
+ to_colored_json_auto(&serde_json::to_value(&ee)?)?
+ )?
}
Command::Changelog { limit, json } => {
let resp = api_client
@@ -503,10 +525,7 @@ fn run(opt: Opt) -> Result<()> {
batch.run(&mut api_client, input_path, BatchOp::Delete, None)?;
}
Command::Batch {
- cmd: BatchCommand::Download {
- jobs,
- output_dir,
- },
+ cmd: BatchCommand::Download { jobs, output_dir },
input_path,
limit,
} => {
@@ -520,7 +539,9 @@ fn run(opt: Opt) -> Result<()> {
return Err(anyhow!("--jobs=0 not implemented"));
}
if jobs > 12 {
- return Err(anyhow!("please don't download more than 12 parallel requests"));
+ return Err(anyhow!(
+ "please don't download more than 12 parallel requests"
+ ));
}
download_batch(input_path, output_dir, limit, jobs)?;
}
@@ -604,7 +625,9 @@ fn run(opt: Opt) -> Result<()> {
let hit = hit?;
match (index_json, entity_json, entity_type) {
(false, false, _) => unreachable!("case handled above"),
- (true, _, _) => writeln!(&mut std::io::stdout(), "{}", hit.to_string())?,
+ (true, _, _) => {
+ writeln!(&mut std::io::stdout(), "{}", hit.to_string())?
+ }
(false, true, SearchEntityType::Release) => {
let specifier =
Specifier::Release(hit["ident"].as_str().unwrap().to_string());
@@ -616,8 +639,9 @@ fn run(opt: Opt) -> Result<()> {
writeln!(&mut std::io::stdout(), "{}", entity.to_json_string()?)?
}
(false, true, SearchEntityType::Container) => {
- let specifier =
- Specifier::Container(hit["ident"].as_str().unwrap().to_string());
+ let specifier = Specifier::Container(
+ hit["ident"].as_str().unwrap().to_string(),
+ );
let entity = specifier.get_from_api(
&mut api_client,
expand.clone(),
@@ -716,25 +740,41 @@ fn run(opt: Opt) -> Result<()> {
cmd: EditgroupsCommand::Create { description },
} => {
let eg = api_client.create_editgroup(Some(description))?;
- writeln!(&mut std::io::stdout(), "{}", to_colored_json_auto(&serde_json::to_value(&eg)?)?)?
+ writeln!(
+ &mut std::io::stdout(),
+ "{}",
+ to_colored_json_auto(&serde_json::to_value(&eg)?)?
+ )?
}
Command::Editgroups {
cmd: EditgroupsCommand::Accept { editgroup_id },
} => {
let msg = api_client.accept_editgroup(editgroup_id.as_string())?;
- writeln!(&mut std::io::stdout(), "{}", to_colored_json_auto(&serde_json::to_value(&msg)?)?)?
+ writeln!(
+ &mut std::io::stdout(),
+ "{}",
+ to_colored_json_auto(&serde_json::to_value(&msg)?)?
+ )?
}
Command::Editgroups {
cmd: EditgroupsCommand::Submit { editgroup_id },
} => {
let msg = api_client.update_editgroup_submit(editgroup_id.as_string(), true)?;
- writeln!(&mut std::io::stdout(), "{}", to_colored_json_auto(&serde_json::to_value(&msg)?)?)?
+ writeln!(
+ &mut std::io::stdout(),
+ "{}",
+ to_colored_json_auto(&serde_json::to_value(&msg)?)?
+ )?
}
Command::Editgroups {
cmd: EditgroupsCommand::Unsubmit { editgroup_id },
} => {
let msg = api_client.update_editgroup_submit(editgroup_id.as_string(), false)?;
- writeln!(&mut std::io::stdout(), "{}", to_colored_json_auto(&serde_json::to_value(&msg)?)?)?
+ writeln!(
+ &mut std::io::stdout(),
+ "{}",
+ to_colored_json_auto(&serde_json::to_value(&msg)?)?
+ )?
}
Command::Status { json } => {
let status = ClientStatus::generate(&mut api_client)?;
diff --git a/fatcat-cli/src/specifier.rs b/fatcat-cli/src/specifier.rs
index cac4282..ab1a421 100644
--- a/fatcat-cli/src/specifier.rs
+++ b/fatcat-cli/src/specifier.rs
@@ -2,8 +2,8 @@ use crate::{ApiEntityModel, EntityType, FatcatApiClient};
use anyhow::{anyhow, Context, Result};
use lazy_static::lazy_static;
use regex::Regex;
-use std::str::FromStr;
use std::fmt;
+use std::str::FromStr;
#[derive(Debug, PartialEq, Clone)]
pub enum ReleaseLookupKey {
@@ -61,9 +61,9 @@ pub enum FileLookupKey {
impl fmt::Display for FileLookupKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
- Self::SHA1=> write!(f, "sha1"),
- Self::SHA256=> write!(f, "sha256"),
- Self::MD5=> write!(f, "md5"),
+ Self::SHA1 => write!(f, "sha1"),
+ Self::SHA256 => write!(f, "sha256"),
+ Self::MD5 => write!(f, "md5"),
}
}
}
@@ -560,10 +560,9 @@ impl FromStr for Specifier {
// then try lookup prefixes
lazy_static! {
- static ref SPEC_LOOKUP_RE: Regex = Regex::new(
- r"^(doi|pmcid|pmid|arxiv|issnl|orcid|sha1|sha256|md5|user):(\S+)$"
- )
- .unwrap();
+ static ref SPEC_LOOKUP_RE: Regex =
+ Regex::new(r"^(doi|pmcid|pmid|arxiv|issnl|orcid|sha1|sha256|md5|user):(\S+)$")
+ .unwrap();
}
if let Some(caps) = SPEC_LOOKUP_RE.captures(s) {
return match (&caps[1], &caps[2]) {
@@ -618,7 +617,6 @@ impl FromStr for Specifier {
pub struct EditgroupSpecifier(String);
impl EditgroupSpecifier {
-
pub fn as_string(self) -> String {
self.0
}
@@ -629,15 +627,13 @@ impl FromStr for EditgroupSpecifier {
fn from_str(s: &str) -> Result<Self, Self::Err> {
lazy_static! {
- static ref SPEC_ENTITY_RE: Regex = Regex::new(r"^(editgroup_)?([2-7a-z]{26})$").unwrap();
+ static ref SPEC_ENTITY_RE: Regex =
+ Regex::new(r"^(editgroup_)?([2-7a-z]{26})$").unwrap();
}
if let Some(caps) = SPEC_ENTITY_RE.captures(s) {
- return Ok(EditgroupSpecifier(caps[2].to_string()))
+ return Ok(EditgroupSpecifier(caps[2].to_string()));
}
- Err(anyhow!(
- "expecting an editgroup identifier, got: {}",
- s
- ))
+ Err(anyhow!("expecting an editgroup identifier, got: {}", s))
}
}
@@ -672,11 +668,15 @@ mod tests {
fn test_editgroup_from_str() -> () {
assert!(EditgroupSpecifier::from_str("release_asdf").is_err());
assert_eq!(
- EditgroupSpecifier::from_str("editgroup_iimvc523xbhqlav6j3sbthuehu").unwrap().0,
+ EditgroupSpecifier::from_str("editgroup_iimvc523xbhqlav6j3sbthuehu")
+ .unwrap()
+ .0,
"iimvc523xbhqlav6j3sbthuehu".to_string()
);
assert_eq!(
- EditgroupSpecifier::from_str("iimvc523xbhqlav6j3sbthuehu").unwrap().0,
+ EditgroupSpecifier::from_str("iimvc523xbhqlav6j3sbthuehu")
+ .unwrap()
+ .0,
"iimvc523xbhqlav6j3sbthuehu".to_string()
);
}