From 11a28071168d1694ca45f783ce4df75a62ab0cea Mon Sep 17 00:00:00 2001 From: trivernis Date: Wed, 2 Mar 2022 21:11:15 +0100 Subject: [PATCH] Add definitions updates Signed-off-by: trivernis --- src/client.rs | 8 +++ src/endpoints/mod.rs | 2 + src/endpoints/update.rs | 66 +++++++++++++++++ src/hydrus_serializable/definitions_update.rs | 71 +++++++++++++++++++ src/hydrus_serializable/metadata.rs | 3 +- src/hydrus_serializable/mod.rs | 1 + src/hydrus_serializable/tag_filter.rs | 3 +- src/hydrus_serializable/wrapper.rs | 23 ++++++ tests/endpoints.rs | 9 +++ 9 files changed, 184 insertions(+), 2 deletions(-) create mode 100644 src/endpoints/update.rs create mode 100644 src/hydrus_serializable/definitions_update.rs diff --git a/src/client.rs b/src/client.rs index ed6c1f5..17e6de6 100644 --- a/src/client.rs +++ b/src/client.rs @@ -40,6 +40,14 @@ impl Client { self.get::(&[("since", since)]).await } + /// Returns the parsed update file identified by the given hash. + /// The hash can be retrieved by fetching the metadata with [Client::metadata] + #[tracing::instrument(skip(self), level = "debug")] + pub async fn update + Debug>(&self, update_hash: S) -> Result { + self.get::(&[("update_hash", update_hash.as_ref())]) + .await + } + /// Performs a get request to the given Get Endpoint #[tracing::instrument(skip(self), level = "trace")] async fn get(&self, query: &Q) -> Result { diff --git a/src/endpoints/mod.rs b/src/endpoints/mod.rs index f036562..98678d6 100644 --- a/src/endpoints/mod.rs +++ b/src/endpoints/mod.rs @@ -1,11 +1,13 @@ mod metadata; mod options; +mod update; use crate::Result; use std::fmt::Debug; pub use metadata::*; pub use options::*; +pub use update::*; pub trait Endpoint { fn path() -> &'static str; diff --git a/src/endpoints/update.rs b/src/endpoints/update.rs new file mode 100644 index 0000000..485ca3b --- /dev/null +++ b/src/endpoints/update.rs @@ -0,0 +1,66 @@ +use crate::hydrus_serializable::definitions_update::{ + HashDefinition, HydrusDefinitionsUpdate, TagDefinition, +}; +use crate::hydrus_serializable::wrapper::GenericHydrusSerWrapper; +use crate::Result; +use crate::{Endpoint, Error, FromJson, GetEndpoint}; +use serde_json::Value; +use std::collections::HashMap; + +pub struct UpdateEndpoint; + +impl Endpoint for UpdateEndpoint { + fn path() -> &'static str { + "update" + } +} + +impl GetEndpoint for UpdateEndpoint { + type Response = UpdateResponse; +} + +#[derive(Clone, Debug)] +pub enum UpdateResponse { + Definitions(DefinitionsUpdateResponse), +} + +#[derive(Clone, Debug)] +pub struct DefinitionsUpdateResponse { + pub hashes: HashMap, + pub tags: HashMap, +} + +impl FromJson for UpdateResponse { + fn from_json(value: Value) -> crate::Result + where + Self: Sized, + { + let wrapper = serde_json::from_value::(value)?; + match wrapper.type_id { + 36 => { + let definitions_update = DefinitionsUpdateResponse::from_wrapper(wrapper)?; + + Ok(Self::Definitions(definitions_update)) + } + _ => Err(Error::Malformed), + } + } +} + +impl DefinitionsUpdateResponse { + fn from_wrapper(wrapper: GenericHydrusSerWrapper) -> Result { + let mut definitions_update = wrapper.into_inner::()?; + + let hashes = definitions_update + .take::()? + .map(|h| h.into_iter().map(|h| (h.id, h.hash)).collect()) + .unwrap_or_else(|| HashMap::new()); + + let tags = definitions_update + .take::()? + .map(|t| t.into_iter().map(|t| (t.id, t.tag)).collect()) + .unwrap_or_else(|| HashMap::new()); + + Ok(Self { hashes, tags }) + } +} diff --git a/src/hydrus_serializable/definitions_update.rs b/src/hydrus_serializable/definitions_update.rs new file mode 100644 index 0000000..9b0e91f --- /dev/null +++ b/src/hydrus_serializable/definitions_update.rs @@ -0,0 +1,71 @@ +use crate::constants::HYDRUS_TYPE_DEFINITIONS_UPDATE; +use crate::hydrus_serializable::HydrusSerializable; +use crate::{Error, Result}; +use serde::de::DeserializeOwned; +use serde::Deserialize; +use serde_json::Value; + +#[derive(Clone, Debug, Deserialize)] +pub struct HydrusDefinitionsUpdate(pub Vec); + +impl HydrusDefinitionsUpdate { + pub fn take(&mut self) -> Result>> { + let entry_index = self + .0 + .iter() + .position(|d| d.definition_id == D::definition_id()); + if let Some(idx) = entry_index { + let entry = self.0.swap_remove(idx); + + entry.into_inner() + } else { + Ok(None) + } + } +} + +impl HydrusSerializable for HydrusDefinitionsUpdate { + fn type_id() -> u64 { + HYDRUS_TYPE_DEFINITIONS_UPDATE + } +} + +#[derive(Clone, Debug, Deserialize)] +pub struct DefinitionsUpdateEntries { + pub definition_id: u64, + entries: Value, +} + +impl DefinitionsUpdateEntries { + pub fn into_inner(self) -> Result { + serde_json::from_value::(self.entries).map_err(Error::from) + } +} + +pub trait DefinitionsTrait: DeserializeOwned { + fn definition_id() -> u64; +} + +#[derive(Deserialize, Clone, Debug)] +pub struct HashDefinition { + pub id: u64, + pub hash: String, +} + +impl DefinitionsTrait for HashDefinition { + fn definition_id() -> u64 { + 0 + } +} + +#[derive(Deserialize, Clone, Debug)] +pub struct TagDefinition { + pub id: u64, + pub tag: String, +} + +impl DefinitionsTrait for TagDefinition { + fn definition_id() -> u64 { + 1 + } +} diff --git a/src/hydrus_serializable/metadata.rs b/src/hydrus_serializable/metadata.rs index c5a3499..41451a0 100644 --- a/src/hydrus_serializable/metadata.rs +++ b/src/hydrus_serializable/metadata.rs @@ -1,3 +1,4 @@ +use crate::constants::HYDRUS_TYPE_METADATA; use crate::hydrus_serializable::HydrusSerializable; use serde::Deserialize; @@ -17,6 +18,6 @@ pub struct MetadataEntry { impl HydrusSerializable for HydrusMetadata { fn type_id() -> u64 { - 37 + HYDRUS_TYPE_METADATA } } diff --git a/src/hydrus_serializable/mod.rs b/src/hydrus_serializable/mod.rs index 7e0b881..f0fbfba 100644 --- a/src/hydrus_serializable/mod.rs +++ b/src/hydrus_serializable/mod.rs @@ -6,6 +6,7 @@ use serde_json::Value; use std::fmt::Formatter; use std::marker::PhantomData; +pub mod definitions_update; pub mod dictionary; pub mod metadata; pub mod tag_filter; diff --git a/src/hydrus_serializable/tag_filter.rs b/src/hydrus_serializable/tag_filter.rs index 23ec34a..56f5a79 100644 --- a/src/hydrus_serializable/tag_filter.rs +++ b/src/hydrus_serializable/tag_filter.rs @@ -1,3 +1,4 @@ +use crate::constants::HYDRUS_TYPE_TAG_FILTER; use crate::hydrus_serializable::HydrusSerializable; use serde::Deserialize; use serde_json::Value; @@ -7,6 +8,6 @@ pub struct HydrusTagFilter(pub Value); impl HydrusSerializable for HydrusTagFilter { fn type_id() -> u64 { - 44 + HYDRUS_TYPE_TAG_FILTER } } diff --git a/src/hydrus_serializable/wrapper.rs b/src/hydrus_serializable/wrapper.rs index 1f7ffa4..46f8fc8 100644 --- a/src/hydrus_serializable/wrapper.rs +++ b/src/hydrus_serializable/wrapper.rs @@ -1,5 +1,7 @@ use crate::hydrus_serializable::{ConstNumberTrait, HydrusSerializable, SerializableId}; +use crate::{Error, Result}; use serde::Deserialize; +use serde_json::Value; #[derive(Clone, Debug, Deserialize)] pub struct VersionOne; @@ -19,3 +21,24 @@ pub struct HydrusSerWrapper { pub version: SerializableId, pub inner: T, } + +/// A generic hydrus serializable wrapper that allows one +/// to retrieve the type id and act on that +#[derive(Clone, Debug, Deserialize)] +pub struct GenericHydrusSerWrapper { + pub type_id: u64, + #[allow(unused)] + pub version: SerializableId, + pub inner: Value, +} + +impl GenericHydrusSerWrapper { + /// Converts the inner value into the target deserializable format + pub fn into_inner(self) -> Result { + if self.type_id == T::type_id() { + serde_json::from_value(self.inner).map_err(Error::from) + } else { + Err(Error::Malformed) + } + } +} diff --git a/tests/endpoints.rs b/tests/endpoints.rs index a6f0a45..83ac4cd 100644 --- a/tests/endpoints.rs +++ b/tests/endpoints.rs @@ -11,3 +11,12 @@ async fn test_metadata() { let client = common::get_client(); client.metadata(0).await.unwrap(); } + +#[tokio::test] +async fn test_update() { + let client = common::get_client(); + client + .update("4a4d13c1fcdf0cf734927ec4c9637fdac6144512ad7dc919e0f222e7b0e71586") + .await + .unwrap(); +}