From 6b06a9cf6089d69f6f8b9043221cace2bb715a05 Mon Sep 17 00:00:00 2001 From: trivernis Date: Thu, 31 Mar 2022 21:06:40 +0200 Subject: [PATCH] Add service selection to undelete, archive and unarchive Signed-off-by: trivernis --- src/api_core/client.rs | 58 ++++++++-- src/api_core/common.rs | 115 +++++++++++++++++++ src/api_core/endpoints/adding_files.rs | 67 +++++------ src/wrapper/builders/delete_files_builder.rs | 25 ++-- src/wrapper/hydrus_file.rs | 9 +- tests/client/test_adding_files.rs | 40 +++++-- 6 files changed, 241 insertions(+), 73 deletions(-) diff --git a/src/api_core/client.rs b/src/api_core/client.rs index c3b05de..9b3d789 100644 --- a/src/api_core/client.rs +++ b/src/api_core/client.rs @@ -1,4 +1,7 @@ -use crate::api_core::common::{FileIdentifier, FileMetadataInfo, FileRecord, OptionalStringNumber}; +use crate::api_core::common::{ + FileIdentifier, FileMetadataInfo, FileRecord, FileSelection, FileServiceSelection, + OptionalStringNumber, +}; use crate::api_core::endpoints::access_management::{ ApiVersion, ApiVersionResponse, GetServices, GetServicesResponse, SessionKey, SessionKeyResponse, VerifyAccessKey, VerifyAccessKeyResponse, @@ -110,35 +113,66 @@ impl Client { /// Moves files with matching hashes to the trash #[tracing::instrument(skip(self), level = "debug")] - pub async fn delete_files(&self, request: DeleteFilesRequest) -> Result<()> { - self.post::(request).await?; + pub async fn delete_files( + &self, + files: FileSelection, + service: FileServiceSelection, + reason: Option, + ) -> Result<()> { + self.post::(DeleteFilesRequest { + file_selection: files, + service_selection: service, + reason, + }) + .await?; Ok(()) } /// Pulls files out of the trash by hash #[tracing::instrument(skip(self), level = "debug")] - pub async fn undelete_files(&self, hashes: Vec) -> Result<()> { - self.post::(UndeleteFilesRequest { hashes }) - .await?; + pub async fn undelete_files( + &self, + files: FileSelection, + service: FileServiceSelection, + ) -> Result<()> { + self.post::(UndeleteFilesRequest { + file_selection: files, + service_selection: service, + }) + .await?; Ok(()) } /// Moves files from the inbox into the archive #[tracing::instrument(skip(self), level = "debug")] - pub async fn archive_files(&self, hashes: Vec) -> Result<()> { - self.post::(ArchiveFilesRequest { hashes }) - .await?; + pub async fn archive_files( + &self, + files: FileSelection, + service: FileServiceSelection, + ) -> Result<()> { + self.post::(ArchiveFilesRequest { + file_selection: files, + service_selection: service, + }) + .await?; Ok(()) } /// Moves files from the archive into the inbox #[tracing::instrument(skip(self), level = "debug")] - pub async fn unarchive_files(&self, hashes: Vec) -> Result<()> { - self.post::(UnarchiveFilesRequest { hashes }) - .await?; + pub async fn unarchive_files( + &self, + files: FileSelection, + service: FileServiceSelection, + ) -> Result<()> { + self.post::(UnarchiveFilesRequest { + file_selection: files, + service_selection: service, + }) + .await?; Ok(()) } diff --git a/src/api_core/common.rs b/src/api_core/common.rs index 0d8937d..4ce5b9f 100644 --- a/src/api_core/common.rs +++ b/src/api_core/common.rs @@ -1,3 +1,5 @@ +use crate::wrapper::service::ServiceName; +use serde::Serialize; use std::collections::HashMap; #[derive(Debug, Clone, Serialize, Deserialize)] @@ -98,6 +100,119 @@ impl FileIdentifier { } } +/// A generic selection for one or multiple files +#[derive(Clone, Debug, Serialize, Default)] +pub struct FileSelection { + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) hash: Option, + #[serde(skip_serializing_if = "Vec::is_empty")] + pub(crate) hashes: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) file_id: Option, + #[serde(skip_serializing_if = "Vec::is_empty")] + pub(crate) file_ids: Vec, +} + +impl From for FileSelection { + fn from(id: FileIdentifier) -> Self { + let mut selection = Self::default(); + match id { + FileIdentifier::ID(id) => selection.file_id = Some(id), + FileIdentifier::Hash(hash) => selection.hash = Some(hash), + } + selection + } +} + +impl FileSelection { + /// Creates a new single hash file selection + pub fn by_hash(hash: S) -> Self { + Self { + hash: Some(hash.to_string()), + ..Default::default() + } + } + + /// Creates a new file selection with a single file id + pub fn by_file_id(file_id: u64) -> Self { + Self { + file_id: Some(file_id), + ..Default::default() + } + } + + /// Creates a new file selection with several hashes + pub fn by_hashes(mut hashes: Vec) -> Self { + if hashes.len() == 1 { + Self::by_hash(hashes.pop().unwrap()) + } else { + Self { + hashes, + ..Default::default() + } + } + } + + /// Creates a new file selection with several IDs + pub fn by_file_ids(mut file_ids: Vec) -> Self { + if file_ids.len() == 1 { + Self::by_file_id(file_ids.pop().unwrap()) + } else { + Self { + file_ids, + ..Default::default() + } + } + } +} + +/// A selection for a single file service +#[derive(Clone, Debug, Serialize, Default)] +pub struct FileServiceSelection { + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) file_service_name: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) file_service_key: Option, +} + +impl FileServiceSelection { + /// Creates a new file service selection by name + pub fn by_name(name: S) -> Self { + Self { + file_service_name: Some(name.to_string()), + ..Default::default() + } + } + + /// Creates a new file service selection by service key + pub fn by_key(key: S) -> Self { + Self { + file_service_key: Some(key.to_string()), + ..Default::default() + } + } + + /// Selects no service + pub fn none() -> Self { + Self::default() + } +} + +impl From for FileServiceSelection { + fn from(id: ServiceIdentifier) -> Self { + match id { + ServiceIdentifier::Name(n) => Self::by_name(n), + ServiceIdentifier::Key(k) => Self::by_key(k), + } + } +} + +impl From for FileServiceSelection { + fn from(name: ServiceName) -> Self { + Self::by_name(name) + } +} + #[derive(Clone)] pub struct FileRecord { pub bytes: Vec, diff --git a/src/api_core/endpoints/adding_files.rs b/src/api_core/endpoints/adding_files.rs index e9028e0..135f49e 100644 --- a/src/api_core/endpoints/adding_files.rs +++ b/src/api_core/endpoints/adding_files.rs @@ -1,4 +1,4 @@ -use crate::api_core::common::{BasicHashList, ServiceIdentifier}; +use crate::api_core::common::{FileSelection, FileServiceSelection}; use crate::api_core::endpoints::Endpoint; use serde::Serialize; @@ -33,41 +33,13 @@ impl Endpoint for AddFile { #[derive(Clone, Debug, Serialize)] pub struct DeleteFilesRequest { - /// The files by hashes to delete - pub hashes: Vec, - /// The files by file ids to delete - pub file_ids: Vec, - pub file_service_name: Option, - pub file_service_key: Option, + #[serde(flatten)] + pub file_selection: FileSelection, + #[serde(flatten)] + pub service_selection: FileServiceSelection, pub reason: Option, } -impl DeleteFilesRequest { - pub fn new(hashes: Vec, file_ids: Vec) -> Self { - Self { - hashes, - file_ids, - file_service_key: None, - file_service_name: None, - reason: None, - } - } - - /// Sets the service to delete from. If none is given it deletes - /// from all files. - pub fn set_service(&mut self, service: ServiceIdentifier) { - match service { - ServiceIdentifier::Name(name) => self.file_service_name = Some(name), - ServiceIdentifier::Key(key) => self.file_service_key = Some(key), - } - } - - /// Sets the reason for deletion - pub fn set_reason(&mut self, reason: S) { - self.reason = Some(reason.to_string()); - } -} - pub struct DeleteFiles; impl Endpoint for DeleteFiles { @@ -79,7 +51,14 @@ impl Endpoint for DeleteFiles { } } -pub type UndeleteFilesRequest = BasicHashList; +#[derive(Clone, Debug, Serialize)] +pub struct UndeleteFilesRequest { + #[serde(flatten)] + pub file_selection: FileSelection, + #[serde(flatten)] + pub service_selection: FileServiceSelection, +} + pub struct UndeleteFiles; impl Endpoint for UndeleteFiles { @@ -91,7 +70,14 @@ impl Endpoint for UndeleteFiles { } } -pub type ArchiveFilesRequest = BasicHashList; +#[derive(Clone, Debug, Serialize)] +pub struct ArchiveFilesRequest { + #[serde(flatten)] + pub file_selection: FileSelection, + #[serde(flatten)] + pub service_selection: FileServiceSelection, +} + pub struct ArchiveFiles; impl Endpoint for ArchiveFiles { @@ -103,11 +89,18 @@ impl Endpoint for ArchiveFiles { } } -pub type UnarchiveFilesRequest = BasicHashList; +#[derive(Clone, Debug, Serialize)] +pub struct UnarchiveFilesRequest { + #[serde(flatten)] + pub file_selection: FileSelection, + #[serde(flatten)] + pub service_selection: FileServiceSelection, +} + pub struct UnarchiveFiles; impl Endpoint for UnarchiveFiles { - type Request = UndeleteFilesRequest; + type Request = UnarchiveFilesRequest; type Response = (); fn path() -> String { diff --git a/src/wrapper/builders/delete_files_builder.rs b/src/wrapper/builders/delete_files_builder.rs index 2f57ec3..8333379 100644 --- a/src/wrapper/builders/delete_files_builder.rs +++ b/src/wrapper/builders/delete_files_builder.rs @@ -1,5 +1,6 @@ -use crate::api_core::common::{FileIdentifier, ServiceIdentifier}; -use crate::api_core::endpoints::adding_files::DeleteFilesRequest; +use crate::api_core::common::{ + FileIdentifier, FileSelection, FileServiceSelection, ServiceIdentifier, +}; use crate::error::Result; use crate::Client; @@ -53,20 +54,18 @@ impl DeleteFilesBuilder { /// Deletes all files specified in this builder pub async fn run(self) -> Result<()> { - let mut request = DeleteFilesRequest { - reason: self.reason, + let file_selection = FileSelection { hashes: self.hashes, file_ids: self.ids, - file_service_key: None, - file_service_name: None, + ..Default::default() }; - if let Some(service) = self.service { - match service { - ServiceIdentifier::Name(name) => request.file_service_name = Some(name), - ServiceIdentifier::Key(key) => request.file_service_key = Some(key), - } - } + let service_selection = self + .service + .map(FileServiceSelection::from) + .unwrap_or_default(); - self.client.delete_files(request).await + self.client + .delete_files(file_selection, service_selection, self.reason) + .await } } diff --git a/src/wrapper/hydrus_file.rs b/src/wrapper/hydrus_file.rs index 3e2d60b..0688ca8 100644 --- a/src/wrapper/hydrus_file.rs +++ b/src/wrapper/hydrus_file.rs @@ -1,4 +1,7 @@ -use crate::api_core::common::{FileIdentifier, FileMetadataInfo, FileRecord, ServiceIdentifier}; +use crate::api_core::common::{ + FileIdentifier, FileMetadataInfo, FileRecord, FileSelection, FileServiceSelection, + ServiceIdentifier, +}; use crate::api_core::endpoints::adding_tags::{AddTagsRequestBuilder, TagAction}; use crate::error::{Error, Result}; use crate::utils::tag_list_to_string_list; @@ -240,7 +243,9 @@ impl HydrusFile { pub async fn undelete(&mut self) -> Result<()> { let hash = self.hash().await?; self.metadata = None; - self.client.undelete_files(vec![hash]).await + self.client + .undelete_files(FileSelection::by_hash(hash), FileServiceSelection::none()) + .await } /// Associates the file with a list of urls diff --git a/tests/client/test_adding_files.rs b/tests/client/test_adding_files.rs index dc47a42..8cafa81 100644 --- a/tests/client/test_adding_files.rs +++ b/tests/client/test_adding_files.rs @@ -1,7 +1,7 @@ use crate::common; use crate::common::create_testdata; -use crate::common::test_data::get_test_hashes; -use hydrus_api::api_core::endpoints::adding_files::DeleteFilesRequest; +use crate::common::test_data::{get_test_hashes, TEST_HASH_1}; +use hydrus_api::api_core::common::FileSelection; use hydrus_api::wrapper::service::ServiceName; #[tokio::test] @@ -25,29 +25,51 @@ async fn it_adds_binary_files() { async fn it_deletes_files() { let client = common::get_client(); create_testdata(&client).await; - let mut delete_request = DeleteFilesRequest::new(get_test_hashes(), vec![]); - delete_request.set_reason("Testing"); - delete_request.set_service(ServiceName::my_files().into()); - client.delete_files(delete_request).await.unwrap(); + client + .delete_files( + FileSelection::by_hashes(get_test_hashes()), + ServiceName::my_files().into(), + Some("Test".to_string()), + ) + .await + .unwrap(); } #[tokio::test] async fn it_undeletes_files() { let client = common::get_client(); create_testdata(&client).await; - client.undelete_files(get_test_hashes()).await.unwrap(); + client + .undelete_files( + FileSelection::by_hashes(get_test_hashes()), + ServiceName::my_files().into(), + ) + .await + .unwrap(); } #[tokio::test] async fn it_archives_files() { let client = common::get_client(); create_testdata(&client).await; - client.archive_files(get_test_hashes()).await.unwrap(); + client + .archive_files( + FileSelection::by_hashes(vec![TEST_HASH_1.to_string()]), + ServiceName::my_files().into(), + ) + .await + .unwrap(); } #[tokio::test] async fn it_unarchives_files() { let client = common::get_client(); create_testdata(&client).await; - client.unarchive_files(get_test_hashes()).await.unwrap(); + client + .unarchive_files( + FileSelection::by_hashes(get_test_hashes()), + ServiceName::my_files().into(), + ) + .await + .unwrap(); }