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