summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--rust/fatcat-cli/src/api.rs365
-rw-r--r--rust/fatcat-cli/src/entities.rs211
-rw-r--r--rust/fatcat-cli/src/lib.rs107
-rw-r--r--rust/fatcat-cli/src/main.rs314
-rw-r--r--rust/fatcat-cli/src/search.rs113
-rw-r--r--rust/fatcat-cli/src/specifier.rs437
6 files changed, 1110 insertions, 437 deletions
diff --git a/rust/fatcat-cli/src/api.rs b/rust/fatcat-cli/src/api.rs
index 41718ea..3fa67e9 100644
--- a/rust/fatcat-cli/src/api.rs
+++ b/rust/fatcat-cli/src/api.rs
@@ -1,14 +1,18 @@
-
-use hyper::client::ResponseFuture;
-use fatcat_openapi::{ApiNoContext, ContextWrapperExt};
+use crate::{parse_macaroon_editor_id, ClientStatus, EntityType, Specifier};
+use anyhow::{anyhow, Context, Result};
use fatcat_openapi::client::Client;
use fatcat_openapi::models;
-use swagger::{AuthData, ContextBuilder, EmptyContext, Push, XSpanIdString, auth};
-use anyhow::{Result, anyhow, Context};
-use crate::{ClientStatus,parse_macaroon_editor_id,Specifier, EntityType};
+use fatcat_openapi::{ApiNoContext, ContextWrapperExt};
+use hyper::client::ResponseFuture;
+use swagger::{auth, AuthData, ContextBuilder, EmptyContext, Push, XSpanIdString};
use tokio::runtime::current_thread::Runtime;
-type FatcatApiContextType = swagger::make_context_ty!( ContextBuilder, EmptyContext, Option<AuthData>, XSpanIdString);
+type FatcatApiContextType = swagger::make_context_ty!(
+ ContextBuilder,
+ EmptyContext,
+ Option<AuthData>,
+ XSpanIdString
+);
pub struct FatcatApiClient<'a> {
pub api: fatcat_openapi::ContextWrapper<'a, Client<ResponseFuture>, FatcatApiContextType>,
@@ -19,11 +23,15 @@ pub struct FatcatApiClient<'a> {
}
impl<'a> FatcatApiClient<'a> {
-
- pub fn new(client: &'a fatcat_openapi::client::Client<ResponseFuture>, api_host: String, api_token: Option<String>) -> Result<Self> {
-
+ pub fn new(
+ client: &'a fatcat_openapi::client::Client<ResponseFuture>,
+ api_host: String,
+ api_token: Option<String>,
+ ) -> Result<Self> {
let auth_data = match api_token {
- Some(ref token) => Some(AuthData::Bearer(auth::Bearer{ token: token.clone() })),
+ Some(ref token) => Some(AuthData::Bearer(auth::Bearer {
+ token: token.clone(),
+ })),
None => None,
};
//info!("{:?}", auth_data);
@@ -34,14 +42,19 @@ impl<'a> FatcatApiClient<'a> {
XSpanIdString::default()
);
- let wrapped_client: fatcat_openapi::ContextWrapper<Client<ResponseFuture>, FatcatApiContextType> = client.with_context(context);
+ let wrapped_client: fatcat_openapi::ContextWrapper<
+ Client<ResponseFuture>,
+ FatcatApiContextType,
+ > = client.with_context(context);
let rt: Runtime = Runtime::new().expect("create tokio runtime");
let editor_id = match api_token {
- Some(ref token) => Some(parse_macaroon_editor_id(token).context("parse API auth token")?),
+ Some(ref token) => {
+ Some(parse_macaroon_editor_id(token).context("parse API auth token")?)
+ }
None => None,
};
-
+
Ok(FatcatApiClient {
api: wrapped_client,
rt,
@@ -53,20 +66,40 @@ impl<'a> FatcatApiClient<'a> {
pub fn status(&mut self) -> Result<ClientStatus> {
let last_changelog = match self.rt.block_on(self.api.get_changelog(Some(1))) {
- Ok(fatcat_openapi::GetChangelogResponse::Success(entry_vec)) => Some(entry_vec[0].index),
+ Ok(fatcat_openapi::GetChangelogResponse::Success(entry_vec)) => {
+ Some(entry_vec[0].index)
+ }
Ok(_) | Err(_) => None,
};
let has_api_token = self.api_token.is_some();
let account: Option<models::Editor> = if has_api_token && last_changelog.is_some() {
- match self.rt.block_on(self.api.auth_check(None)).context("check auth token")? {
+ match self
+ .rt
+ .block_on(self.api.auth_check(None))
+ .context("check auth token")?
+ {
fatcat_openapi::AuthCheckResponse::Success(_) => Ok(()),
- fatcat_openapi::AuthCheckResponse::Forbidden(err) => Err(anyhow!("Forbidden ({}): {}", err.error, err.message)),
- fatcat_openapi::AuthCheckResponse::NotAuthorized{body: err, ..} => Err(anyhow!("Bad Request ({}): {}", err.error, err.message)),
+ fatcat_openapi::AuthCheckResponse::Forbidden(err) => {
+ Err(anyhow!("Forbidden ({}): {}", err.error, err.message))
+ }
+ fatcat_openapi::AuthCheckResponse::NotAuthorized { body: err, .. } => {
+ Err(anyhow!("Bad Request ({}): {}", err.error, err.message))
+ }
resp => return Err(anyhow!("{:?}", resp)).context("auth check failed"),
- }.context("check auth token")?;
- match self.rt.block_on(self.api.get_editor(self.editor_id.as_ref().unwrap().to_string())).context("fetching editor account info")? {
+ }
+ .context("check auth token")?;
+ match self
+ .rt
+ .block_on(
+ self.api
+ .get_editor(self.editor_id.as_ref().unwrap().to_string()),
+ )
+ .context("fetching editor account info")?
+ {
fatcat_openapi::GetEditorResponse::Found(editor) => Some(editor),
- fatcat_openapi::GetEditorResponse::NotFound(err) => return Err(anyhow!("Not Found: {}", err.message)),
+ fatcat_openapi::GetEditorResponse::NotFound(err) => {
+ return Err(anyhow!("Not Found: {}", err.message))
+ }
resp => return Err(anyhow!("{:?}", resp)).context("editor fetch failed"),
}
} else {
@@ -80,18 +113,29 @@ impl<'a> FatcatApiClient<'a> {
})
}
- pub fn update_editgroup_submit(&mut self, editgroup_id: String, submit: bool) -> Result<models::Editgroup> {
- let result = self.rt.block_on(
- self.api.get_editgroup(editgroup_id.clone())
- ).context("fetch editgroups")?;
+ pub fn update_editgroup_submit(
+ &mut self,
+ editgroup_id: String,
+ submit: bool,
+ ) -> Result<models::Editgroup> {
+ let result = self
+ .rt
+ .block_on(self.api.get_editgroup(editgroup_id.clone()))
+ .context("fetch editgroups")?;
let eg = match result {
fatcat_openapi::GetEditgroupResponse::Found(eg) => eg,
- other => return Err(anyhow!("{:?}", other))
- .with_context(|| format!("failed to fetch editgroup {}", editgroup_id)),
+ other => {
+ return Err(anyhow!("{:?}", other))
+ .with_context(|| format!("failed to fetch editgroup {}", editgroup_id))
+ }
};
- let result = self.rt.block_on(
- self.api.update_editgroup(editgroup_id.clone(), eg, Some(submit))
- ).context("submit editgroup")?;
+ let result = self
+ .rt
+ .block_on(
+ self.api
+ .update_editgroup(editgroup_id.clone(), eg, Some(submit)),
+ )
+ .context("submit editgroup")?;
match result {
fatcat_openapi::UpdateEditgroupResponse::UpdatedEditgroup(eg) => Ok(eg),
other => Err(anyhow!("{:?}", other))
@@ -99,11 +143,18 @@ impl<'a> FatcatApiClient<'a> {
}
}
- pub fn delete_entity(&mut self, specifier: Specifier, editgroup_id: String) -> Result<models::EntityEdit> {
+ pub fn delete_entity(
+ &mut self,
+ specifier: Specifier,
+ editgroup_id: String,
+ ) -> Result<models::EntityEdit> {
use Specifier::*;
let specifier = specifier.into_entity_specifier(self)?;
match specifier.clone() {
- Release(fcid) => match self.rt.block_on(self.api.delete_release(editgroup_id, fcid))? {
+ Release(fcid) => match self
+ .rt
+ .block_on(self.api.delete_release(editgroup_id, fcid))?
+ {
fatcat_openapi::DeleteReleaseResponse::DeletedEntity(ee) => Ok(ee),
other => Err(anyhow!("{:?}", other)),
},
@@ -111,11 +162,17 @@ impl<'a> FatcatApiClient<'a> {
fatcat_openapi::DeleteWorkResponse::DeletedEntity(ee) => Ok(ee),
other => Err(anyhow!("{:?}", other)),
},
- Container(fcid) => match self.rt.block_on(self.api.delete_container(editgroup_id, fcid))? {
+ Container(fcid) => match self
+ .rt
+ .block_on(self.api.delete_container(editgroup_id, fcid))?
+ {
fatcat_openapi::DeleteContainerResponse::DeletedEntity(ee) => Ok(ee),
other => Err(anyhow!("{:?}", other)),
},
- Creator(fcid) => match self.rt.block_on(self.api.delete_creator(editgroup_id, fcid))? {
+ Creator(fcid) => match self
+ .rt
+ .block_on(self.api.delete_creator(editgroup_id, fcid))?
+ {
fatcat_openapi::DeleteCreatorResponse::DeletedEntity(ee) => Ok(ee),
other => Err(anyhow!("{:?}", other)),
},
@@ -123,104 +180,282 @@ impl<'a> FatcatApiClient<'a> {
fatcat_openapi::DeleteFileResponse::DeletedEntity(ee) => Ok(ee),
other => Err(anyhow!("{:?}", other)),
},
- FileSet(fcid) => match self.rt.block_on(self.api.delete_fileset(editgroup_id, fcid))? {
+ FileSet(fcid) => match self
+ .rt
+ .block_on(self.api.delete_fileset(editgroup_id, fcid))?
+ {
fatcat_openapi::DeleteFilesetResponse::DeletedEntity(ee) => Ok(ee),
other => Err(anyhow!("{:?}", other)),
},
- WebCapture(fcid) => match self.rt.block_on(self.api.delete_webcapture(editgroup_id, fcid))? {
+ WebCapture(fcid) => match self
+ .rt
+ .block_on(self.api.delete_webcapture(editgroup_id, fcid))?
+ {
fatcat_openapi::DeleteWebcaptureResponse::DeletedEntity(ee) => Ok(ee),
other => Err(anyhow!("{:?}", other)),
},
Editgroup(..) | Editor(..) => unimplemented!("deletion for this entity type"),
Changelog(..) => return Err(anyhow!("mutating this entity type doesn't make sense")),
- EditorUsername(..) | ReleaseLookup(..) | ContainerLookup(..) | FileLookup(..) | CreatorLookup(..) =>
- return Err(anyhow!("into_entity_specifier() didn't work?")),
- }.with_context(|| format!("failed to delete {:?}", specifier))
+ EditorUsername(..) | ReleaseLookup(..) | ContainerLookup(..) | FileLookup(..)
+ | CreatorLookup(..) => return Err(anyhow!("into_entity_specifier() didn't work?")),
+ }
+ .with_context(|| format!("failed to delete {:?}", specifier))
}
- pub fn create_entity_from_json(&mut self, entity_type: EntityType, json_str: &str, editgroup_id: String) -> Result<models::EntityEdit> {
+ pub fn create_entity_from_json(
+ &mut self,
+ entity_type: EntityType,
+ json_str: &str,
+ editgroup_id: String,
+ ) -> Result<models::EntityEdit> {
match entity_type {
EntityType::Release => {
- match self.rt.block_on(self.api.create_release(editgroup_id, serde_json::from_str(&json_str)?))? {
+ match self.rt.block_on(
+ self.api
+ .create_release(editgroup_id, serde_json::from_str(&json_str)?),
+ )? {
fatcat_openapi::CreateReleaseResponse::CreatedEntity(ee) => Ok(ee),
other => Err(anyhow!("{:?}", other)),
}
- },
+ }
EntityType::Work => {
- match self.rt.block_on(self.api.create_work(editgroup_id, serde_json::from_str(&json_str)?))? {
+ match self.rt.block_on(
+ self.api
+ .create_work(editgroup_id, serde_json::from_str(&json_str)?),
+ )? {
fatcat_openapi::CreateWorkResponse::CreatedEntity(ee) => Ok(ee),
other => Err(anyhow!("{:?}", other)),
}
- },
+ }
EntityType::Creator => {
- match self.rt.block_on(self.api.create_creator(editgroup_id, serde_json::from_str(&json_str)?))? {
+ match self.rt.block_on(
+ self.api
+ .create_creator(editgroup_id, serde_json::from_str(&json_str)?),
+ )? {
fatcat_openapi::CreateCreatorResponse::CreatedEntity(ee) => Ok(ee),
other => Err(anyhow!("{:?}", other)),
}
- },
+ }
EntityType::Container => {
- match self.rt.block_on(self.api.create_container(editgroup_id, serde_json::from_str(&json_str)?))? {
+ match self.rt.block_on(
+ self.api
+ .create_container(editgroup_id, serde_json::from_str(&json_str)?),
+ )? {
fatcat_openapi::CreateContainerResponse::CreatedEntity(ee) => Ok(ee),
other => Err(anyhow!("{:?}", other)),
}
- },
+ }
EntityType::File => {
- match self.rt.block_on(self.api.create_file(editgroup_id, serde_json::from_str(&json_str)?))? {
+ match self.rt.block_on(
+ self.api
+ .create_file(editgroup_id, serde_json::from_str(&json_str)?),
+ )? {
fatcat_openapi::CreateFileResponse::CreatedEntity(ee) => Ok(ee),
other => Err(anyhow!("{:?}", other)),
}
- },
+ }
EntityType::FileSet => {
- match self.rt.block_on(self.api.create_fileset(editgroup_id, serde_json::from_str(&json_str)?))? {
+ match self.rt.block_on(
+ self.api
+ .create_fileset(editgroup_id, serde_json::from_str(&json_str)?),
+ )? {
fatcat_openapi::CreateFilesetResponse::CreatedEntity(ee) => Ok(ee),
other => Err(anyhow!("{:?}", other)),
}
- },
+ }
EntityType::WebCapture => {
- match self.rt.block_on(self.api.create_webcapture(editgroup_id, serde_json::from_str(&json_str)?))? {
+ match self.rt.block_on(
+ self.api
+ .create_webcapture(editgroup_id, serde_json::from_str(&json_str)?),
+ )? {
fatcat_openapi::CreateWebcaptureResponse::CreatedEntity(ee) => Ok(ee),
other => Err(anyhow!("{:?}", other)),
}
+ }
+ }
+ .with_context(|| format!("parsing and creating {:?} entity", entity_type))
+ }
+
+ pub fn existing_edit_in_editgroup(
+ &mut self,
+ editgroup: &models::Editgroup,
+ specifier: &Specifier,
+ ) -> Option<models::EntityEdit> {
+ use Specifier::*;
+ let (fcid, edit_list) = match specifier.clone() {
+ Release(fcid) => (fcid, editgroup.edits.as_ref().unwrap().releases.clone()),
+ Work(fcid) => (fcid, editgroup.edits.as_ref().unwrap().works.clone()),
+ Container(fcid) => (fcid, editgroup.edits.as_ref().unwrap().containers.clone()),
+ Creator(fcid) => (fcid, editgroup.edits.as_ref().unwrap().creators.clone()),
+ File(fcid) => (fcid, editgroup.edits.as_ref().unwrap().files.clone()),
+ FileSet(fcid) => (fcid, editgroup.edits.as_ref().unwrap().filesets.clone()),
+ WebCapture(fcid) => (fcid, editgroup.edits.as_ref().unwrap().webcaptures.clone()),
+ EditorUsername(..) | ReleaseLookup(..) | ContainerLookup(..) | FileLookup(..)
+ | CreatorLookup(..) | Editgroup(..) | Editor(..) | Changelog(..) => {
+ panic!("this entity type doesn't exist in editgroups")
+ }
+ };
+ for entity_edit in edit_list.unwrap() {
+ if entity_edit.ident == fcid {
+ return Some(entity_edit);
+ }
+ }
+ None
+ }
+
+ pub fn delete_editgroup_edit(
+ &mut self,
+ editgroup: &models::Editgroup,
+ specifier: &Specifier,
+ edit: &models::EntityEdit,
+ ) -> Result<()> {
+ use Specifier::*;
+ let editgroup_id = editgroup.editgroup_id.clone().unwrap();
+ let edit_id = edit.edit_id.clone();
+ match specifier.clone() {
+ Release(..) => match self
+ .rt
+ .block_on(self.api.delete_release_edit(editgroup_id, edit_id))?
+ {
+ fatcat_openapi::DeleteReleaseEditResponse::DeletedEdit(..) => Ok(()),
+ other => Err(anyhow!("{:?}", other)),
+ },
+ Work(..) => match self
+ .rt
+ .block_on(self.api.delete_work_edit(editgroup_id, edit_id))?
+ {
+ fatcat_openapi::DeleteWorkEditResponse::DeletedEdit(..) => Ok(()),
+ other => Err(anyhow!("{:?}", other)),
+ },
+ Container(..) => match self
+ .rt
+ .block_on(self.api.delete_container_edit(editgroup_id, edit_id))?
+ {
+ fatcat_openapi::DeleteContainerEditResponse::DeletedEdit(..) => Ok(()),
+ other => Err(anyhow!("{:?}", other)),
+ },
+ Creator(..) => match self
+ .rt
+ .block_on(self.api.delete_creator_edit(editgroup_id, edit_id))?
+ {
+ fatcat_openapi::DeleteCreatorEditResponse::DeletedEdit(..) => Ok(()),
+ other => Err(anyhow!("{:?}", other)),
+ },
+ File(..) => match self
+ .rt
+ .block_on(self.api.delete_file_edit(editgroup_id, edit_id))?
+ {
+ fatcat_openapi::DeleteFileEditResponse::DeletedEdit(..) => Ok(()),
+ other => Err(anyhow!("{:?}", other)),
+ },
+ FileSet(..) => match self
+ .rt
+ .block_on(self.api.delete_fileset_edit(editgroup_id, edit_id))?
+ {
+ fatcat_openapi::DeleteFilesetEditResponse::DeletedEdit(..) => Ok(()),
+ other => Err(anyhow!("{:?}", other)),
},
- }.with_context(|| format!("parsing and creating {:?} entity", entity_type))
+ WebCapture(..) => match self
+ .rt
+ .block_on(self.api.delete_webcapture_edit(editgroup_id, edit_id))?
+ {
+ fatcat_openapi::DeleteWebcaptureEditResponse::DeletedEdit(..) => Ok(()),
+ other => Err(anyhow!("{:?}", other)),
+ },
+ EditorUsername(..) | ReleaseLookup(..) | ContainerLookup(..) | FileLookup(..)
+ | CreatorLookup(..) | Editgroup(..) | Editor(..) | Changelog(..) => {
+ panic!("this entity type doesn't exist in editgroups")
+ }
+ }
}
- pub fn update_entity_from_json(&mut self, specifier: Specifier, json_str: &str, editgroup_id: String) -> Result<models::EntityEdit> {
+ pub fn update_entity_from_json(
+ &mut self,
+ specifier: Specifier,
+ json_str: &str,
+ editgroup_id: String,
+ ) -> Result<models::EntityEdit> {
use Specifier::*;
let specifier = specifier.into_entity_specifier(self)?;
+ let eg = match self
+ .rt
+ .block_on(self.api.get_editgroup(editgroup_id.clone()))?
+ {
+ fatcat_openapi::GetEditgroupResponse::Found(model) => Ok(model),
+ fatcat_openapi::GetEditgroupResponse::BadRequest(err) => {
+ Err(anyhow!("Bad Request ({}): {}", err.error, err.message))
+ }
+ fatcat_openapi::GetEditgroupResponse::NotFound(err) => {
+ Err(anyhow!("Not Found: {}", err.message))
+ }
+ resp => Err(anyhow!("{:?}", resp))
+ .with_context(|| format!("API GET failed: editgroup_{:?}", editgroup_id)),
+ }?;
+ if let Some(entity_edit) = self.existing_edit_in_editgroup(&eg, &specifier) {
+ self.delete_editgroup_edit(&eg, &specifier, &entity_edit)?;
+ };
match specifier.clone() {
- Release(fcid) => match self.rt.block_on(self.api.update_release(editgroup_id, fcid, serde_json::from_str(&json_str)?))? {
+ Release(fcid) => match self.rt.block_on(self.api.update_release(
+ editgroup_id,
+ fcid,
+ serde_json::from_str(&json_str)?,
+ ))? {
fatcat_openapi::UpdateReleaseResponse::UpdatedEntity(ee) => Ok(ee),
other => Err(anyhow!("{:?}", other)),
},
- Work(fcid) => match self.rt.block_on(self.api.update_work(editgroup_id, fcid, serde_json::from_str(&json_str)?))? {
+ Work(fcid) => match self.rt.block_on(self.api.update_work(
+ editgroup_id,
+ fcid,
+ serde_json::from_str(&json_str)?,
+ ))? {
fatcat_openapi::UpdateWorkResponse::UpdatedEntity(ee) => Ok(ee),
other => Err(anyhow!("{:?}", other)),
},
- Container(fcid) => match self.rt.block_on(self.api.update_container(editgroup_id, fcid, serde_json::from_str(&json_str)?))? {
+ Container(fcid) => match self.rt.block_on(self.api.update_container(
+ editgroup_id,
+ fcid,
+ serde_json::from_str(&json_str)?,
+ ))? {
fatcat_openapi::UpdateContainerResponse::UpdatedEntity(ee) => Ok(ee),
other => Err(anyhow!("{:?}", other)),
},
- Creator(fcid) => match self.rt.block_on(self.api.update_creator(editgroup_id, fcid, serde_json::from_str(&json_str)?))? {
+ Creator(fcid) => match self.rt.block_on(self.api.update_creator(
+ editgroup_id,
+ fcid,
+ serde_json::from_str(&json_str)?,
+ ))? {
fatcat_openapi::UpdateCreatorResponse::UpdatedEntity(ee) => Ok(ee),
other => Err(anyhow!("{:?}", other)),
},
- File(fcid) => match self.rt.block_on(self.api.update_file(editgroup_id, fcid, serde_json::from_str(&json_str)?))? {
+ File(fcid) => match self.rt.block_on(self.api.update_file(
+ editgroup_id,
+ fcid,
+ serde_json::from_str(&json_str)?,
+ ))? {
fatcat_openapi::UpdateFileResponse::UpdatedEntity(ee) => Ok(ee),
other => Err(anyhow!("{:?}", other)),
},
- FileSet(fcid) => match self.rt.block_on(self.api.update_fileset(editgroup_id, fcid, serde_json::from_str(&json_str)?))? {
+ FileSet(fcid) => match self.rt.block_on(self.api.update_fileset(
+ editgroup_id,
+ fcid,
+ serde_json::from_str(&json_str)?,
+ ))? {
fatcat_openapi::UpdateFilesetResponse::UpdatedEntity(ee) => Ok(ee),
other => Err(anyhow!("{:?}", other)),
},
- WebCapture(fcid) => match self.rt.block_on(self.api.update_webcapture(editgroup_id, fcid, serde_json::from_str(&json_str)?))? {
+ WebCapture(fcid) => match self.rt.block_on(self.api.update_webcapture(
+ editgroup_id,
+ fcid,
+ serde_json::from_str(&json_str)?,
+ ))? {
fatcat_openapi::UpdateWebcaptureResponse::UpdatedEntity(ee) => Ok(ee),
other => Err(anyhow!("{:?}", other)),
},
Editgroup(..) | Editor(..) => unimplemented!("updates for this entity type"),
Changelog(..) => return Err(anyhow!("deleting this entity type doesn't make sense")),
- EditorUsername(..) | ReleaseLookup(..) | ContainerLookup(..) | FileLookup(..) | CreatorLookup(..) =>
- return Err(anyhow!("into_entity_specifier() didn't work?")),
- }.with_context(|| format!("failed to update {:?}", specifier))
+ EditorUsername(..) | ReleaseLookup(..) | ContainerLookup(..) | FileLookup(..)
+ | CreatorLookup(..) => return Err(anyhow!("into_entity_specifier() didn't work?")),
+ }
+ .with_context(|| format!("failed to update {:?}", specifier))
}
}
diff --git a/rust/fatcat-cli/src/entities.rs b/rust/fatcat-cli/src/entities.rs
index c606d17..eee3946 100644
--- a/rust/fatcat-cli/src/entities.rs
+++ b/rust/fatcat-cli/src/entities.rs
@@ -1,11 +1,9 @@
-
-use std::str::FromStr;
-use anyhow::{Result, anyhow};
+use crate::Specifier;
+use anyhow::{anyhow, Result};
+use fatcat_openapi::models;
use lazy_static::lazy_static;
use regex::Regex;
-use fatcat_openapi::models;
-use crate::Specifier;
-
+use std::str::FromStr;
#[derive(Debug, PartialEq, Clone)]
pub struct Mutation {
@@ -22,7 +20,6 @@ impl FromStr for Mutation {
static ref MUTATE_ENTITY_RE: Regex = Regex::new(r"^([a-z_]+)=(.*)$").unwrap();
}
if let Some(captures) = MUTATE_ENTITY_RE.captures(s) {
- // XXX: Some() vs None for value
return Ok(Mutation {
field: captures[1].to_string(),
value: match &captures[2] {
@@ -48,8 +45,7 @@ impl FromStr for Mutation {
* - get by specifier
*/
-pub trait ApiEntityModel: ApiModelSer+ApiModelIdent+ApiModelMutate {
-}
+pub trait ApiEntityModel: ApiModelSer + ApiModelIdent + ApiModelMutate {}
impl ApiEntityModel for models::ReleaseEntity {}
impl ApiEntityModel for models::ContainerEntity {}
@@ -58,9 +54,9 @@ impl ApiEntityModel for models::WorkEntity {}
impl ApiEntityModel for models::FileEntity {}
impl ApiEntityModel for models::FilesetEntity {}
impl ApiEntityModel for models::WebcaptureEntity {}
-impl ApiEntityModel for models::Editor{}
-impl ApiEntityModel for models::Editgroup{}
-impl ApiEntityModel for models::ChangelogEntry{}
+impl ApiEntityModel for models::Editor {}
+impl ApiEntityModel for models::Editgroup {}
+impl ApiEntityModel for models::ChangelogEntry {}
pub trait ApiModelSer {
fn to_json_string(&self) -> Result<String>;
@@ -68,7 +64,6 @@ pub trait ApiModelSer {
}
impl<T: serde::Serialize> ApiModelSer for T {
-
fn to_json_string(&self) -> Result<String> {
Ok(serde_json::to_string(self)?)
}
@@ -85,20 +80,38 @@ pub trait ApiModelIdent {
macro_rules! generic_entity_specifier {
($specifier_type:ident) => {
fn specifier(&self) -> Specifier {
- if let Some(fcid) = &self.ident { Specifier::$specifier_type(fcid.to_string()) } else { panic!("expected full entity") }
+ if let Some(fcid) = &self.ident {
+ Specifier::$specifier_type(fcid.to_string())
+ } else {
+ panic!("expected full entity")
+ }
}
- }
+ };
}
-impl ApiModelIdent for models::ReleaseEntity { generic_entity_specifier!(Release); }
-impl ApiModelIdent for models::ContainerEntity { generic_entity_specifier!(Container); }
-impl ApiModelIdent for models::CreatorEntity { generic_entity_specifier!(Creator); }
-impl ApiModelIdent for models::WorkEntity { generic_entity_specifier!(Work); }
-impl ApiModelIdent for models::FileEntity { generic_entity_specifier!(File); }
-impl ApiModelIdent for models::FilesetEntity { generic_entity_specifier!(FileSet); }
-impl ApiModelIdent for models::WebcaptureEntity { generic_entity_specifier!(WebCapture); }
+impl ApiModelIdent for models::ReleaseEntity {
+ generic_entity_specifier!(Release);
+}
+impl ApiModelIdent for models::ContainerEntity {
+ generic_entity_specifier!(Container);
+}
+impl ApiModelIdent for models::CreatorEntity {
+ generic_entity_specifier!(Creator);
+}
+impl ApiModelIdent for models::WorkEntity {
+ generic_entity_specifier!(Work);
+}
+impl ApiModelIdent for models::FileEntity {
+ generic_entity_specifier!(File);
+}
+impl ApiModelIdent for models::FilesetEntity {
+ generic_entity_specifier!(FileSet);
+}
+impl ApiModelIdent for models::WebcaptureEntity {
+ generic_entity_specifier!(WebCapture);
+}
-impl ApiModelIdent for models::ChangelogEntry{
+impl ApiModelIdent for models::ChangelogEntry {
fn specifier(&self) -> Specifier {
Specifier::Changelog(self.index)
}
@@ -106,13 +119,21 @@ impl ApiModelIdent for models::ChangelogEntry{
impl ApiModelIdent for models::Editgroup {
fn specifier(&self) -> Specifier {
- if let Some(fcid) = &self.editgroup_id { Specifier::Editgroup(fcid.to_string()) } else { panic!("expected full entity") }
+ if let Some(fcid) = &self.editgroup_id {
+ Specifier::Editgroup(fcid.to_string())
+ } else {
+ panic!("expected full entity")
+ }
}
}
impl ApiModelIdent for models::Editor {
fn specifier(&self) -> Specifier {
- if let Some(fcid) = &self.editor_id { Specifier::Editor(fcid.to_string()) } else { panic!("expected full entity") }
+ if let Some(fcid) = &self.editor_id {
+ Specifier::Editor(fcid.to_string())
+ } else {
+ panic!("expected full entity")
+ }
}
}
@@ -124,19 +145,45 @@ impl ApiModelMutate for models::ReleaseEntity {
fn mutate(&mut self, mutations: Vec<Mutation>) -> Result<()> {
for m in mutations {
match (m.field.as_str(), m.value) {
- ("title", val) => { self.title = val; },
- ("subtitle", val) => { self.subtitle = val; },
- ("container_id", val) => { self.container_id = val; },
- ("work_id", val) => { self.work_id = val; },
- ("release_type", val) => { self.release_type = val; },
- ("release_stage", val) => { self.release_stage = val; },
- ("withdrawn_status", val) => { self.withdrawn_status = val; },
- ("license_slug", val) => { self.license_slug= val; },
- ("volume", val) => { self.volume = val; },
- ("issue", val) => { self.issue = val; },
- ("number", val) => { self.number = val; },
- ("publisher", val) => { self.publisher = val; },
- ("language", val) => { self.language = val; },
+ ("title", val) => {
+ self.title = val;
+ }
+ ("subtitle", val) => {
+ self.subtitle = val;
+ }
+ ("container_id", val) => {
+ self.container_id = val;
+ }
+ ("work_id", val) => {
+ self.work_id = val;
+ }
+ ("release_type", val) => {
+ self.release_type = val;
+ }
+ ("release_stage", val) => {
+ self.release_stage = val;
+ }
+ ("withdrawn_status", val) => {
+ self.withdrawn_status = val;
+ }
+ ("license_slug", val) => {
+ self.license_slug = val;
+ }
+ ("volume", val) => {
+ self.volume = val;
+ }
+ ("issue", val) => {
+ self.issue = val;
+ }
+ ("number", val) => {
+ self.number = val;
+ }
+ ("publisher", val) => {
+ self.publisher = val;
+ }
+ ("language", val) => {
+ self.language = val;
+ }
(field, _) => unimplemented!("setting field {} on a release", field),
}
}
@@ -148,10 +195,18 @@ impl ApiModelMutate for models::ContainerEntity {
fn mutate(&mut self, mutations: Vec<Mutation>) -> Result<()> {
for m in mutations {
match (m.field.as_str(), m.value) {
- ("name", val) => { self.name = val; },
- ("container_type", val) => { self.container_type = val; },
- ("publisher", val) => { self.publisher = val; },
- ("issnl", val) => { self.issnl = val; },
+ ("name", val) => {
+ self.name = val;
+ }
+ ("container_type", val) => {
+ self.container_type = val;
+ }
+ ("publisher", val) => {
+ self.publisher = val;
+ }
+ ("issnl", val) => {
+ self.issnl = val;
+ }
(field, _) => unimplemented!("setting field {} on a container", field),
}
}
@@ -163,9 +218,15 @@ impl ApiModelMutate for models::CreatorEntity {
fn mutate(&mut self, mutations: Vec<Mutation>) -> Result<()> {
for m in mutations {
match (m.field.as_str(), m.value) {
- ("display_name", val) => { self.display_name = val; },
- ("given_name", val) => { self.given_name = val; },
- ("surname", val) => { self.surname = val; },
+ ("display_name", val) => {
+ self.display_name = val;
+ }
+ ("given_name", val) => {
+ self.given_name = val;
+ }
+ ("surname", val) => {
+ self.surname = val;
+ }
(field, _) => unimplemented!("setting field {} on a creator", field),
}
}
@@ -183,12 +244,24 @@ impl ApiModelMutate for models::FileEntity {
fn mutate(&mut self, mutations: Vec<Mutation>) -> Result<()> {
for m in mutations {
match (m.field.as_str(), m.value) {
- ("size", Some(val)) => { self.size = Some(i64::from_str(&val)?); },
- ("size", None) => { self.size = None; },
- ("md5", val) => { self.md5 = val; },
- ("sha1", val) => { self.sha1 = val; },
- ("sha256", val) => { self.sha256 = val; },
- ("mimetype", val) => { self.mimetype = val; },
+ ("size", Some(val)) => {
+ self.size = Some(i64::from_str(&val)?);
+ }
+ ("size", None) => {
+ self.size = None;
+ }
+ ("md5", val) => {
+ self.md5 = val;
+ }
+ ("sha1", val) => {
+ self.sha1 = val;
+ }
+ ("sha256", val) => {
+ self.sha256 = val;
+ }
+ ("mimetype", val) => {
+ self.mimetype = val;
+ }
(field, _) => unimplemented!("setting field {} on a file", field),
}
}
@@ -212,7 +285,9 @@ impl ApiModelMutate for models::Editor {
fn mutate(&mut self, mutations: Vec<Mutation>) -> Result<()> {
for m in mutations {
match (m.field.as_str(), m.value) {
- ("username", Some(val)) => { self.username = val; },
+ ("username", Some(val)) => {
+ self.username = val;
+ }
(field, _) => unimplemented!("setting field {} on an editor", field),
}
}
@@ -224,7 +299,9 @@ impl ApiModelMutate for models::Editgroup {
fn mutate(&mut self, mutations: Vec<Mutation>) -> Result<()> {
for m in mutations {
match (m.field.as_str(), m.value) {
- ("description", val) => { self.description = val; },
+ ("description", val) => {
+ self.description = val;
+ }
(field, _) => unimplemented!("setting field {} on an editgroup", field),
}
}
@@ -245,12 +322,26 @@ mod tests {
#[test]
fn test_mutation_from_str() -> () {
assert!(Mutation::from_str("release_asdf").is_err());
- assert_eq!(Mutation::from_str("title=blah").unwrap(),
- Mutation { field: "title".to_string(), value: Some("blah".to_string()) });
- assert_eq!(Mutation::from_str("title=").unwrap(),
- Mutation { field: "title".to_string(), value: None });
- assert_eq!(Mutation::from_str("title=string with spaces and stuff").unwrap(),
- Mutation { field: "title".to_string(), value: Some("string with spaces and stuff".to_string()) });
+ assert_eq!(
+ Mutation::from_str("title=blah").unwrap(),
+ Mutation {
+ field: "title".to_string(),
+ value: Some("blah".to_string())
+ }
+ );
+ assert_eq!(
+ Mutation::from_str("title=").unwrap(),
+ Mutation {
+ field: "title".to_string(),
+ value: None
+ }
+ );
+ assert_eq!(
+ Mutation::from_str("title=string with spaces and stuff").unwrap(),
+ Mutation {
+ field: "title".to_string(),
+ value: Some("string with spaces and stuff".to_string())
+ }
+ );
}
-
}
diff --git a/rust/fatcat-cli/src/lib.rs b/rust/fatcat-cli/src/lib.rs
index 1fffd50..fc9f209 100644
--- a/rust/fatcat-cli/src/lib.rs
+++ b/rust/fatcat-cli/src/lib.rs
@@ -1,28 +1,27 @@
-
-use std::io::Read;
-use std::path::PathBuf;
-use std::io::BufRead;
-use tabwriter::TabWriter;
+use anyhow::{anyhow, Context, Result};
use chrono_humanize::HumanTime;
-use anyhow::{Result, anyhow, Context};
-use std::io::Write;
-use termcolor::{ColorChoice, StandardStream, Color, ColorSpec, WriteColor};
use data_encoding::BASE64;
-use macaroon::{Macaroon, Verifier};
use fatcat_openapi::models;
#[allow(unused_imports)]
-use log::{self,info,debug};
+use log::{self, debug, info};
+use macaroon::{Macaroon, Verifier};
+use std::io::BufRead;
+use std::io::Read;
+use std::io::Write;
+use std::path::PathBuf;
use std::str::FromStr;
+use tabwriter::TabWriter;
+use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
+mod api;
mod entities;
mod search;
mod specifier;
-mod api;
-pub use entities::{ApiEntityModel,ApiModelSer,ApiModelIdent,Mutation};
-pub use specifier::Specifier;
pub use api::FatcatApiClient;
+pub use entities::{ApiEntityModel, ApiModelIdent, ApiModelSer, Mutation};
pub use search::crude_search;
+pub use specifier::Specifier;
// Want to show:
// - whether api_token found
@@ -39,16 +38,12 @@ pub struct ClientStatus {
}
impl ClientStatus {
-
pub fn pretty_print(self) -> Result<()> {
-
- let mut color_stdout = StandardStream::stdout(
- if atty::is(atty::Stream::Stdout) {
- ColorChoice::Auto
- } else {
- ColorChoice::Never
- }
- );
+ let mut color_stdout = StandardStream::stdout(if atty::is(atty::Stream::Stdout) {
+ ColorChoice::Auto
+ } else {
+ ColorChoice::Never
+ });
let color_normal = ColorSpec::new();
let mut color_bold = ColorSpec::new();
color_bold.set_bold(true);
@@ -69,7 +64,7 @@ impl ClientStatus {
write!(&mut color_stdout, "{:>16}: ", "Last changelog")?;
color_stdout.set_color(&color_bold)?;
writeln!(&mut color_stdout, "{}", index)?;
- },
+ }
None => {
color_stdout.set_color(&color_sad)?;
writeln!(&mut color_stdout, " [Failed to connect]")?;
@@ -90,25 +85,32 @@ impl ClientStatus {
color_stdout.set_color(&color_bold)?;
write!(&mut color_stdout, "{}", editor.username)?;
if editor.is_bot == Some(true) {
- color_stdout.set_color(ColorSpec::new().set_fg(Some(Color::Blue)).set_bold(true))?;
+ color_stdout
+ .set_color(ColorSpec::new().set_fg(Some(Color::Blue)).set_bold(true))?;
write!(&mut color_stdout, " [bot]")?;
}
if editor.is_admin == Some(true) {
- color_stdout.set_color(ColorSpec::new().set_fg(Some(Color::Magenta)).set_bold(true))?;
+ color_stdout
+ .set_color(ColorSpec::new().set_fg(Some(Color::Magenta)).set_bold(true))?;
write!(&mut color_stdout, " [admin]")?;
}
match editor.is_active {
Some(true) => {
color_stdout.set_color(&color_happy)?;
writeln!(&mut color_stdout, " [active]")?;
- },
+ }
Some(false) | None => {
color_stdout.set_color(&color_sad)?;
writeln!(&mut color_stdout, " [disabled]")?;
- },
+ }
};
color_stdout.set_color(&color_normal)?;
- writeln!(&mut color_stdout, "{:>16} editor_{}", "", editor.editor_id.unwrap())?;
+ writeln!(
+ &mut color_stdout,
+ "{:>16} editor_{}",
+ "",
+ editor.editor_id.unwrap()
+ )?;
};
color_stdout.set_color(&color_normal)?;
Ok(())
@@ -145,14 +147,25 @@ impl FromStr for EntityType {
/// Takes a macaroon token (as base64-encoded string) and tries to parse out an editor id
pub fn parse_macaroon_editor_id(s: &str) -> Result<String> {
- let raw = BASE64.decode(s.as_bytes()).context("macaroon parsing failed")?;
- let mac = Macaroon::deserialize(&raw).map_err(|err| anyhow!("macaroon deserialization failed: {:?}", err))?;
- let mac = mac.validate().map_err(|err| anyhow!("macaroon validation failed: {:?}", err))?;
+ let raw = BASE64
+ .decode(s.as_bytes())
+ .context("macaroon parsing failed")?;
+ let mac = Macaroon::deserialize(&raw)
+ .map_err(|err| anyhow!("macaroon deserialization failed: {:?}", err))?;
+ let mac = mac
+ .validate()
+ .map_err(|err| anyhow!("macaroon validation failed: {:?}", err))?;
let mut verifier = Verifier::new();
let mut editor_id: Option<String> = None;
for caveat in mac.first_party_caveats() {
if caveat.predicate().starts_with("editor_id = ") {
- editor_id = Some(caveat.predicate().get(12..).context("parsing macaroon")?.to_string());
+ editor_id = Some(
+ caveat
+ .predicate()
+ .get(12..)
+ .context("parsing macaroon")?
+ .to_string(),
+ );
break;
}
}
@@ -171,14 +184,23 @@ pub fn print_editgroups(eg_list: Vec<models::Editgroup>, json: bool) -> Result<(
}
} else {
let mut tw = TabWriter::new(std::io::stdout());
- writeln!(tw, "editgroup_id\tchangelog_index\tcreated\tsubmitted\tdescription")?;
+ writeln!(
+ tw,
+ "editgroup_id\tchangelog_index\tcreated\tsubmitted\tdescription"
+ )?;
for eg in eg_list {
- writeln!(tw, "{}\t{}\t{}\t{}\t{}",
+ writeln!(
+ tw,
+ "{}\t{}\t{}\t{}\t{}",
eg.editgroup_id.unwrap(),
- eg.changelog_index.map_or("-".to_string(), |v| v.to_string()),
- eg.created.map_or("-".to_string(), |v| HumanTime::from(v).to_string()),
- eg.submitted.map_or("-".to_string(), |v| HumanTime::from(v).to_string()),
- eg.description.unwrap_or_else(|| "-".to_string()))?;
+ eg.changelog_index
+ .map_or("-".to_string(), |v| v.to_string()),
+ eg.created
+ .map_or("-".to_string(), |v| HumanTime::from(v).to_string()),
+ eg.submitted
+ .map_or("-".to_string(), |v| HumanTime::from(v).to_string()),
+ eg.description.unwrap_or_else(|| "-".to_string())
+ )?;
}
tw.flush()?;
}
@@ -196,22 +218,23 @@ pub fn read_entity_file(input_path: Option<PathBuf>) -> Result<String> {
let mut line = String::new();
std::io::stdin().read_line(&mut line)?;
Ok(line)
- },
+ }
Some(path) if path.extension().map(|v| v.to_str()) == Some(Some("toml")) => {
info!("reading {:?} as TOML", path);
// as a hack, read TOML but then serialize it back to JSON
let mut contents = String::new();
- let mut input_file = std::fs::File::open(path).context("reading entity from TOML file")?;
+ let mut input_file =
+ std::fs::File::open(path).context("reading entity from TOML file")?;
input_file.read_to_string(&mut contents)?;
let value: toml::Value = contents.parse().context("parsing TOML file")?;
Ok(serde_json::to_string(&value)?)
- },
+ }
Some(path) => {
let mut line = String::new();
let input_file = std::fs::File::open(path)?;
let mut buffered = std::io::BufReader::new(input_file);
buffered.read_line(&mut line)?;
Ok(line)
- },
+ }
}
}
diff --git a/rust/fatcat-cli/src/main.rs b/rust/fatcat-cli/src/main.rs
index 05b003d..ce12f9d 100644
--- a/rust/fatcat-cli/src/main.rs
+++ b/rust/fatcat-cli/src/main.rs
@@ -1,27 +1,36 @@
-
-use std::path::PathBuf;
+use anyhow::{anyhow, Context, Result};
use fatcat_cli::ApiModelSer;
-use std::io::Write;
-use termcolor::{ColorChoice, StandardStream, Color, ColorSpec, WriteColor};
-use anyhow::{Result, Context, anyhow};
-#[allow(unused_imports)]
-use log::{self,info,debug};
-use structopt::StructOpt;
use fatcat_cli::*;
use fatcat_openapi::{client, models, ApiNoContext};
-
+#[allow(unused_imports)]
+use log::{self, debug, info};
+use std::io::Write;
+use std::path::PathBuf;
+use structopt::StructOpt;
+use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
#[derive(StructOpt)]
-#[structopt(rename_all = "kebab-case", about = "CLI interface to Fatcat API" )]
+#[structopt(rename_all = "kebab-case", about = "CLI interface to Fatcat API")]
struct Opt {
-
- #[structopt(long = "--api-host", env = "FATCAT_API_HOST", default_value = "https://api.fatcat.wiki")]
+ #[structopt(
+ long = "--api-host",
+ env = "FATCAT_API_HOST",
+ default_value = "https://api.fatcat.wiki"
+ )]
api_host: String,
- #[structopt(long = "--api-token", env = "FATCAT_API_AUTH_TOKEN", hide_env_values = true)]
+ #[structopt(
+ long = "--api-token",
+ env = "FATCAT_API_AUTH_TOKEN",
+ hide_env_values = true
+ )]
api_token: Option<String>,
- #[structopt(long = "--search-host", env = "FATCAT_SEARCH_HOST", default_value = "https://search.fatcat.wiki")]
+ #[structopt(
+ long = "--search-host",
+ env = "FATCAT_SEARCH_HOST",
+ default_value = "https://search.fatcat.wiki"
+ )]
search_host: String,
/// Pass many times for more log output
@@ -97,7 +106,12 @@ enum Command {
#[structopt(long = "--file", short = "-f", parse(from_os_str))]
input_path: Option<PathBuf>,
- #[structopt(long = "--editgroup-id", short, env = "FATCAT_EDITGROUP", hide_env_values = true)]
+ #[structopt(
+ long = "--editgroup-id",
+ short,
+ env = "FATCAT_EDITGROUP",
+ hide_env_values = true
+ )]
editgroup_id: String,
},
Update {
@@ -107,7 +121,12 @@ enum Command {
#[structopt(long = "--file", short = "-f", parse(from_os_str))]
input_path: Option<PathBuf>,
- #[structopt(long = "--editgroup-id", short, env = "FATCAT_EDITGROUP", hide_env_values = true)]
+ #[structopt(
+ long = "--editgroup-id",
+ short,
+ env = "FATCAT_EDITGROUP",
+ hide_env_values = true
+ )]
editgroup_id: String,
mutations: Vec<Mutation>,
@@ -115,7 +134,12 @@ enum Command {
Edit {
specifier: Specifier,
- #[structopt(long = "--editgroup-id", short, env = "FATCAT_EDITGROUP", hide_env_values = true)]
+ #[structopt(
+ long = "--editgroup-id",
+ short,
+ env = "FATCAT_EDITGROUP",
+ hide_env_values = true
+ )]
editgroup_id: String,
#[structopt(long)]
@@ -127,7 +151,12 @@ enum Command {
Delete {
specifier: Specifier,
- #[structopt(long = "--editgroup-id", short, env = "FATCAT_EDITGROUP", hide_env_values = true)]
+ #[structopt(
+ long = "--editgroup-id",
+ short,
+ env = "FATCAT_EDITGROUP",
+ hide_env_values = true
+ )]
editgroup_id: String,
},
Editgroup {
@@ -138,7 +167,6 @@ enum Command {
//Download
//History
Search {
-
entity_type: EntityType,
terms: Vec<String>,
@@ -186,13 +214,11 @@ fn main() -> Result<()> {
std::process::exit(0);
}
}
- let mut color_stderr = StandardStream::stderr(
- if atty::is(atty::Stream::Stderr) {
- ColorChoice::Auto
- } else {
- ColorChoice::Never
- }
- );
+ let mut color_stderr = StandardStream::stderr(if atty::is(atty::Stream::Stderr) {
+ ColorChoice::Auto
+ } else {
+ ColorChoice::Never
+ });
color_stderr.set_color(ColorSpec::new().set_fg(Some(Color::Red)).set_bold(true))?;
eprintln!("Error: {:?}", err);
color_stderr.set_color(&ColorSpec::new())?;
@@ -212,46 +238,69 @@ fn run(opt: Opt) -> Result<()> {
return Err(anyhow!("unsupported API Host prefix: {}", opt.api_host));
};
- let mut api_client = FatcatApiClient::new(&client, opt.api_host.clone(), opt.api_token.clone())?;
+ let mut api_client =
+ FatcatApiClient::new(&client, opt.api_host.clone(), opt.api_token.clone())?;
match opt.cmd {
- Command::Get {toml, specifier, expand, hide } => {
+ Command::Get {
+ toml,
+ specifier,
+ expand,
+ hide,
+ } => {
let result = specifier.get_from_api(&mut api_client, expand, hide)?;
if toml {
writeln!(&mut std::io::stdout(), "{}", result.to_toml_string()?)?
} else {
writeln!(&mut std::io::stdout(), "{}", result.to_json_string()?)?
}
- },
- Command::Create { entity_type, input_path, editgroup_id } => {
+ }
+ Command::Create {
+ entity_type,
+ input_path,
+ editgroup_id,
+ } => {
let json_str = read_entity_file(input_path)?;
let ee = api_client.create_entity_from_json(entity_type, &json_str, editgroup_id)?;
println!("{}", serde_json::to_string(&ee)?);
- },
- Command::Update { specifier, input_path, editgroup_id, mutations } => {
- let (json_str, exact_specifier): (String, Specifier) = match (&input_path, mutations.len()) {
- // input path or no mutations: read from path or stdin
- (Some(_), _) | (None, 0) => {
- (read_entity_file(input_path)?, specifier.into_entity_specifier(&mut api_client)?)
- },
- // no input path *and* mutations: fetch from API
- (None, _) => {
- let mut entity = specifier.get_from_api(&mut api_client, None, None)?;
- entity.mutate(mutations)?;
- (entity.to_json_string()?, entity.specifier())
- },
- };
- let ee = api_client.update_entity_from_json(exact_specifier, &json_str, editgroup_id)?;
+ }
+ Command::Update {
+ specifier,
+ input_path,
+ editgroup_id,
+ mutations,
+ } => {
+ let (json_str, exact_specifier): (String, Specifier) =
+ match (&input_path, mutations.len()) {
+ // input path or no mutations: read from path or stdin
+ (Some(_), _) | (None, 0) => (
+ read_entity_file(input_path)?,
+ specifier.into_entity_specifier(&mut api_client)?,
+ ),
+ // no input path *and* mutations: fetch from API
+ (None, _) => {
+ let mut entity = specifier.get_from_api(&mut api_client, None, None)?;
+ entity.mutate(mutations)?;
+ (entity.to_json_string()?, entity.specifier())
+ }
+ };
+ let ee =
+ api_client.update_entity_from_json(exact_specifier, &json_str, editgroup_id)?;
println!("{}", serde_json::to_string(&ee)?);
- },
- Command::Edit { specifier, editgroup_id, json, editing_command } => {
+ }
+ Command::Edit {
+ specifier,
+ editgroup_id,
+ json,
+ editing_command,
+ } => {
// TODO: fetch editgroup, check if this entity is already being updated in it. If so,
// need to fetch that revision, do the edit, parse that synatx is good, then delete the
// existing edit and update with the new one.
let original_entity = specifier.get_from_api(&mut api_client, None, None)?;
let exact_specifier = original_entity.specifier();
let tmp_file = tempfile::Builder::new()
- .suffix( if json { ".json" } else { ".toml"} )
+ .suffix(if json { ".json" } else { ".toml" })
.tempfile()?;
if json {
writeln!(&tmp_file, "{}", original_entity.to_json_string()?)?
@@ -264,16 +313,35 @@ fn run(opt: Opt) -> Result<()> {
.expect("failed to execute process");
let cmd_status = editor_cmd.wait()?;
if !cmd_status.success() {
- return Err(anyhow!("editor ({}) exited with non-success status code ({}), bailing on edit", editing_command, cmd_status.code().map(|v| v.to_string()).unwrap_or_else(|| "N/A".to_string())));
+ return Err(anyhow!(
+ "editor ({}) exited with non-success status code ({}), bailing on edit",
+ editing_command,
+ cmd_status
+ .code()
+ .map(|v| v.to_string())
+ .unwrap_or_else(|| "N/A".to_string())
+ ));
};
let json_str = read_entity_file(Some(tmp_file.path().to_path_buf()))?;
// for whatever reason api_client's TCP connection is broken after spawning, so try a
// dummy call, expected to fail, but connection should re-establish after this
- specifier.get_from_api(&mut api_client, None, None).context("re-fetch").ok();
- let ee = api_client.update_entity_from_json(exact_specifier, &json_str, editgroup_id).context("updating after edit")?;
+ specifier
+ .get_from_api(&mut api_client, None, None)
+ .context("re-fetch")
+ .ok();
+ let ee = api_client
+ .update_entity_from_json(exact_specifier, &json_str, editgroup_id)
+ .context("updating after edit")?;
println!("{}", serde_json::to_string(&ee)?);
- },
- Command::Search { entity_type, terms, limit, search_schema, expand, hide } => {
+ }
+ Command::Search {
+ entity_type,
+ terms,
+ limit,
+ search_schema,
+ expand,
+ hide,
+ } => {
let limit: Option<u64> = match limit {
l if l < 0 => None,
l => Some(l as u64),
@@ -286,79 +354,137 @@ fn run(opt: Opt) -> Result<()> {
match (search_schema, entity_type) {
(true, _) => writeln!(&mut std::io::stdout(), "{}", hit.to_string())?,
(false, EntityType::Release) => {
- let specifier = Specifier::Release(hit["ident"].as_str().unwrap().to_string());
- let entity = specifier.get_from_api(&mut api_client, expand.clone(), hide.clone())?;
+ let specifier =
+ Specifier::Release(hit["ident"].as_str().unwrap().to_string());
+ let entity = specifier.get_from_api(
+ &mut api_client,
+ expand.clone(),
+ hide.clone(),
+ )?;
writeln!(&mut std::io::stdout(), "{}", entity.to_json_string()?)?
- },
+ }
(false, _) => unimplemented!("searching other entity types"),
}
}
- },
- Command::Delete { specifier, editgroup_id } => {
- let result = api_client.delete_entity(specifier.clone(), editgroup_id)
+ }
+ Command::Delete {
+ specifier,
+ editgroup_id,
+ } => {
+ let result = api_client
+ .delete_entity(specifier.clone(), editgroup_id)
.with_context(|| format!("delete entity: {:?}", specifier))?;
println!("{}", serde_json::to_string(&result)?);
- },
- Command::Editgroup { cmd: EditgroupCommand::List { editor_id, limit, json } } => {
+ }
+ Command::Editgroup {
+ cmd:
+ EditgroupCommand::List {
+ editor_id,
+ limit,
+ json,
+ },
+ } => {
let editor_id = match editor_id.or(api_client.editor_id) {
Some(eid) => eid,
None => return Err(anyhow!("require either working auth token or --editor-id")),
};
- let result = api_client.rt.block_on(
- api_client.api.get_editor_editgroups(editor_id.clone(), Some(limit), None, None)
- ).context("fetch editgroups")?;
+ let result = api_client
+ .rt
+ .block_on(api_client.api.get_editor_editgroups(
+ editor_id.clone(),
+ Some(limit),
+ None,
+ None,
+ ))
+ .context("fetch editgroups")?;
match result {
fatcat_openapi::GetEditorEditgroupsResponse::Found(eg_list) => {
print_editgroups(eg_list, json)?;
- },
- other => return Err(anyhow!("{:?}", other)).with_context(|| format!("failed to fetch editgroups for editor_{}", editor_id)),
+ }
+ other => {
+ return Err(anyhow!("{:?}", other)).with_context(|| {
+ format!("failed to fetch editgroups for editor_{}", editor_id)
+ })
+ }
}
- },
- Command::Editgroup { cmd: EditgroupCommand::Reviewable { limit, json } } => {
- let result = api_client.rt.block_on(
- api_client.api.get_editgroups_reviewable(Some("editors".to_string()), Some(limit), None, None)
- ).context("fetch reviewable editgroups")?;
+ }
+ Command::Editgroup {
+ cmd: EditgroupCommand::Reviewable { limit, json },
+ } => {
+ let result = api_client
+ .rt
+ .block_on(api_client.api.get_editgroups_reviewable(
+ Some("editors".to_string()),
+ Some(limit),
+ None,
+ None,
+ ))
+ .context("fetch reviewable editgroups")?;
match result {
fatcat_openapi::GetEditgroupsReviewableResponse::Found(eg_list) => {
print_editgroups(eg_list, json)?;
- },
- other => return Err(anyhow!("{:?}", other)).context("failed to fetch reviewable editgroups"),
+ }
+ other => {
+ return Err(anyhow!("{:?}", other))
+ .context("failed to fetch reviewable editgroups")
+ }
}
- },
- Command::Editgroup { cmd: EditgroupCommand::Create { description }} => {
+ }
+ Command::Editgroup {
+ cmd: EditgroupCommand::Create { description },
+ } => {
let mut eg = models::Editgroup::new();
eg.description = Some(description);
eg.extra = Some({
let mut extra = std::collections::HashMap::new();
- extra.insert("agent".to_string(), serde_json::Value::String("fatcat-cli".to_string()));
+ extra.insert(
+ "agent".to_string(),
+ serde_json::Value::String("fatcat-cli".to_string()),
+ );
extra
});
- let result = api_client.rt.block_on(
- api_client.api.create_editgroup(eg))?;
+ let result = api_client
+ .rt
+ .block_on(api_client.api.create_editgroup(eg))?;
match result {
- fatcat_openapi::CreateEditgroupResponse::SuccessfullyCreated(eg) =>
- println!("{}", serde_json::to_string(&eg)?),
+ fatcat_openapi::CreateEditgroupResponse::SuccessfullyCreated(eg) => {
+ println!("{}", serde_json::to_string(&eg)?)
+ }
other => return Err(anyhow!("{:?}", other)).context("failed to create editgroup"),
}
- },
- Command::Editgroup { cmd: EditgroupCommand::Accept { editgroup_id } } => {
- let result = api_client.rt.block_on(
- api_client.api.accept_editgroup(editgroup_id.clone())
- ).context("accept editgroup")?;
+ }
+ Command::Editgroup {
+ cmd: EditgroupCommand::Accept { editgroup_id },
+ } => {
+ let result = api_client
+ .rt
+ .block_on(api_client.api.accept_editgroup(editgroup_id.clone()))
+ .context("accept editgroup")?;
match result {
- fatcat_openapi::AcceptEditgroupResponse::MergedSuccessfully(msg) =>
- println!("{}", serde_json::to_string(&msg)?),
- other => return Err(anyhow!("failed to accept editgroup {}: {:?}", editgroup_id, other)),
+ fatcat_openapi::AcceptEditgroupResponse::MergedSuccessfully(msg) => {
+ println!("{}", serde_json::to_string(&msg)?)
+ }
+ other => {
+ return Err(anyhow!(
+ "failed to accept editgroup {}: {:?}",
+ editgroup_id,
+ other
+ ))
+ }
}
- },
- Command::Editgroup { cmd: EditgroupCommand::Submit{ editgroup_id } } => {
+ }
+ Command::Editgroup {
+ cmd: EditgroupCommand::Submit { editgroup_id },
+ } => {
let eg = api_client.update_editgroup_submit(editgroup_id, true)?;
println!("{}", eg.to_json_string()?);
- },
- Command::Editgroup { cmd: EditgroupCommand::Unsubmit { editgroup_id } } => {
+ }
+ Command::Editgroup {
+ cmd: EditgroupCommand::Unsubmit { editgroup_id },
+ } => {
let eg = api_client.update_editgroup_submit(editgroup_id, false)?;
println!("{}", eg.to_json_string()?);
- },
+ }
Command::Status { json } => {
let status = api_client.status()?;
if json {
@@ -366,7 +492,7 @@ fn run(opt: Opt) -> Result<()> {
} else {
status.pretty_print()?;
}
- },
+ }
}
Ok(())
}
diff --git a/rust/fatcat-cli/src/search.rs b/rust/fatcat-cli/src/search.rs
index d7fbbad..133ea41 100644
--- a/rust/fatcat-cli/src/search.rs
+++ b/rust/fatcat-cli/src/search.rs
@@ -1,10 +1,8 @@
-
+use crate::EntityType;
+use anyhow::{anyhow, Result};
+use log::{self, info};
use serde_json::json;
use std::time::Duration;
-use anyhow::{Result, anyhow};
-use log::{self,info};
-use crate::EntityType;
-
pub struct SearchResults {
pub entity_type: EntityType,
@@ -25,17 +23,22 @@ impl Iterator for SearchResults {
// if we already hit limit, bail early
if let Some(l) = self.limit {
if self.offset >= l {
- return None
+ return None;
}
}
// if current batch is empty, and we are scrolling, refill the current batch
if self.batch.is_empty() && self.scroll_id.is_some() {
- let response = self.http_client.get(&self.scroll_url)
+ let response = self
+ .http_client
+ .get(&self.scroll_url)
.header("Content-Type", "application/json")
- .body(json!({
- "scroll": "2m",
- "scroll_id": self.scroll_id.clone().unwrap(),
- }).to_string())
+ .body(
+ json!({
+ "scroll": "2m",
+ "scroll_id": self.scroll_id.clone().unwrap(),
+ })
+ .to_string(),
+ )
.send();
let mut response = match response {
Err(e) => return Some(Err(e.into())),
@@ -66,13 +69,22 @@ impl Iterator for SearchResults {
}
}
-pub fn crude_search(api_host: &str, entity_type: EntityType, limit: Option<u64>, terms: Vec<String>) -> Result<SearchResults> {
-
+pub fn crude_search(
+ api_host: &str,
+ entity_type: EntityType,
+ limit: Option<u64>,
+ terms: Vec<String>,
+) -> Result<SearchResults> {
let index = match entity_type {
EntityType::Release => "fatcat_release",
EntityType::File => "fatcat_file",
EntityType::Container => "fatcat_container",
- _ => return Err(anyhow!("No search index for entity type: {:?}", entity_type)),
+ _ => {
+ return Err(anyhow!(
+ "No search index for entity type: {:?}",
+ entity_type
+ ))
+ }
};
let http_client = reqwest::Client::builder()
.timeout(Duration::from_secs(10))
@@ -94,50 +106,51 @@ pub fn crude_search(api_host: &str, entity_type: EntityType, limit: Option<u64>,
None => (true, "_doc", 100),
Some(l) if l > 100 => (true, "_doc", 100),
Some(l) => (false, "_score", l),
-
};
let query_body = json!({
- "query": {
- "boosting": {
- "positive": {
- "bool": {
- "must": {
- "query_string": {
- "query": query,
- "default_operator": "AND",
- "analyze_wildcard": true,
- "allow_leading_wildcard": false,
- "lenient": true,
- "fields": [
- "title^2",
- "biblio",
- ],
- },
- },
- "should": {
- "term": { "in_ia": true },
+ "query": {
+ "boosting": {
+ "positive": {
+ "bool": {
+ "must": {
+ "query_string": {
+ "query": query,
+ "default_operator": "AND",
+ "analyze_wildcard": true,
+ "allow_leading_wildcard": false,
+ "lenient": true,
+ "fields": [
+ "title^2",
+ "biblio",
+ ],
},
},
- },
- "negative": {
- "bool": {
- "should": [
- {"bool": { "must_not" : { "exists": { "field": "title" }}}},
- {"bool": { "must_not" : { "exists": { "field": "year" }}}},
- {"bool": { "must_not" : { "exists": { "field": "type" }}}},
- {"bool": { "must_not" : { "exists": { "field": "stage" }}}},
- ],
+ "should": {
+ "term": { "in_ia": true },
},
},
- "negative_boost": 0.5,
},
+ "negative": {
+ "bool": {
+ "should": [
+ {"bool": { "must_not" : { "exists": { "field": "title" }}}},
+ {"bool": { "must_not" : { "exists": { "field": "year" }}}},
+ {"bool": { "must_not" : { "exists": { "field": "type" }}}},
+ {"bool": { "must_not" : { "exists": { "field": "stage" }}}},
+ ],
+ },
+ },
+ "negative_boost": 0.5,
},
- "size": size,
- "sort": [ sort_mode ],
- }).to_string();
+ },
+ "size": size,
+ "sort": [ sort_mode ],
+ })
+ .to_string();
- let mut request = http_client.get(&request_url)
+ let mut request = http_client
+ .get(&request_url)
.header("Content-Type", "application/json")
.body(query_body);
@@ -154,9 +167,9 @@ pub fn crude_search(api_host: &str, entity_type: EntityType, limit: Option<u64>,
let body: serde_json::Value = response.json()?;
let scroll_id = if scroll_mode {
- None
- } else {
Some(body["_scroll_id"].as_str().unwrap().to_string())
+ } else {
+ None
};
Ok(SearchResults {
diff --git a/rust/fatcat-cli/src/specifier.rs b/rust/fatcat-cli/src/specifier.rs
index 3085345..c9bc581 100644
--- a/rust/fatcat-cli/src/specifier.rs
+++ b/rust/fatcat-cli/src/specifier.rs
@@ -1,11 +1,9 @@
-
+use crate::{ApiEntityModel, FatcatApiClient};
+use anyhow::{anyhow, Context, Result};
use fatcat_openapi::ApiNoContext;
-use anyhow::{Result, anyhow, Context};
-use std::str::FromStr;
use lazy_static::lazy_static;
use regex::Regex;
-use crate::{ApiEntityModel, FatcatApiClient};
-
+use std::str::FromStr;
#[derive(Debug, PartialEq, Clone)]
pub enum ReleaseLookupKey {
@@ -53,156 +51,305 @@ pub enum Specifier {
}
impl Specifier {
-
/// If this Specifier is a lookup, call the API to do the lookup and return the resulting
/// specific entity specifier (eg, with an FCID). If already specific, just pass through.
pub fn into_entity_specifier(self, api_client: &mut FatcatApiClient) -> Result<Specifier> {
use Specifier::*;
match self {
- Release(_) | Work(_) | Creator(_) | Container(_) | File(_) | FileSet(_) | WebCapture(_) | Editgroup(_) | Editor(_) | Changelog(_) => Ok(self),
+ Release(_) | Work(_) | Creator(_) | Container(_) | File(_) | FileSet(_)
+ | WebCapture(_) | Editgroup(_) | Editor(_) | Changelog(_) => Ok(self),
ReleaseLookup(_, _) => Ok(self.get_from_api(api_client, None, None)?.specifier()),
ContainerLookup(_, _) => Ok(self.get_from_api(api_client, None, None)?.specifier()),
CreatorLookup(_, _) => Ok(self.get_from_api(api_client, None, None)?.specifier()),
FileLookup(_, _) => Ok(self.get_from_api(api_client, None, None)?.specifier()),
- EditorUsername(_username) => {
- Err(anyhow!("editor lookup by username isn't implemented in fatcat-server API yet, sorry"))
- },
+ EditorUsername(_username) => Err(anyhow!(
+ "editor lookup by username isn't implemented in fatcat-server API yet, sorry"
+ )),
}
}
- pub fn get_from_api(&self, api_client: &mut FatcatApiClient, expand: Option<String>, hide: Option<String>) -> Result<Box<dyn ApiEntityModel>> {
+ pub fn get_from_api(
+ &self,
+ api_client: &mut FatcatApiClient,
+ expand: Option<String>,
+ hide: Option<String>,
+ ) -> Result<Box<dyn ApiEntityModel>> {
use Specifier::*;
let ret: Result<Box<dyn ApiEntityModel>> = match self {
- Release(fcid) =>
- match api_client.rt.block_on(api_client.api.get_release(fcid.to_string(), expand, hide))? {
- fatcat_openapi::GetReleaseResponse::FoundEntity(model) => Ok(Box::new(model)),
- fatcat_openapi::GetReleaseResponse::BadRequest(err) => Err(anyhow!("Bad Request ({}): {}", err.error, err.message)),
- fatcat_openapi::GetReleaseResponse::NotFound(err) => Err(anyhow!("Not Found: {}", err.message)),
- resp => Err(anyhow!("{:?}", resp)).with_context(|| format!("API GET failed: {:?}", self)),
- },
+ Release(fcid) => match api_client.rt.block_on(api_client.api.get_release(
+ fcid.to_string(),
+ expand,
+ hide,
+ ))? {
+ fatcat_openapi::GetReleaseResponse::FoundEntity(model) => Ok(Box::new(model)),
+ fatcat_openapi::GetReleaseResponse::BadRequest(err) => {
+ Err(anyhow!("Bad Request ({}): {}", err.error, err.message))
+ }
+ fatcat_openapi::GetReleaseResponse::NotFound(err) => {
+ Err(anyhow!("Not Found: {}", err.message))
+ }
+ resp => Err(anyhow!("{:?}", resp))
+ .with_context(|| format!("API GET failed: {:?}", self)),
+ },
ReleaseLookup(ext_id, key) => {
use ReleaseLookupKey::*;
let (doi, pmcid, pmid, arxiv) = (
- if let DOI = ext_id { Some(key.to_string()) } else { None },
- if let PMCID = ext_id { Some(key.to_string()) } else { None },
- if let PMID = ext_id { Some(key.to_string()) } else { None },
- if let Arxiv = ext_id { Some(key.to_string()) } else { None },
+ if let DOI = ext_id {
+ Some(key.to_string())
+ } else {
+ None
+ },
+ if let PMCID = ext_id {
+ Some(key.to_string())
+ } else {
+ None
+ },
+ if let PMID = ext_id {
+ Some(key.to_string())
+ } else {
+ None
+ },
+ if let Arxiv = ext_id {
+ Some(key.to_string())
+ } else {
+ None
+ },
);
// doi, wikidata, isbn13, pmid, pmcid, core, arxiv, jstor, ark, mag
- let result = api_client.rt.block_on(
- api_client.api.lookup_release(doi, None, None, pmid, pmcid, None, arxiv, None, None, None, expand, hide))?;
+ let result = api_client.rt.block_on(api_client.api.lookup_release(
+ doi, None, None, pmid, pmcid, None, arxiv, None, None, None, expand, hide,
+ ))?;
match result {
- fatcat_openapi::LookupReleaseResponse::FoundEntity(model) => Ok(Box::new(model)),
- fatcat_openapi::LookupReleaseResponse::BadRequest(err) => Err(anyhow!("Bad Request ({}): {}", err.error, err.message)),
- fatcat_openapi::LookupReleaseResponse::NotFound(err) => Err(anyhow!("Not Found: {}", err.message)),
- resp => Err(anyhow!("{:?}", resp)).with_context(|| format!("API GET failed: {:?}", self)),
+ fatcat_openapi::LookupReleaseResponse::FoundEntity(model) => {
+ Ok(Box::new(model))
+ }
+ fatcat_openapi::LookupReleaseResponse::BadRequest(err) => {
+ Err(anyhow!("Bad Request ({}): {}", err.error, err.message))
+ }
+ fatcat_openapi::LookupReleaseResponse::NotFound(err) => {
+ Err(anyhow!("Not Found: {}", err.message))
+ }
+ resp => Err(anyhow!("{:?}", resp))
+ .with_context(|| format!("API GET failed: {:?}", self)),
+ }
+ }
+ Work(fcid) => match api_client.rt.block_on(api_client.api.get_work(
+ fcid.to_string(),
+ expand,
+ hide,
+ ))? {
+ fatcat_openapi::GetWorkResponse::FoundEntity(model) => Ok(Box::new(model)),
+ fatcat_openapi::GetWorkResponse::BadRequest(err) => {
+ Err(anyhow!("Bad Request ({}): {}", err.error, err.message))
}
+ fatcat_openapi::GetWorkResponse::NotFound(err) => {
+ Err(anyhow!("Not Found: {}", err.message))
+ }
+ resp => Err(anyhow!("{:?}", resp))
+ .with_context(|| format!("API GET failed: {:?}", self)),
+ },
+ Container(fcid) => match api_client.rt.block_on(api_client.api.get_container(
+ fcid.to_string(),
+ expand,
+ hide,
+ ))? {
+ fatcat_openapi::GetContainerResponse::FoundEntity(model) => Ok(Box::new(model)),
+ fatcat_openapi::GetContainerResponse::BadRequest(err) => {
+ Err(anyhow!("Bad Request ({}): {}", err.error, err.message))
+ }
+ fatcat_openapi::GetContainerResponse::NotFound(err) => {
+ Err(anyhow!("Not Found: {}", err.message))
+ }
+ resp => Err(anyhow!("{:?}", resp))
+ .with_context(|| format!("API GET failed: {:?}", self)),
},
- Work(fcid) =>
- match api_client.rt.block_on(api_client.api.get_work(fcid.to_string(), expand, hide))? {
- fatcat_openapi::GetWorkResponse::FoundEntity(model) => Ok(Box::new(model)),
- fatcat_openapi::GetWorkResponse::BadRequest(err) => Err(anyhow!("Bad Request ({}): {}", err.error, err.message)),
- fatcat_openapi::GetWorkResponse::NotFound(err) => Err(anyhow!("Not Found: {}", err.message)),
- resp => Err(anyhow!("{:?}", resp)).with_context(|| format!("API GET failed: {:?}", self)),
- },
- Container(fcid) =>
- match api_client.rt.block_on(api_client.api.get_container(fcid.to_string(), expand, hide))? {
- fatcat_openapi::GetContainerResponse::FoundEntity(model) => Ok(Box::new(model)),
- fatcat_openapi::GetContainerResponse::BadRequest(err) => Err(anyhow!("Bad Request ({}): {}", err.error, err.message)),
- fatcat_openapi::GetContainerResponse::NotFound(err) => Err(anyhow!("Not Found: {}", err.message)),
- resp => Err(anyhow!("{:?}", resp)).with_context(|| format!("API GET failed: {:?}", self)),
- },
ContainerLookup(ext_id, key) => {
let result = api_client.rt.block_on(match ext_id {
- ContainerLookupKey::ISSNL => api_client.api.lookup_container(Some(key.to_string()), None, expand, hide),
+ ContainerLookupKey::ISSNL => {
+ api_client
+ .api
+ .lookup_container(Some(key.to_string()), None, expand, hide)
+ }
})?;
match result {
- fatcat_openapi::LookupContainerResponse::FoundEntity(model) => Ok(Box::new(model)),
- fatcat_openapi::LookupContainerResponse::BadRequest(err) => Err(anyhow!("Bad Request ({}): {}", err.error, err.message)),
- fatcat_openapi::LookupContainerResponse::NotFound(err) => Err(anyhow!("Not Found: {}", err.message)),
- resp => Err(anyhow!("{:?}", resp)).with_context(|| format!("API GET failed: {:?}", self)),
+ fatcat_openapi::LookupContainerResponse::FoundEntity(model) => {
+ Ok(Box::new(model))
+ }
+ fatcat_openapi::LookupContainerResponse::BadRequest(err) => {
+ Err(anyhow!("Bad Request ({}): {}", err.error, err.message))
+ }
+ fatcat_openapi::LookupContainerResponse::NotFound(err) => {
+ Err(anyhow!("Not Found: {}", err.message))
+ }
+ resp => Err(anyhow!("{:?}", resp))
+ .with_context(|| format!("API GET failed: {:?}", self)),
+ }
+ }
+ Creator(fcid) => match api_client.rt.block_on(api_client.api.get_creator(
+ fcid.to_string(),
+ expand,
+ hide,
+ ))? {
+ fatcat_openapi::GetCreatorResponse::FoundEntity(model) => Ok(Box::new(model)),
+ fatcat_openapi::GetCreatorResponse::BadRequest(err) => {
+ Err(anyhow!("Bad Request ({}): {}", err.error, err.message))
+ }
+ fatcat_openapi::GetCreatorResponse::NotFound(err) => {
+ Err(anyhow!("Not Found: {}", err.message))
}
+ resp => Err(anyhow!("{:?}", resp))
+ .with_context(|| format!("API GET failed: {:?}", self)),
},
- Creator(fcid) =>
- match api_client.rt.block_on(api_client.api.get_creator(fcid.to_string(), expand, hide))? {
- fatcat_openapi::GetCreatorResponse::FoundEntity(model) => Ok(Box::new(model)),
- fatcat_openapi::GetCreatorResponse::BadRequest(err) => Err(anyhow!("Bad Request ({}): {}", err.error, err.message)),
- fatcat_openapi::GetCreatorResponse::NotFound(err) => Err(anyhow!("Not Found: {}", err.message)),
- resp => Err(anyhow!("{:?}", resp)).with_context(|| format!("API GET failed: {:?}", self)),
- },
CreatorLookup(ext_id, key) => {
let result = api_client.rt.block_on(match ext_id {
- CreatorLookupKey::Orcid => api_client.api.lookup_creator(Some(key.to_string()), None, expand, hide),
+ CreatorLookupKey::Orcid => {
+ api_client
+ .api
+ .lookup_creator(Some(key.to_string()), None, expand, hide)
+ }
})?;
match result {
- fatcat_openapi::LookupCreatorResponse::FoundEntity(model) => Ok(Box::new(model)),
- fatcat_openapi::LookupCreatorResponse::BadRequest(err) => Err(anyhow!("Bad Request ({}): {}", err.error, err.message)),
- fatcat_openapi::LookupCreatorResponse::NotFound(err) => Err(anyhow!("Not Found: {}", err.message)),
- resp => Err(anyhow!("{:?}", resp)).with_context(|| format!("API GET failed: {:?}", self)),
+ fatcat_openapi::LookupCreatorResponse::FoundEntity(model) => {
+ Ok(Box::new(model))
+ }
+ fatcat_openapi::LookupCreatorResponse::BadRequest(err) => {
+ Err(anyhow!("Bad Request ({}): {}", err.error, err.message))
+ }
+ fatcat_openapi::LookupCreatorResponse::NotFound(err) => {
+ Err(anyhow!("Not Found: {}", err.message))
+ }
+ resp => Err(anyhow!("{:?}", resp))
+ .with_context(|| format!("API GET failed: {:?}", self)),
}
+ }
+ File(fcid) => match api_client.rt.block_on(api_client.api.get_file(
+ fcid.to_string(),
+ expand,
+ hide,
+ ))? {
+ fatcat_openapi::GetFileResponse::FoundEntity(model) => Ok(Box::new(model)),
+ fatcat_openapi::GetFileResponse::BadRequest(err) => {
+ Err(anyhow!("Bad Request ({}): {}", err.error, err.message))
+ }
+ fatcat_openapi::GetFileResponse::NotFound(err) => {
+ Err(anyhow!("Not Found: {}", err.message))
+ }
+ resp => Err(anyhow!("{:?}", resp))
+ .with_context(|| format!("API GET failed: {:?}", self)),
},
- File(fcid) =>
- match api_client.rt.block_on(api_client.api.get_file(fcid.to_string(), expand, hide))? {
- fatcat_openapi::GetFileResponse::FoundEntity(model) => Ok(Box::new(model)),
- fatcat_openapi::GetFileResponse::BadRequest(err) => Err(anyhow!("Bad Request ({}): {}", err.error, err.message)),
- fatcat_openapi::GetFileResponse::NotFound(err) => Err(anyhow!("Not Found: {}", err.message)),
- resp => Err(anyhow!("{:?}", resp)).with_context(|| format!("API GET failed: {:?}", self)),
- },
FileLookup(hash, key) => {
use FileLookupKey::*;
let (sha1, sha256, md5) = (
- if let SHA1 = hash { Some(key.to_string()) } else { None },
- if let SHA256 = hash { Some(key.to_string()) } else { None },
- if let MD5 = hash { Some(key.to_string()) } else { None },
+ if let SHA1 = hash {
+ Some(key.to_string())
+ } else {
+ None
+ },
+ if let SHA256 = hash {
+ Some(key.to_string())
+ } else {
+ None
+ },
+ if let MD5 = hash {
+ Some(key.to_string())
+ } else {
+ None
+ },
);
- let result = api_client.rt.block_on(
- api_client.api.lookup_file(sha1, sha256, md5, expand, hide),
- )?;
+ let result = api_client
+ .rt
+ .block_on(api_client.api.lookup_file(sha1, sha256, md5, expand, hide))?;
match result {
fatcat_openapi::LookupFileResponse::FoundEntity(model) => Ok(Box::new(model)),
- fatcat_openapi::LookupFileResponse::BadRequest(err) => Err(anyhow!("Bad Request ({}): {}", err.error, err.message)),
- fatcat_openapi::LookupFileResponse::NotFound(err) => Err(anyhow!("Not Found: {}", err.message)),
- resp => Err(anyhow!("{:?}", resp)).with_context(|| format!("API GET failed: {:?}", self)),
+ fatcat_openapi::LookupFileResponse::BadRequest(err) => {
+ Err(anyhow!("Bad Request ({}): {}", err.error, err.message))
+ }
+ fatcat_openapi::LookupFileResponse::NotFound(err) => {
+ Err(anyhow!("Not Found: {}", err.message))
+ }
+ resp => Err(anyhow!("{:?}", resp))
+ .with_context(|| format!("API GET failed: {:?}", self)),
+ }
+ }
+ FileSet(fcid) => match api_client.rt.block_on(api_client.api.get_fileset(
+ fcid.to_string(),
+ expand,
+ hide,
+ ))? {
+ fatcat_openapi::GetFilesetResponse::FoundEntity(model) => Ok(Box::new(model)),
+ fatcat_openapi::GetFilesetResponse::BadRequest(err) => {
+ Err(anyhow!("Bad Request ({}): {}", err.error, err.message))
}
+ fatcat_openapi::GetFilesetResponse::NotFound(err) => {
+ Err(anyhow!("Not Found: {}", err.message))
+ }
+ resp => Err(anyhow!("{:?}", resp))
+ .with_context(|| format!("API GET failed: {:?}", self)),
},
- FileSet(fcid) =>
- match api_client.rt.block_on(api_client.api.get_fileset(fcid.to_string(), expand, hide))? {
- fatcat_openapi::GetFilesetResponse::FoundEntity(model) => Ok(Box::new(model)),
- fatcat_openapi::GetFilesetResponse::BadRequest(err) => Err(anyhow!("Bad Request ({}): {}", err.error, err.message)),
- fatcat_openapi::GetFilesetResponse::NotFound(err) => Err(anyhow!("Not Found: {}", err.message)),
- resp => Err(anyhow!("{:?}", resp)).with_context(|| format!("API GET failed: {:?}", self)),
- },
- WebCapture(fcid) =>
- match api_client.rt.block_on(api_client.api.get_webcapture(fcid.to_string(), expand, hide))? {
- fatcat_openapi::GetWebcaptureResponse::FoundEntity(model) => Ok(Box::new(model)),
- fatcat_openapi::GetWebcaptureResponse::BadRequest(err) => Err(anyhow!("Bad Request ({}): {}", err.error, err.message)),
- fatcat_openapi::GetWebcaptureResponse::NotFound(err) => Err(anyhow!("Not Found: {}", err.message)),
- resp => Err(anyhow!("{:?}", resp)).with_context(|| format!("API GET failed: {:?}", self)),
- },
- Editgroup(fcid) =>
- match api_client.rt.block_on(api_client.api.get_editgroup(fcid.to_string()))? {
- fatcat_openapi::GetEditgroupResponse::Found(model) => Ok(Box::new(model)),
- fatcat_openapi::GetEditgroupResponse::BadRequest(err) => Err(anyhow!("Bad Request ({}): {}", err.error, err.message)),
- fatcat_openapi::GetEditgroupResponse::NotFound(err) => Err(anyhow!("Not Found: {}", err.message)),
- resp => Err(anyhow!("{:?}", resp)).with_context(|| format!("API GET failed: {:?}", self)),
- },
- Editor(fcid) =>
- match api_client.rt.block_on(api_client.api.get_editor(fcid.to_string()))? {
- fatcat_openapi::GetEditorResponse::Found(model) => Ok(Box::new(model)),
- fatcat_openapi::GetEditorResponse::BadRequest(err) => Err(anyhow!("Bad Request ({}): {}", err.error, err.message)),
- fatcat_openapi::GetEditorResponse::NotFound(err) => Err(anyhow!("Not Found: {}", err.message)),
- resp => Err(anyhow!("{:?}", resp)).with_context(|| format!("API GET failed: {:?}", self)),
- },
- Changelog(index) =>
- match api_client.rt.block_on(api_client.api.get_changelog_entry(*index))? {
- fatcat_openapi::GetChangelogEntryResponse::FoundChangelogEntry(model) => Ok(Box::new(model)),
- fatcat_openapi::GetChangelogEntryResponse::BadRequest(err) => Err(anyhow!("Bad Request ({}): {}", err.error, err.message)),
- fatcat_openapi::GetChangelogEntryResponse::NotFound(err) => Err(anyhow!("Not Found: {}", err.message)),
- resp => Err(anyhow!("{:?}", resp)).with_context(|| format!("API GET failed: {:?}", self)),
- },
- EditorUsername(_username) => {
- unimplemented!("editor lookup by username isn't implemented in fatcat-server API yet, sorry")
+ WebCapture(fcid) => match api_client.rt.block_on(api_client.api.get_webcapture(
+ fcid.to_string(),
+ expand,
+ hide,
+ ))? {
+ fatcat_openapi::GetWebcaptureResponse::FoundEntity(model) => Ok(Box::new(model)),
+ fatcat_openapi::GetWebcaptureResponse::BadRequest(err) => {
+ Err(anyhow!("Bad Request ({}): {}", err.error, err.message))
+ }
+ fatcat_openapi::GetWebcaptureResponse::NotFound(err) => {
+ Err(anyhow!("Not Found: {}", err.message))
+ }
+ resp => Err(anyhow!("{:?}", resp))
+ .with_context(|| format!("API GET failed: {:?}", self)),
+ },
+ Editgroup(fcid) => match api_client
+ .rt
+ .block_on(api_client.api.get_editgroup(fcid.to_string()))?
+ {
+ fatcat_openapi::GetEditgroupResponse::Found(model) => Ok(Box::new(model)),
+ fatcat_openapi::GetEditgroupResponse::BadRequest(err) => {
+ Err(anyhow!("Bad Request ({}): {}", err.error, err.message))
+ }
+ fatcat_openapi::GetEditgroupResponse::NotFound(err) => {
+ Err(anyhow!("Not Found: {}", err.message))
+ }
+ resp => Err(anyhow!("{:?}", resp))
+ .with_context(|| format!("API GET failed: {:?}", self)),
},
+ Editor(fcid) => match api_client
+ .rt
+ .block_on(api_client.api.get_editor(fcid.to_string()))?
+ {
+ fatcat_openapi::GetEditorResponse::Found(model) => Ok(Box::new(model)),
+ fatcat_openapi::GetEditorResponse::BadRequest(err) => {
+ Err(anyhow!("Bad Request ({}): {}", err.error, err.message))
+ }
+ fatcat_openapi::GetEditorResponse::NotFound(err) => {
+ Err(anyhow!("Not Found: {}", err.message))
+ }
+ resp => Err(anyhow!("{:?}", resp))
+ .with_context(|| format!("API GET failed: {:?}", self)),
+ },
+ Changelog(index) => match api_client
+ .rt
+ .block_on(api_client.api.get_changelog_entry(*index))?
+ {
+ fatcat_openapi::GetChangelogEntryResponse::FoundChangelogEntry(model) => {
+ Ok(Box::new(model))
+ }
+ fatcat_openapi::GetChangelogEntryResponse::BadRequest(err) => {
+ Err(anyhow!("Bad Request ({}): {}", err.error, err.message))
+ }
+ fatcat_openapi::GetChangelogEntryResponse::NotFound(err) => {
+ Err(anyhow!("Not Found: {}", err.message))
+ }
+ resp => Err(anyhow!("{:?}", resp))
+ .with_context(|| format!("API GET failed: {:?}", self)),
+ },
+ EditorUsername(_username) => {
+ unimplemented!(
+ "editor lookup by username isn't implemented in fatcat-server API yet, sorry"
+ )
+ }
};
match ret {
Ok(_) => ret,
@@ -236,18 +383,42 @@ 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|username|changelog):(\S+)$").unwrap();
+ static ref SPEC_LOOKUP_RE: Regex = Regex::new(
+ r"^(doi|pmcid|pmid|arxiv|issnl|orcid|sha1|sha256|md5|username|changelog):(\S+)$"
+ )
+ .unwrap();
}
if let Some(caps) = SPEC_LOOKUP_RE.captures(s) {
return match (&caps[1], &caps[2]) {
- ("doi", key) => Ok(Specifier::ReleaseLookup(ReleaseLookupKey::DOI, key.to_string())),
- ("pmcid", key) => Ok(Specifier::ReleaseLookup(ReleaseLookupKey::PMCID, key.to_string())),
- ("pmid", key) => Ok(Specifier::ReleaseLookup(ReleaseLookupKey::PMID, key.to_string())),
- ("arxiv", key) => Ok(Specifier::ReleaseLookup(ReleaseLookupKey::Arxiv, key.to_string())),
- ("issnl", key) => Ok(Specifier::ContainerLookup(ContainerLookupKey::ISSNL, key.to_string())),
- ("orcid", key) => Ok(Specifier::CreatorLookup(CreatorLookupKey::Orcid, key.to_string())),
+ ("doi", key) => Ok(Specifier::ReleaseLookup(
+ ReleaseLookupKey::DOI,
+ key.to_string(),
+ )),
+ ("pmcid", key) => Ok(Specifier::ReleaseLookup(
+ ReleaseLookupKey::PMCID,
+ key.to_string(),
+ )),
+ ("pmid", key) => Ok(Specifier::ReleaseLookup(
+ ReleaseLookupKey::PMID,
+ key.to_string(),
+ )),
+ ("arxiv", key) => Ok(Specifier::ReleaseLookup(
+ ReleaseLookupKey::Arxiv,
+ key.to_string(),
+ )),
+ ("issnl", key) => Ok(Specifier::ContainerLookup(
+ ContainerLookupKey::ISSNL,
+ key.to_string(),
+ )),
+ ("orcid", key) => Ok(Specifier::CreatorLookup(
+ CreatorLookupKey::Orcid,
+ key.to_string(),
+ )),
("sha1", key) => Ok(Specifier::FileLookup(FileLookupKey::SHA1, key.to_string())),
- ("sha256", key) => Ok(Specifier::FileLookup(FileLookupKey::SHA256, key.to_string())),
+ ("sha256", key) => Ok(Specifier::FileLookup(
+ FileLookupKey::SHA256,
+ key.to_string(),
+ )),
("md5", key) => Ok(Specifier::FileLookup(FileLookupKey::MD5, key.to_string())),
("username", key) => Ok(Specifier::EditorUsername(key.to_string())),
_ => Err(anyhow!("unexpected entity lookup type: {}", &caps[1])),
@@ -260,7 +431,10 @@ impl FromStr for Specifier {
if let Some(caps) = SPEC_CHANGELOG_RE.captures(s) {
return Ok(Specifier::Changelog(caps[1].parse::<i64>()?));
}
- Err(anyhow!("expecting a specifier: entity identifier or key/value lookup: {}", s))
+ Err(anyhow!(
+ "expecting a specifier: entity identifier or key/value lookup: {}",
+ s
+ ))
}
}
@@ -271,12 +445,23 @@ mod tests {
#[test]
fn test_specifier_from_str() -> () {
assert!(Specifier::from_str("release_asdf").is_err());
- assert_eq!(Specifier::from_str("creator_iimvc523xbhqlav6j3sbthuehu").unwrap(), Specifier::Creator("iimvc523xbhqlav6j3sbthuehu".to_string()));
- assert_eq!(Specifier::from_str("username:big-bot").unwrap(), Specifier::EditorUsername("big-bot".to_string()));
- assert_eq!(Specifier::from_str("doi:10.1234/a!s.df+-d").unwrap(), Specifier::ReleaseLookup(ReleaseLookupKey::DOI, "10.1234/a!s.df+-d".to_string()));
+ assert_eq!(
+ Specifier::from_str("creator_iimvc523xbhqlav6j3sbthuehu").unwrap(),
+ Specifier::Creator("iimvc523xbhqlav6j3sbthuehu".to_string())
+ );
+ assert_eq!(
+ Specifier::from_str("username:big-bot").unwrap(),
+ Specifier::EditorUsername("big-bot".to_string())
+ );
+ assert_eq!(
+ Specifier::from_str("doi:10.1234/a!s.df+-d").unwrap(),
+ Specifier::ReleaseLookup(ReleaseLookupKey::DOI, "10.1234/a!s.df+-d".to_string())
+ );
assert!(Specifier::from_str("doi:").is_err());
- assert_eq!(Specifier::from_str("changelog_1234").unwrap(), Specifier::Changelog(1234));
+ assert_eq!(
+ Specifier::from_str("changelog_1234").unwrap(),
+ Specifier::Changelog(1234)
+ );
assert!(Specifier::from_str("changelog_12E4").is_err());
}
-
}