Add first horrible implementation of hydrus dictionaries
Signed-off-by: trivernis <trivernis@protonmail.com>main
commit
a6c8ad8795
@ -0,0 +1,3 @@
|
|||||||
|
/target
|
||||||
|
Cargo.lock
|
||||||
|
.env
|
@ -0,0 +1,8 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="DiscordProjectSettings">
|
||||||
|
<option name="show" value="ASK" />
|
||||||
|
<option name="description" value="" />
|
||||||
|
</component>
|
||||||
|
</project>
|
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="CPP_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/hydrus-ptr-client.iml" filepath="$PROJECT_DIR$/.idea/hydrus-ptr-client.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
@ -0,0 +1,29 @@
|
|||||||
|
[package]
|
||||||
|
name = "hydrus-ptr-client"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
tracing = "0.1.31"
|
||||||
|
thiserror = "1.0.30"
|
||||||
|
serde_json = "1.0.79"
|
||||||
|
flate2 = "1.0.22"
|
||||||
|
reqwest = "0.11.9"
|
||||||
|
|
||||||
|
[dependencies.serde]
|
||||||
|
version = "1.0.136"
|
||||||
|
features = ["derive"]
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
tracing-subscriber = "0.3.9"
|
||||||
|
dotenv = "0.15.0"
|
||||||
|
lazy_static = "1.4.0"
|
||||||
|
|
||||||
|
[dev-dependencies.tokio]
|
||||||
|
version = "1.17.0"
|
||||||
|
features = ["rt-multi-thread", "macros"]
|
||||||
|
|
||||||
|
[features]
|
||||||
|
rustls = ["reqwest/rustls"]
|
@ -0,0 +1,89 @@
|
|||||||
|
pub use crate::endpoints::*;
|
||||||
|
use crate::{ClientBuilder, Error, Result};
|
||||||
|
use flate2::write::ZlibDecoder;
|
||||||
|
use reqwest::Response;
|
||||||
|
use serde::Serialize;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
pub struct Client {
|
||||||
|
pub(crate) client: reqwest::Client,
|
||||||
|
pub(crate) base_url: String,
|
||||||
|
pub(crate) access_key: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Client {
|
||||||
|
/// Creates a new client builder
|
||||||
|
pub fn builder() -> ClientBuilder {
|
||||||
|
ClientBuilder::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new PTR Client
|
||||||
|
pub fn new<S1: ToString, S2: ToString>(endpoint: S1, access_key: S2) -> Self {
|
||||||
|
Self {
|
||||||
|
base_url: endpoint.to_string(),
|
||||||
|
client: reqwest::Client::new(),
|
||||||
|
access_key: access_key.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(skip(self), level = "debug")]
|
||||||
|
pub async fn options(&self) -> Result<OptionsResponse> {
|
||||||
|
self.get::<Options, ()>(&()).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Performs a get request to the given Get Endpoint
|
||||||
|
#[tracing::instrument(skip(self), level = "trace")]
|
||||||
|
async fn get<E: GetEndpoint, Q: Serialize + Debug>(&self, query: &Q) -> Result<E::Response> {
|
||||||
|
tracing::trace!("GET request to {}", E::path());
|
||||||
|
let response = self
|
||||||
|
.client
|
||||||
|
.get(format!("{}/{}", self.base_url, E::path()))
|
||||||
|
.query(query)
|
||||||
|
.header("Hydrus-Key", self.access_key.to_string())
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
|
let body = Self::get_body(response).await?;
|
||||||
|
let bytes = Self::decompress_body(body)?;
|
||||||
|
let response_type = Self::deserialize_body(bytes)?;
|
||||||
|
tracing::trace!("response is: {:?}", response_type);
|
||||||
|
|
||||||
|
Ok(response_type)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the body from the response
|
||||||
|
#[tracing::instrument(level = "trace")]
|
||||||
|
async fn get_body(response: Response) -> Result<Vec<u8>> {
|
||||||
|
if response.status().is_success() {
|
||||||
|
Ok(response.bytes().await?.to_vec())
|
||||||
|
} else {
|
||||||
|
let message = response.text().await?;
|
||||||
|
Err(Error::Response(message))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Uses zlib to decompress the body
|
||||||
|
#[tracing::instrument(skip(bytes), level = "trace")]
|
||||||
|
fn decompress_body(mut bytes: Vec<u8>) -> Result<Vec<u8>> {
|
||||||
|
tracing::trace!("body length {}", bytes.len());
|
||||||
|
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
let mut decoder = ZlibDecoder::new(buf);
|
||||||
|
|
||||||
|
decoder.write_all(&mut bytes)?;
|
||||||
|
buf = decoder.finish()?;
|
||||||
|
|
||||||
|
tracing::trace!("result length {}", buf.len());
|
||||||
|
|
||||||
|
Ok(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deserializes the body to the given type
|
||||||
|
#[tracing::instrument(skip(bytes), level = "trace")]
|
||||||
|
fn deserialize_body<T: FromJson>(bytes: Vec<u8>) -> Result<T> {
|
||||||
|
let json_value: serde_json::Value = serde_json::from_reader(&bytes[..])?;
|
||||||
|
tracing::trace!("json value = {}", json_value.to_string());
|
||||||
|
|
||||||
|
T::from_json(json_value)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
use crate::Client;
|
||||||
|
use crate::{Error, Result};
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
pub struct ClientBuilder {
|
||||||
|
reqwest_builder: reqwest::ClientBuilder,
|
||||||
|
endpoint: String,
|
||||||
|
access_key: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ClientBuilder {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
reqwest_builder: reqwest::ClientBuilder::new(),
|
||||||
|
endpoint: String::from("https://ptr.hydrus.network:45871"),
|
||||||
|
access_key: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClientBuilder {
|
||||||
|
/// Doesn't validate ssl certificates of the endpoint.
|
||||||
|
///
|
||||||
|
/// # Warning
|
||||||
|
/// Turning this on allows invalid and expired certificates which is a security risk.
|
||||||
|
pub fn accept_invalid_certs(mut self, accept: bool) -> Self {
|
||||||
|
self.reqwest_builder = self.reqwest_builder.danger_accept_invalid_certs(accept);
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the request timeout
|
||||||
|
pub fn timeout(mut self, timeout: Duration) -> Self {
|
||||||
|
self.reqwest_builder = self.reqwest_builder.timeout(timeout);
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the endpoint of the client.
|
||||||
|
/// The default endpoint is `https://ptr.hydrus.network:45871`
|
||||||
|
pub fn endpoint<S: ToString>(mut self, endpoint: S) -> Self {
|
||||||
|
self.endpoint = endpoint.to_string();
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the access key. This key is required for requests
|
||||||
|
/// to the PTR.
|
||||||
|
pub fn access_key<S: ToString>(mut self, access_key: S) -> Self {
|
||||||
|
self.access_key = Some(access_key.to_string());
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Validates the configuration and builds the client
|
||||||
|
pub fn build(self) -> Result<Client> {
|
||||||
|
let access_key = self
|
||||||
|
.access_key
|
||||||
|
.ok_or_else(|| Error::Builder(String::from("missing access key")))?;
|
||||||
|
|
||||||
|
Ok(Client {
|
||||||
|
client: self.reqwest_builder.build()?,
|
||||||
|
base_url: self.endpoint,
|
||||||
|
access_key,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,111 @@
|
|||||||
|
#![allow(unused)]
|
||||||
|
|
||||||
|
pub const HYDRUS_TYPE_BASE: u64 = 0;
|
||||||
|
pub const HYDRUS_TYPE_BASE_NAMED: u64 = 1;
|
||||||
|
pub const HYDRUS_TYPE_SHORTCUT_SET: u64 = 2;
|
||||||
|
pub const HYDRUS_TYPE_SUBSCRIPTION_LEGACY: u64 = 3;
|
||||||
|
pub const HYDRUS_TYPE_PERIODIC: u64 = 4;
|
||||||
|
pub const HYDRUS_TYPE_GALLERY_IDENTIFIER: u64 = 5;
|
||||||
|
pub const HYDRUS_TYPE_TAG_IMPORT_OPTIONS: u64 = 6;
|
||||||
|
pub const HYDRUS_TYPE_FILE_IMPORT_OPTIONS: u64 = 7;
|
||||||
|
pub const HYDRUS_TYPE_FILE_SEED_CACHE: u64 = 8;
|
||||||
|
pub const HYDRUS_TYPE_HDD_IMPORT: u64 = 9;
|
||||||
|
pub const HYDRUS_TYPE_SERVER_TO_CLIENT_CONTENT_UPDATE_PACKAGE: u64 = 10;
|
||||||
|
pub const HYDRUS_TYPE_SERVER_TO_CLIENT_SERVICE_UPDATE_PACKAGE: u64 = 11;
|
||||||
|
pub const HYDRUS_TYPE_MANAGEMENT_CONTROLLER: u64 = 12;
|
||||||
|
pub const HYDRUS_TYPE_GUI_SESSION_LEGACY: u64 = 13;
|
||||||
|
pub const HYDRUS_TYPE_PREDICATE: u64 = 14;
|
||||||
|
pub const HYDRUS_TYPE_FILE_SEARCH_CONTEXT: u64 = 15;
|
||||||
|
pub const HYDRUS_TYPE_EXPORT_FOLDER: u64 = 16;
|
||||||
|
pub const HYDRUS_TYPE_WATCHER_IMPORT: u64 = 17;
|
||||||
|
pub const HYDRUS_TYPE_SIMPLE_DOWNLOADER_IMPORT: u64 = 18;
|
||||||
|
pub const HYDRUS_TYPE_IMPORT_FOLDER: u64 = 19;
|
||||||
|
pub const HYDRUS_TYPE_MULTIPLE_GALLERY_IMPORT: u64 = 20;
|
||||||
|
pub const HYDRUS_TYPE_DICTIONARY: u64 = 21;
|
||||||
|
pub const HYDRUS_TYPE_CLIENT_OPTIONS: u64 = 22;
|
||||||
|
pub const HYDRUS_TYPE_CONTENT: u64 = 23;
|
||||||
|
pub const HYDRUS_TYPE_PETITION: u64 = 24;
|
||||||
|
pub const HYDRUS_TYPE_ACCOUNT_IDENTIFIER: u64 = 25;
|
||||||
|
pub const HYDRUS_TYPE_LIST: u64 = 26;
|
||||||
|
pub const HYDRUS_TYPE_PARSE_FORMULA_HTML: u64 = 27;
|
||||||
|
pub const HYDRUS_TYPE_URLS_IMPORT: u64 = 28;
|
||||||
|
pub const HYDRUS_TYPE_PARSE_NODE_CONTENT_LINK: u64 = 29;
|
||||||
|
pub const HYDRUS_TYPE_CONTENT_PARSER: u64 = 30;
|
||||||
|
pub const HYDRUS_TYPE_PARSE_FORMULA_JSON: u64 = 31;
|
||||||
|
pub const HYDRUS_TYPE_PARSE_ROOT_FILE_LOOKUP: u64 = 32;
|
||||||
|
pub const HYDRUS_TYPE_BYTES_DICT: u64 = 33;
|
||||||
|
pub const HYDRUS_TYPE_CONTENT_UPDATE: u64 = 34;
|
||||||
|
pub const HYDRUS_TYPE_CREDENTIALS: u64 = 35;
|
||||||
|
pub const HYDRUS_TYPE_DEFINITIONS_UPDATE: u64 = 36;
|
||||||
|
pub const HYDRUS_TYPE_METADATA: u64 = 37;
|
||||||
|
pub const HYDRUS_TYPE_BANDWIDTH_RULES: u64 = 38;
|
||||||
|
pub const HYDRUS_TYPE_BANDWIDTH_TRACKER: u64 = 39;
|
||||||
|
pub const HYDRUS_TYPE_CLIENT_TO_SERVER_UPDATE: u64 = 40;
|
||||||
|
pub const HYDRUS_TYPE_SHORTCUT: u64 = 41;
|
||||||
|
pub const HYDRUS_TYPE_APPLICATION_COMMAND: u64 = 42;
|
||||||
|
pub const HYDRUS_TYPE_DUPLICATE_ACTION_OPTIONS: u64 = 43;
|
||||||
|
pub const HYDRUS_TYPE_TAG_FILTER: u64 = 44;
|
||||||
|
pub const HYDRUS_TYPE_NETWORK_BANDWIDTH_MANAGER_LEGACY: u64 = 45;
|
||||||
|
pub const HYDRUS_TYPE_NETWORK_SESSION_MANAGER_LEGACY: u64 = 46;
|
||||||
|
pub const HYDRUS_TYPE_NETWORK_CONTEXT: u64 = 47;
|
||||||
|
pub const HYDRUS_TYPE_NETWORK_LOGIN_MANAGER: u64 = 48;
|
||||||
|
pub const HYDRUS_TYPE_MEDIA_SORT: u64 = 49;
|
||||||
|
pub const HYDRUS_TYPE_URL_CLASS: u64 = 50;
|
||||||
|
pub const HYDRUS_TYPE_STRING_MATCH: u64 = 51;
|
||||||
|
pub const HYDRUS_TYPE_CHECKER_OPTIONS: u64 = 52;
|
||||||
|
pub const HYDRUS_TYPE_NETWORK_DOMAIN_MANAGER: u64 = 53;
|
||||||
|
pub const HYDRUS_TYPE_SUBSCRIPTION_QUERY_LEGACY: u64 = 54;
|
||||||
|
pub const HYDRUS_TYPE_STRING_CONVERTER: u64 = 55;
|
||||||
|
pub const HYDRUS_TYPE_FILENAME_TAGGING_OPTIONS: u64 = 56;
|
||||||
|
pub const HYDRUS_TYPE_FILE_SEED: u64 = 57;
|
||||||
|
pub const HYDRUS_TYPE_PAGE_PARSER: u64 = 58;
|
||||||
|
pub const HYDRUS_TYPE_PARSE_FORMULA_COMPOUND: u64 = 59;
|
||||||
|
pub const HYDRUS_TYPE_PARSE_FORMULA_CONTEXT_VARIABLE: u64 = 60;
|
||||||
|
pub const HYDRUS_TYPE_TAG_SUMMARY_GENERATOR: u64 = 61;
|
||||||
|
pub const HYDRUS_TYPE_PARSE_RULE_HTML: u64 = 62;
|
||||||
|
pub const HYDRUS_TYPE_SIMPLE_DOWNLOADER_PARSE_FORMULA: u64 = 63;
|
||||||
|
pub const HYDRUS_TYPE_MULTIPLE_WATCHER_IMPORT: u64 = 64;
|
||||||
|
pub const HYDRUS_TYPE_SERVICE_TAG_IMPORT_OPTIONS: u64 = 65;
|
||||||
|
pub const HYDRUS_TYPE_GALLERY_SEED: u64 = 66;
|
||||||
|
pub const HYDRUS_TYPE_GALLERY_SEED_LOG: u64 = 67;
|
||||||
|
pub const HYDRUS_TYPE_GALLERY_IMPORT: u64 = 68;
|
||||||
|
pub const HYDRUS_TYPE_GALLERY_URL_GENERATOR: u64 = 69;
|
||||||
|
pub const HYDRUS_TYPE_NESTED_GALLERY_URL_GENERATOR: u64 = 70;
|
||||||
|
pub const HYDRUS_TYPE_DOMAIN_METADATA_PACKAGE: u64 = 71;
|
||||||
|
pub const HYDRUS_TYPE_LOGIN_CREDENTIAL_DEFINITION: u64 = 72;
|
||||||
|
pub const HYDRUS_TYPE_LOGIN_SCRIPT_DOMAIN: u64 = 73;
|
||||||
|
pub const HYDRUS_TYPE_LOGIN_STEP: u64 = 74;
|
||||||
|
pub const HYDRUS_TYPE_CLIENT_API_MANAGER: u64 = 75;
|
||||||
|
pub const HYDRUS_TYPE_CLIENT_API_PERMISSIONS: u64 = 76;
|
||||||
|
pub const HYDRUS_TYPE_SERVICE_KEYS_TO_TAGS: u64 = 77;
|
||||||
|
pub const HYDRUS_TYPE_MEDIA_COLLECT: u64 = 78;
|
||||||
|
pub const HYDRUS_TYPE_TAG_DISPLAY_MANAGER: u64 = 79;
|
||||||
|
pub const HYDRUS_TYPE_TAG_SEARCH_CONTEXT: u64 = 80;
|
||||||
|
pub const HYDRUS_TYPE_FAVOURITE_SEARCH_MANAGER: u64 = 81;
|
||||||
|
pub const HYDRUS_TYPE_NOTE_IMPORT_OPTIONS: u64 = 82;
|
||||||
|
pub const HYDRUS_TYPE_STRING_SPLITTER: u64 = 83;
|
||||||
|
pub const HYDRUS_TYPE_STRING_PROCESSOR: u64 = 84;
|
||||||
|
pub const HYDRUS_TYPE_TAG_AUTOCOMPLETE_OPTIONS: u64 = 85;
|
||||||
|
pub const HYDRUS_TYPE_SUBSCRIPTION_QUERY_LOG_CONTAINER: u64 = 86;
|
||||||
|
pub const HYDRUS_TYPE_SUBSCRIPTION_QUERY_HEADER: u64 = 87;
|
||||||
|
pub const HYDRUS_TYPE_SUBSCRIPTION: u64 = 88;
|
||||||
|
pub const HYDRUS_TYPE_FILE_SEED_CACHE_STATUS: u64 = 89;
|
||||||
|
pub const HYDRUS_TYPE_SUBSCRIPTION_CONTAINER: u64 = 90;
|
||||||
|
pub const HYDRUS_TYPE_COLUMN_LIST_STATUS: u64 = 91;
|
||||||
|
pub const HYDRUS_TYPE_COLUMN_LIST_MANAGER: u64 = 92;
|
||||||
|
pub const HYDRUS_TYPE_NUMBER_TEST: u64 = 93;
|
||||||
|
pub const HYDRUS_TYPE_NETWORK_BANDWIDTH_MANAGER: u64 = 94;
|
||||||
|
pub const HYDRUS_TYPE_NETWORK_SESSION_MANAGER: u64 = 95;
|
||||||
|
pub const HYDRUS_TYPE_NETWORK_SESSION_MANAGER_SESSION_CONTAINER: u64 = 96;
|
||||||
|
pub const HYDRUS_TYPE_NETWORK_BANDWIDTH_MANAGER_TRACKER_CONTAINER: u64 = 97;
|
||||||
|
pub const HYDRUS_TYPE_SIDECAR_EXPORTER: u64 = 98;
|
||||||
|
pub const HYDRUS_TYPE_STRING_SORTER: u64 = 99;
|
||||||
|
pub const HYDRUS_TYPE_STRING_SLICER: u64 = 100;
|
||||||
|
pub const HYDRUS_TYPE_TAG_SORT: u64 = 101;
|
||||||
|
pub const HYDRUS_TYPE_ACCOUNT_TYPE: u64 = 102;
|
||||||
|
pub const HYDRUS_TYPE_LOCATION_SEARCH_CONTEXT: u64 = 103;
|
||||||
|
pub const HYDRUS_TYPE_GUI_SESSION_CONTAINER: u64 = 104;
|
||||||
|
pub const HYDRUS_TYPE_GUI_SESSION_PAGE_DATA: u64 = 105;
|
||||||
|
pub const HYDRUS_TYPE_GUI_SESSION_CONTAINER_PAGE_NOTEBOOK: u64 = 106;
|
||||||
|
pub const HYDRUS_TYPE_GUI_SESSION_CONTAINER_PAGE_SINGLE: u64 = 107;
|
||||||
|
pub const HYDRUS_TYPE_PRESENTATION_IMPORT_OPTIONS: u64 = 108;
|
@ -0,0 +1,32 @@
|
|||||||
|
mod options;
|
||||||
|
|
||||||
|
use crate::Result;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
pub use options::*;
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! fix {
|
||||||
|
($opt:expr) => {
|
||||||
|
$opt.ok_or_else(|| crate::Error::Malformed)?
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Endpoint {
|
||||||
|
fn path() -> &'static str;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait GetEndpoint: Endpoint {
|
||||||
|
type Response: FromJson + Debug;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait PostEndpoint: Endpoint {
|
||||||
|
type Request;
|
||||||
|
type Response: FromJson + Debug;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait FromJson {
|
||||||
|
fn from_json(value: serde_json::Value) -> Result<Self>
|
||||||
|
where
|
||||||
|
Self: Sized;
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
use crate::hydrus_serializable::dictionary::HydrusDictionary;
|
||||||
|
use crate::hydrus_serializable::wrapper::HydrusSerWrapper;
|
||||||
|
use crate::Result;
|
||||||
|
use crate::{fix, Endpoint, FromJson, GetEndpoint};
|
||||||
|
use serde_json::Value;
|
||||||
|
|
||||||
|
pub struct Options;
|
||||||
|
|
||||||
|
impl Endpoint for Options {
|
||||||
|
fn path() -> &'static str {
|
||||||
|
"options"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GetEndpoint for Options {
|
||||||
|
type Response = OptionsResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct OptionsResponse {
|
||||||
|
server_message: String,
|
||||||
|
update_period: u64,
|
||||||
|
nullification_period: u64,
|
||||||
|
tag_filter: Value,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromJson for OptionsResponse {
|
||||||
|
fn from_json(value: serde_json::Value) -> Result<Self> {
|
||||||
|
let response = serde_json::from_value::<HydrusSerWrapper<HydrusDictionary>>(value)?;
|
||||||
|
let options_value = fix!(response.inner.get_one(&"service_options".into()));
|
||||||
|
let options_value =
|
||||||
|
serde_json::from_value::<HydrusSerWrapper<HydrusDictionary>>(options_value.clone())?
|
||||||
|
.inner;
|
||||||
|
|
||||||
|
let server_message =
|
||||||
|
fix!(fix!(options_value.get_one(&"server_message".into())).as_str()).to_string();
|
||||||
|
let update_period = fix!(fix!(options_value.get_one(&"update_period".into())).as_u64());
|
||||||
|
let nullification_period =
|
||||||
|
fix!(fix!(options_value.get_one(&"nullification_period".into())).as_u64());
|
||||||
|
let tag_filter = fix!(options_value.get_one(&"tag_filter".into())).clone();
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
server_message,
|
||||||
|
update_period,
|
||||||
|
nullification_period,
|
||||||
|
tag_filter,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum Error {
|
||||||
|
#[error("reqwest error {0}")]
|
||||||
|
Reqwest(#[from] reqwest::Error),
|
||||||
|
|
||||||
|
#[error("api returned error response: {0}")]
|
||||||
|
Response(String),
|
||||||
|
|
||||||
|
#[error("failed to parse content as json: {0}")]
|
||||||
|
JSON(#[from] serde_json::Error),
|
||||||
|
|
||||||
|
#[error("io error {0}")]
|
||||||
|
Io(#[from] std::io::Error),
|
||||||
|
|
||||||
|
#[error("builder error: {0}")]
|
||||||
|
Builder(String),
|
||||||
|
|
||||||
|
#[error("malformed response")]
|
||||||
|
Malformed,
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
use crate::constants::HYDRUS_TYPE_DICTIONARY;
|
||||||
|
use crate::hydrus_serializable::HydrusSerializable;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use serde_json::Value;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
|
pub struct HydrusDictionary {
|
||||||
|
list_sim_sim: Vec<(Value, Value)>,
|
||||||
|
list_sim_ser: Vec<(Value, Value)>,
|
||||||
|
list_ser_sim: Vec<(Value, Value)>,
|
||||||
|
list_ser_ser: Vec<(Value, Value)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HydrusSerializable for HydrusDictionary {
|
||||||
|
fn type_id() -> u64 {
|
||||||
|
HYDRUS_TYPE_DICTIONARY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HydrusDictionary {
|
||||||
|
/// Returns the first value for a given key
|
||||||
|
pub fn get_one(&self, key: &Value) -> Option<&Value> {
|
||||||
|
self.get(key).into_iter().next()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns all values for a given key
|
||||||
|
pub fn get(&self, key: &Value) -> Vec<&Value> {
|
||||||
|
self.list_sim_sim
|
||||||
|
.iter()
|
||||||
|
.chain(self.list_sim_ser.iter())
|
||||||
|
.chain(self.list_ser_sim.iter())
|
||||||
|
.chain(self.list_ser_ser.iter())
|
||||||
|
.filter(|(k, _)| k == key)
|
||||||
|
.map(|(_, v)| v)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,71 @@
|
|||||||
|
use crate::hydrus_serializable::dictionary::HydrusDictionary;
|
||||||
|
use serde::de::{DeserializeOwned, EnumAccess, Error, MapAccess, SeqAccess, Visitor};
|
||||||
|
use serde::{Deserialize, Deserializer, Serialize};
|
||||||
|
use serde_json::Value;
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
use std::fmt::Formatter;
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
|
pub mod dictionary;
|
||||||
|
pub mod wrapper;
|
||||||
|
|
||||||
|
pub trait HydrusSerializable: DeserializeOwned {
|
||||||
|
fn type_id() -> u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct SerializableId<T: HydrusSerializable>(u64, PhantomData<T>);
|
||||||
|
|
||||||
|
impl<'de, T: HydrusSerializable> Deserialize<'de> for SerializableId<T> {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
deserializer.deserialize_u64(SerIdVisitor(PhantomData))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SerIdVisitor<T>(PhantomData<T>);
|
||||||
|
|
||||||
|
impl<'de, T: HydrusSerializable> Visitor<'de> for SerIdVisitor<T> {
|
||||||
|
type Value = SerializableId<T>;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
|
||||||
|
write!(formatter, "an unsigned integer equal to {}", T::type_id())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_u8<E>(self, v: u8) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: Error,
|
||||||
|
{
|
||||||
|
self.visit_u64(v as u64)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_u16<E>(self, v: u16) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: Error,
|
||||||
|
{
|
||||||
|
self.visit_u64(v as u64)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_u32<E>(self, v: u32) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: Error,
|
||||||
|
{
|
||||||
|
self.visit_u64(v as u64)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: Error,
|
||||||
|
{
|
||||||
|
let expected_value = T::type_id();
|
||||||
|
if v != expected_value {
|
||||||
|
Err(E::custom(format!("type not equal to {}", expected_value)))
|
||||||
|
} else {
|
||||||
|
Ok(SerializableId(expected_value, PhantomData))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
use crate::hydrus_serializable::{HydrusSerializable, SerializableId};
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
|
#[serde(bound = "")]
|
||||||
|
pub struct HydrusSerWrapper<T: HydrusSerializable> {
|
||||||
|
pub type_id: SerializableId<T>,
|
||||||
|
pub version: u8,
|
||||||
|
pub inner: T,
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
mod client;
|
||||||
|
mod client_builder;
|
||||||
|
pub(crate) mod constants;
|
||||||
|
mod endpoints;
|
||||||
|
mod error;
|
||||||
|
pub mod hydrus_serializable;
|
||||||
|
|
||||||
|
pub use client::*;
|
||||||
|
pub use client_builder::*;
|
||||||
|
pub use error::*;
|
@ -0,0 +1,25 @@
|
|||||||
|
use hydrus_ptr_client::Client;
|
||||||
|
use std::env;
|
||||||
|
use std::sync::{Arc, Mutex, MutexGuard};
|
||||||
|
|
||||||
|
fn setup() {
|
||||||
|
lazy_static::lazy_static! { static ref SETUP_DONE: Arc<Mutex<bool>> = Arc::new(Mutex::new(false)); }
|
||||||
|
let mut setup_done: MutexGuard<bool> = SETUP_DONE.lock().unwrap();
|
||||||
|
|
||||||
|
if !*setup_done {
|
||||||
|
dotenv::dotenv().expect("failed to initialize dotenv");
|
||||||
|
tracing_subscriber::fmt::init();
|
||||||
|
*setup_done = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_client() -> Client {
|
||||||
|
setup();
|
||||||
|
|
||||||
|
Client::builder()
|
||||||
|
.endpoint(env::var("PTR_URL").unwrap())
|
||||||
|
.access_key(env::var("PTR_ACCESS_KEY").unwrap())
|
||||||
|
.accept_invalid_certs(true)
|
||||||
|
.build()
|
||||||
|
.unwrap()
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
mod common;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_options() {
|
||||||
|
let client = common::get_client();
|
||||||
|
client.options().await.unwrap();
|
||||||
|
}
|
Loading…
Reference in New Issue