summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock8
-rw-r--r--src/client.rs135
-rw-r--r--src/tests.rs8
-rw-r--r--src/types.rs125
4 files changed, 238 insertions, 38 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 34d785e..2b37f1e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -31,9 +31,9 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
[[package]]
name = "cc"
-version = "1.2.36"
+version = "1.2.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5252b3d2648e5eedbc1a6f501e3c795e07025c1e93bbf8bbdd6eef7f447a6d54"
+checksum = "65193589c6404eb80b450d618eaf9a2cafaaafd57ecce47370519ef674a7bd44"
dependencies = [
"find-msvc-tools",
"shlex",
@@ -341,9 +341,9 @@ dependencies = [
[[package]]
name = "rustls-webpki"
-version = "0.103.4"
+version = "0.103.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc"
+checksum = "b5a37813727b78798e53c2bec3f5e8fe12a6d6f8389bf9ca7802add4c9905ad8"
dependencies = [
"ring",
"rustls-pki-types",
diff --git a/src/client.rs b/src/client.rs
index 49d903e..293ef2f 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -1,3 +1,7 @@
+use std::fs::File;
+
+use ureq::RequestBuilder;
+
use crate::types::*;
type Result<T> = std::result::Result<T, HydrusError>;
@@ -80,27 +84,128 @@ impl HydrusClient {
Ok(data)
}
+ fn set_get_api_key(
+ &self,
+ request: RequestBuilder<ureq::typestate::WithoutBody>,
+ ) -> Result<RequestBuilder<ureq::typestate::WithoutBody>> {
+ if let Some(key) = &self.sessionkey {
+ Ok(request.header("Hydrus-Client-API-Access-Key", key))
+ } else if let Some(key) = &self.apikey {
+ Ok(request.header("Hydrus-Client-API-Access-Key", key))
+ } else {
+ Err(HydrusError::KeyNotSupplied)
+ }
+ }
+
+ fn set_post_api_key(
+ &self,
+ request: RequestBuilder<ureq::typestate::WithBody>,
+ ) -> Result<RequestBuilder<ureq::typestate::WithBody>> {
+ if let Some(key) = &self.sessionkey {
+ Ok(request.header("Hydrus-Client-API-Access-Key", key))
+ } else if let Some(key) = &self.apikey {
+ Ok(request.header("Hydrus-Client-API-Access-Key", key))
+ } else {
+ Err(HydrusError::KeyNotSupplied)
+ }
+ }
+
pub fn get_service_name(&self, name: &str) -> Result<Service> {
let mut req_url = self.url.to_owned();
req_url.push_str("get_service?service_name=");
req_url.push_str(&urlencoding::encode(name));
- let response = if let Some(key) = &self.sessionkey {
- ureq::get(req_url)
- .header("Hydrus-Client-API-Access-Key", key)
- .call()?
- .body_mut()
- .read_to_vec()?
- } else if let Some(key) = &self.apikey {
- ureq::get(req_url)
- .header("Hydrus-Client-API-Access-Key", key)
- .call()?
- .body_mut()
- .read_to_vec()?
- } else {
- return Err(HydrusError::KeyNotSupplied);
- };
+
+ let response = self
+ .set_get_api_key(ureq::get(req_url))?
+ .call()?
+ .body_mut()
+ .read_to_vec()?;
+
+ let service: Service = musli::json::decode(response.as_slice())?;
+ Ok(service)
+ }
+
+ pub fn get_service_key(&self, key: &str) -> Result<Service> {
+ let mut req_url = self.url.to_owned();
+ req_url.push_str("get_service?service_key=");
+ req_url.push_str(&urlencoding::encode(key));
+
+ let response = self
+ .set_get_api_key(ureq::get(req_url))?
+ .call()?
+ .body_mut()
+ .read_to_vec()?;
let service: Service = musli::json::decode(response.as_slice())?;
Ok(service)
}
+
+ pub fn get_services(&self) -> Result<Vec<Service>> {
+ let mut req_url = self.url.to_owned();
+ req_url.push_str("get_services");
+
+ let response = self
+ .set_get_api_key(ureq::get(req_url))?
+ .call()?
+ .body_mut()
+ .read_to_vec()?;
+
+ let services: ServiceResponse = musli::json::decode(response.as_slice())?;
+
+ Ok(services.service_vec())
+ }
+
+ pub fn add_file_via_path(
+ &self,
+ path: &str,
+ delete: Option<bool>,
+ domains: Option<FileDomain>,
+ ) -> Result<FileAddResponse> {
+ let mut form = AddFileRequest {
+ path: path.to_owned(),
+ delete_after_successful_import: delete,
+ ..Default::default()
+ };
+
+ if let Some(file_domains) = domains {
+ match file_domains {
+ FileDomain::FileServiceKey(key) => form.file_service_key = Some(key),
+ FileDomain::FileServiceKeys(keys) => form.file_service_keys = Some(keys),
+ FileDomain::DeletedFileServiceKey(key) => form.deleted_file_service_key = Some(key),
+ FileDomain::DeletedFileServiceKeys(keys) => {
+ form.deleted_file_service_keys = Some(keys)
+ }
+ }
+ }
+
+ let data = musli::json::to_vec(&form)?;
+
+ let mut req_url = self.url.to_owned();
+ req_url.push_str("add_files/add_file");
+
+ let response = self
+ .set_post_api_key(ureq::post(req_url))?
+ .content_type("application/json")
+ .send(data)?
+ .body_mut()
+ .read_to_vec()?;
+
+ let status: FileAddResponse = musli::json::decode(response.as_slice())?;
+ Ok(status)
+ }
+
+ pub fn add_file_via_file(&self, file: &File) -> Result<FileAddResponse> {
+ let mut req_url = self.url.to_owned();
+ req_url.push_str("add_files/add_file");
+
+ let response = self
+ .set_post_api_key(ureq::post(req_url))?
+ .content_type("application/octet-stream")
+ .send(file)?
+ .body_mut()
+ .read_to_vec()?;
+
+ let status: FileAddResponse = musli::json::decode(response.as_slice())?;
+ Ok(status)
+ }
}
diff --git a/src/tests.rs b/src/tests.rs
index 3fafdbe..19d60cf 100644
--- a/src/tests.rs
+++ b/src/tests.rs
@@ -42,7 +42,9 @@ fn correct_hydrusservice_decode() {
}
#[test]
-fn get_keys() {
- let client: HydrusClient = HydrusClient::new("http://127.0.0.1:51251/");
- let _ = client.request_new_permissions("test client", &[]).unwrap();
+fn correct_hydrusservices_decode() {
+ let mut client: HydrusClient = HydrusClient::new("http://127.0.0.1:51251/");
+ client.set_api_key("7ab7accf6cf12b2c6c30436cd8fe16361aee33679dbd90da279b5c22b33d622a");
+ let test = client.get_services().unwrap();
+ assert!(!test.is_empty())
}
diff --git a/src/types.rs b/src/types.rs
index ed00efb..f94dd3c 100644
--- a/src/types.rs
+++ b/src/types.rs
@@ -1,4 +1,5 @@
use musli::{Allocator, Decode, Decoder, Encode, Encoder};
+use std::collections::HashMap;
use strum_macros::FromRepr;
use thiserror::Error;
@@ -6,7 +7,7 @@ use thiserror::Error;
pub enum HydrusError {
#[error("failed to connect to Hydrus")]
NetworkError(ureq::Error),
- #[error("failed to deserialize data")]
+ #[error("failed to encode/decode data")]
DeserializeError(musli::json::Error),
#[error("api or session key needed")]
KeyNotSupplied,
@@ -70,11 +71,7 @@ where
where
D: Decoder<'de>,
{
- if let Some(val) = HydrusPermissions::from_repr(decoder.decode()?) {
- return Ok(val);
- } else {
- return Ok(Self::Null);
- }
+ Ok(HydrusPermissions::from_repr(decoder.decode()?).unwrap())
}
}
@@ -147,25 +144,121 @@ where
where
D: Decoder<'de>,
{
- if let Some(val) = ServiceType::from_repr(decoder.decode()?) {
- return Ok(val);
- } else {
- return Ok(Self::Null);
- }
+ Ok(ServiceType::from_repr(decoder.decode()?).unwrap())
}
}
-#[derive(Decode)]
+#[derive(Decode, Debug)]
pub struct Service {
pub name: String,
#[musli(default)]
pub service_key: String,
- pub servicetype: ServiceType,
+ pub r#type: ServiceType,
pub type_pretty: String,
#[musli(default)]
- pub star_shape: String,
+ pub star_shape: Option<String>,
#[musli(default)]
- pub min_stars: u8,
+ pub min_stars: Option<u8>,
#[musli(default)]
- pub max_stars: u8,
+ pub max_stars: Option<u8>,
+}
+
+#[derive(Decode, Debug)]
+pub struct ServiceResponse {
+ services: HashMap<String, Service>,
+}
+
+impl ServiceResponse {
+ pub fn service_vec(mut self) -> Vec<Service> {
+ let mut res = Vec::new();
+ for (key, mut service) in self.services.drain() {
+ service.service_key = key;
+ res.push(service);
+ }
+ res
+ }
+}
+
+pub enum FileDomain {
+ FileServiceKey(String),
+ FileServiceKeys(Vec<String>),
+ DeletedFileServiceKey(String),
+ DeletedFileServiceKeys(Vec<String>),
+}
+
+#[derive(Encode, Debug, Default)]
+pub struct AddFileRequest {
+ pub path: String,
+ #[musli(skip)]
+ pub delete_after_successful_import: Option<bool>,
+ #[musli(skip)]
+ pub file_service_key: Option<String>,
+ #[musli(skip)]
+ pub file_service_keys: Option<Vec<String>>,
+ #[musli(skip)]
+ pub deleted_file_service_key: Option<String>,
+ #[musli(skip)]
+ pub deleted_file_service_keys: Option<Vec<String>>,
+}
+
+#[derive(PartialEq, Debug, Clone, FromRepr)]
+#[repr(u8)]
+pub enum FileAddStatus {
+ SuccessfulImport = 1,
+ AlreadyInDatabase,
+ PreviouslyDeleted,
+ FailedToImport,
+ FileVetoed = 7,
+}
+
+impl<M> Encode<M> for FileAddStatus {
+ type Encode = Self;
+
+ #[inline]
+ fn encode<E>(&self, encoder: E) -> Result<(), E::Error>
+ where
+ E: Encoder<Mode = M>,
+ {
+ encoder.encode(self.clone() as u8)
+ }
+
+ #[inline]
+ fn as_encode(&self) -> &Self::Encode {
+ self
+ }
+}
+
+impl<'de, M, A> Decode<'de, M, A> for FileAddStatus
+where
+ A: Allocator,
+{
+ #[inline]
+ fn decode<D>(decoder: D) -> Result<Self, D::Error>
+ where
+ D: Decoder<'de>,
+ {
+ Ok(FileAddStatus::from_repr(decoder.decode()?).unwrap())
+ }
+}
+
+#[derive(Decode)]
+pub struct FileAddResponse {
+ pub status: FileAddStatus,
+ pub hash: String,
+ pub note: String,
+}
+
+#[derive(Encode, Debug, Default)]
+pub struct DeleteFileRequest {
+ pub path: String,
+ #[musli(skip)]
+ pub file_service_key: Option<String>,
+ #[musli(skip)]
+ pub file_service_keys: Option<Vec<String>>,
+ #[musli(skip)]
+ pub deleted_file_service_key: Option<String>,
+ #[musli(skip)]
+ pub deleted_file_service_keys: Option<Vec<String>>,
+ #[musli(skip)]
+ pub reason: String,
}