summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorjakka <jakka@jakka.su>2025-09-17 21:25:08 +0300
committerjakka <jakka@jakka.su>2025-09-17 21:25:08 +0300
commita5f74e05d49ad938c3f0854dc9b1d203a570a8d9 (patch)
tree76251488c9c05c6319999ef50f21a0ccd2fbf18a /src
parentd07a6af4563c0fde224aa630fb2d9feec01579f2 (diff)
moved to async http client and serde
Diffstat (limited to 'src')
-rw-r--r--src/client.rs167
-rw-r--r--src/tests.rs48
-rw-r--r--src/types.rs166
3 files changed, 129 insertions, 252 deletions
diff --git a/src/client.rs b/src/client.rs
index 293ef2f..c323714 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -1,12 +1,14 @@
-use std::fs::File;
+use std::path::Path;
-use ureq::RequestBuilder;
+use reqwest::{Body, RequestBuilder};
+use tokio_util::codec::{BytesCodec, FramedRead};
use crate::types::*;
type Result<T> = std::result::Result<T, HydrusError>;
pub struct HydrusClient {
+ client: reqwest::Client,
apikey: Option<String>,
sessionkey: Option<String>,
url: String,
@@ -15,6 +17,7 @@ pub struct HydrusClient {
impl HydrusClient {
pub fn new(url: &str) -> HydrusClient {
HydrusClient {
+ client: reqwest::Client::new(),
apikey: None,
sessionkey: None,
url: url.to_string(),
@@ -29,65 +32,68 @@ impl HydrusClient {
self.sessionkey = Some(key.to_owned())
}
- pub fn request_new_permissions(
+ pub async fn request_new_permissions(
&self,
name: &str,
permissions: &[HydrusPermissions],
) -> Result<String> {
let mut req_url = self.url.to_owned();
req_url.push_str("request_new_permissions?name=");
- req_url.push_str(&urlencoding::encode(name));
+
+ req_url.push_str(name);
if permissions.is_empty() {
req_url.push_str("&permit_everything=true");
} else {
+ let json_string = &serde_json::to_string(&permissions)?;
req_url.push_str("&basic_permissions=");
- let json_string = musli::json::to_string(&permissions)?;
- req_url.push_str(&urlencoding::encode(&json_string));
+ req_url.push_str(&urlencoding::encode(json_string));
};
- let response = ureq::get(req_url).call()?.body_mut().read_to_vec()?;
-
- let key: AccessKey = musli::json::decode(response.as_slice())?;
-
- Ok(key.access_key)
+ Ok(self
+ .client
+ .get(req_url)
+ .send()
+ .await?
+ .json::<AccessKey>()
+ .await?
+ .access_key)
}
- pub fn get_session_key(&self) -> Result<String> {
+ pub async fn get_session_key(&self) -> Result<String> {
let mut req_url = self.url.to_owned();
req_url.push_str("session_key");
- let mut request = ureq::get(req_url);
+ let mut request = self.client.get(req_url);
if let Some(key) = &self.apikey {
request = request.header("Hydrus-Client-API-Access-Key", key);
}
- let response = request.call()?.body_mut().read_to_vec()?;
-
- let key: SessionKey = musli::json::decode(response.as_slice())?;
-
- Ok(key.session_key)
+ Ok(request
+ .send()
+ .await?
+ .json::<SessionKey>()
+ .await?
+ .session_key)
}
- pub fn verify_access_key(&self, key: &str) -> Result<KeyInfo> {
+ pub async fn verify_access_key(&self, key: &str) -> Result<KeyInfo> {
let mut req_url = self.url.to_owned();
req_url.push_str("verify_access_key");
- let response = ureq::get(req_url)
- .header("Hydrus-Client-API-Access-Key", key)
- .call()?
- .body_mut()
- .read_to_vec()?;
- let data: KeyInfo = musli::json::decode(response.as_slice())?;
-
- Ok(data)
+ Ok(self
+ .client
+ .get(req_url)
+ .header("Hydrus-Client-API-Access-Key", key)
+ .send()
+ .await?
+ .json::<KeyInfo>()
+ .await?)
}
- fn set_get_api_key(
- &self,
- request: RequestBuilder<ureq::typestate::WithoutBody>,
- ) -> Result<RequestBuilder<ureq::typestate::WithoutBody>> {
+ fn set_get_request_key(&self, url: &str) -> Result<RequestBuilder> {
+ let request = self.client.get(url);
if let Some(key) = &self.sessionkey {
Ok(request.header("Hydrus-Client-API-Access-Key", key))
} else if let Some(key) = &self.apikey {
@@ -97,10 +103,8 @@ impl HydrusClient {
}
}
- fn set_post_api_key(
- &self,
- request: RequestBuilder<ureq::typestate::WithBody>,
- ) -> Result<RequestBuilder<ureq::typestate::WithBody>> {
+ fn set_post_request_key(&self, url: &str) -> Result<RequestBuilder> {
+ let request = self.client.post(url);
if let Some(key) = &self.sessionkey {
Ok(request.header("Hydrus-Client-API-Access-Key", key))
} else if let Some(key) = &self.apikey {
@@ -110,52 +114,46 @@ impl HydrusClient {
}
}
- pub fn get_service_name(&self, name: &str) -> Result<Service> {
+ pub async 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 = 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)
+ Ok(self
+ .set_get_request_key(&req_url)?
+ .send()
+ .await?
+ .json::<Service>()
+ .await?)
}
- pub fn get_service_key(&self, key: &str) -> Result<Service> {
+ pub async 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)
+ Ok(self
+ .set_get_request_key(&req_url)?
+ .send()
+ .await?
+ .json::<Service>()
+ .await?)
}
- pub fn get_services(&self) -> Result<Vec<Service>> {
+ pub async 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())
+ Ok(self
+ .set_get_request_key(&req_url)?
+ .send()
+ .await?
+ .json::<ServiceResponse>()
+ .await?
+ .service_vec())
}
- pub fn add_file_via_path(
+ pub async fn add_file_via_path(
&self,
path: &str,
delete: Option<bool>,
@@ -178,34 +176,35 @@ impl HydrusClient {
}
}
- 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)
+ Ok(self
+ .set_post_request_key(&req_url)?
+ .header("Content-Type", "application/json")
+ .json(&form)
+ .send()
+ .await?
+ .json::<FileAddResponse>()
+ .await?)
}
- pub fn add_file_via_file(&self, file: &File) -> Result<FileAddResponse> {
+ pub async fn add_file_via_file(&self, file: impl AsRef<Path>) -> 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 file = tokio::fs::File::open(file).await?;
+
+ let stream = FramedRead::new(file, BytesCodec::new());
+ let body = Body::wrap_stream(stream);
- let status: FileAddResponse = musli::json::decode(response.as_slice())?;
- Ok(status)
+ Ok(self
+ .set_post_request_key(&req_url)?
+ .header("Content-Type", "application/octet-stream")
+ .body(body)
+ .send()
+ .await?
+ .json::<FileAddResponse>()
+ .await?)
}
}
diff --git a/src/tests.rs b/src/tests.rs
index 19d60cf..0ce410e 100644
--- a/src/tests.rs
+++ b/src/tests.rs
@@ -1,50 +1,10 @@
use crate::client::HydrusClient;
use crate::types::*;
-#[test]
-fn correct_hydruspermissions_url_encode() {
- let perms: [HydrusPermissions; 3] = [
- HydrusPermissions::ImportAndEditURLs,
- HydrusPermissions::ImportAndEditFiles,
- HydrusPermissions::SeeLocalPaths,
- ];
- let json_string = musli::json::to_string(&perms).unwrap();
- let encoded = urlencoding::encode(&json_string);
-
- assert_eq!(encoded, "%5B0%2C1%2C13%5D")
-}
-
-#[test]
-fn correct_hydruspermissions_decode() {
- let perms: [HydrusPermissions; 3] = [
- HydrusPermissions::ImportAndEditURLs,
- HydrusPermissions::ImportAndEditFiles,
- HydrusPermissions::SeeLocalPaths,
- ];
- let json_string = musli::json::to_string(&perms).unwrap();
-
- let res: [HydrusPermissions; 3] = musli::json::from_str(&json_string).unwrap();
- assert_eq!(res, perms)
-}
-
-#[test]
-fn correct_hydrusservice_decode() {
- let input: [ServiceType; 3] = [
- ServiceType::AllLocalFiles,
- ServiceType::ClientAPI,
- ServiceType::Trash,
- ];
-
- let json_string = musli::json::to_string(&input).unwrap();
-
- let res: [ServiceType; 3] = musli::json::from_str(&json_string).unwrap();
- assert_eq!(res, input)
-}
-
-#[test]
-fn correct_hydrusservices_decode() {
+#[tokio::test]
+async fn test_service_info() {
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())
+ let res = client.get_service_name("all my files").await.unwrap();
+ println!("{:?}", res);
}
diff --git a/src/types.rs b/src/types.rs
index 9c2684d..b6e9db4 100644
--- a/src/types.rs
+++ b/src/types.rs
@@ -1,31 +1,39 @@
-use musli::{Allocator, Decode, Decoder, Encode, Encoder};
+use serde::{Deserialize, Serialize};
+use serde_repr::{Deserialize_repr, Serialize_repr};
use std::collections::HashMap;
-use strum_macros::FromRepr;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum HydrusError {
#[error("failed to connect to Hydrus")]
- NetworkError(ureq::Error),
- #[error("failed to encode/decode data")]
- DeserializeError(musli::json::Error),
+ NetworkError(reqwest::Error),
+ #[error("failed to encode/Deserialize data")]
+ DeserializeError(serde_json::Error),
+ #[error("io error")]
+ IOError(std::io::Error),
#[error("api or session key needed")]
KeyNotSupplied,
}
-impl From<musli::json::Error> for HydrusError {
- fn from(value: musli::json::Error) -> Self {
+impl From<serde_json::Error> for HydrusError {
+ fn from(value: serde_json::Error) -> Self {
HydrusError::DeserializeError(value)
}
}
-impl From<ureq::Error> for HydrusError {
- fn from(value: ureq::Error) -> Self {
+impl From<std::io::Error> for HydrusError {
+ fn from(value: std::io::Error) -> Self {
+ HydrusError::IOError(value)
+ }
+}
+
+impl From<reqwest::Error> for HydrusError {
+ fn from(value: reqwest::Error) -> Self {
HydrusError::NetworkError(value)
}
}
-#[derive(PartialEq, Debug, Clone, FromRepr)]
+#[derive(PartialEq, Debug, Clone, Serialize_repr, Deserialize_repr)]
#[repr(u8)]
pub enum HydrusPermissions {
ImportAndEditURLs = 0,
@@ -45,47 +53,17 @@ pub enum HydrusPermissions {
Null = 255,
}
-impl<M> Encode<M> for HydrusPermissions {
- 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 HydrusPermissions
-where
- A: Allocator,
-{
- #[inline]
- fn decode<D>(decoder: D) -> Result<Self, D::Error>
- where
- D: Decoder<'de>,
- {
- Ok(HydrusPermissions::from_repr(decoder.decode()?).unwrap())
- }
-}
-
-#[derive(Decode, Debug)]
+#[derive(Deserialize, Debug)]
pub struct AccessKey {
pub access_key: String,
}
-#[derive(Decode, Debug)]
+#[derive(Deserialize, Debug)]
pub struct SessionKey {
pub session_key: String,
}
-#[derive(Decode, Debug)]
+#[derive(Deserialize, Debug)]
pub struct KeyInfo {
pub name: String,
pub permits_everything: bool,
@@ -93,7 +71,7 @@ pub struct KeyInfo {
pub human_permissions: String,
}
-#[derive(PartialEq, Debug, Clone, FromRepr)]
+#[derive(PartialEq, Debug, Clone, Serialize_repr, Deserialize_repr)]
#[repr(u8)]
pub enum ServiceType {
TagRepository = 0,
@@ -118,52 +96,22 @@ pub enum ServiceType {
Null = 255,
}
-impl<M> Encode<M> for ServiceType {
- 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 ServiceType
-where
- A: Allocator,
-{
- #[inline]
- fn decode<D>(decoder: D) -> Result<Self, D::Error>
- where
- D: Decoder<'de>,
- {
- Ok(ServiceType::from_repr(decoder.decode()?).unwrap())
- }
-}
-
-#[derive(Decode, Debug)]
+#[derive(Deserialize, Debug)]
pub struct Service {
pub name: String,
- #[musli(default)]
+ #[serde(default)]
pub service_key: String,
pub r#type: ServiceType,
pub type_pretty: String,
- #[musli(default)]
+ #[serde(default)]
pub star_shape: Option<String>,
- #[musli(default)]
+ #[serde(default)]
pub min_stars: Option<u8>,
- #[musli(default)]
+ #[serde(default)]
pub max_stars: Option<u8>,
}
-#[derive(Decode, Debug)]
+#[derive(Deserialize, Debug)]
pub struct ServiceResponse {
services: HashMap<String, Service>,
}
@@ -186,22 +134,22 @@ pub enum FileDomain {
DeletedFileServiceKeys(Vec<String>),
}
-#[derive(Encode, Debug, Default)]
+#[derive(Serialize, Debug, Default)]
pub struct AddFileRequest {
pub path: String,
- #[musli(skip)]
+ #[serde(skip)]
pub delete_after_successful_import: Option<bool>,
- #[musli(skip)]
+ #[serde(skip)]
pub file_service_key: Option<String>,
- #[musli(skip)]
+ #[serde(skip)]
pub file_service_keys: Option<Vec<String>>,
- #[musli(skip)]
+ #[serde(skip)]
pub deleted_file_service_key: Option<String>,
- #[musli(skip)]
+ #[serde(skip)]
pub deleted_file_service_keys: Option<Vec<String>>,
}
-#[derive(PartialEq, Debug, Clone, FromRepr)]
+#[derive(PartialEq, Debug, Clone, Serialize_repr, Deserialize_repr)]
#[repr(u8)]
pub enum FileAddStatus {
SuccessfulImport = 1,
@@ -211,54 +159,24 @@ pub enum FileAddStatus {
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)]
+#[derive(Deserialize)]
pub struct FileAddResponse {
pub status: FileAddStatus,
pub hash: String,
pub note: String,
}
-#[derive(Encode, Debug, Default)]
+#[derive(Serialize, Debug, Default)]
pub struct DeleteFileRequest {
pub path: String,
- #[musli(skip)]
+ #[serde(skip)]
pub file_service_key: Option<String>,
- #[musli(skip)]
+ #[serde(skip)]
pub file_service_keys: Option<Vec<String>>,
- #[musli(skip)]
+ #[serde(skip)]
pub deleted_file_service_key: Option<String>,
- #[musli(skip)]
+ #[serde(skip)]
pub deleted_file_service_keys: Option<Vec<String>>,
- #[musli(skip)]
+ #[serde(skip)]
pub reason: Option<String>,
}