diff --git a/src/client.rs b/src/client.rs index 1b2eddb..fc162ab 100644 --- a/src/client.rs +++ b/src/client.rs @@ -25,6 +25,7 @@ use serde::Serialize; static ACCESS_KEY_HEADER: &str = "Hydrus-Client-API-Access-Key"; +#[derive(Clone)] pub struct Client { inner: reqwest::Client, base_url: String, diff --git a/src/endpoints/access_management.rs b/src/endpoints/access_management.rs index 96d759c..6f2597d 100644 --- a/src/endpoints/access_management.rs +++ b/src/endpoints/access_management.rs @@ -2,6 +2,15 @@ use crate::endpoints::common::BasicServiceInfo; use crate::endpoints::Endpoint; use std::collections::HashMap; +pub static SERVICE_TYPE_LOCAL_TAGS: &str = "local_tags"; +pub static SERVICE_TYPE_TAG_REPOSITORIES: &str = "tag_repositories"; +pub static SERVICE_TYPE_LOCAL_FILES: &str = "local_files"; +pub static SERVICE_TYPE_FILE_REPOSITORIES: &str = "file_repositories"; +pub static SERVICE_TYPE_ALL_LOCAL_FILES: &str = "all_local_files"; +pub static SERVICE_TYPE_ALL_KNOWN_FILES: &str = "all_known_files"; +pub static SERVICE_TYPE_ALL_KNOWN_TAGS: &str = "all_known_tags"; +pub static SERVICE_TYPE_TRASH: &str = "trash"; + #[derive(Debug, Clone, Deserialize)] pub struct ApiVersionResponse { pub version: u32, diff --git a/src/error.rs b/src/error.rs index 34e7d64..a0cb93f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -7,6 +7,7 @@ pub type Result = std::result::Result; pub enum Error { Reqwest(reqwest::Error), Hydrus(String), + InvalidServiceType(String), } impl fmt::Display for Error { @@ -14,6 +15,9 @@ impl fmt::Display for Error { match self { Self::Reqwest(e) => e.fmt(f), Self::Hydrus(msg) => msg.fmt(f), + Self::InvalidServiceType(service_type) => { + write!(f, "Invalid Service Type '{}'", service_type) + } } } } @@ -23,6 +27,7 @@ impl StdError for Error { match self { Self::Reqwest(e) => e.source(), Self::Hydrus(_) => None, + Self::InvalidServiceType(_) => None, } } } diff --git a/src/lib.rs b/src/lib.rs index 068f010..956c588 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,6 +37,9 @@ extern crate serde; pub mod client; pub mod endpoints; pub mod error; +mod models; pub(crate) mod utils; pub use client::Client; +pub use models::hydrus::Hydrus; +pub use models::*; diff --git a/src/models/builders/mod.rs b/src/models/builders/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/models/hydrus.rs b/src/models/hydrus.rs new file mode 100644 index 0000000..19137ee --- /dev/null +++ b/src/models/hydrus.rs @@ -0,0 +1,31 @@ +use crate::error::Result; +use crate::models::version::Version; +use crate::service::Services; +use crate::Client; + +pub struct Hydrus { + client: Client, +} + +impl Hydrus { + /// Creates a new high level Hydrus API client + pub fn new(client: Client) -> Self { + Self { client } + } + + /// Returns the Hydrus and API Version + pub async fn version(&mut self) -> Result { + let response = self.client.api_version().await?; + Ok(Version { + api: response.version, + hydrus: response.hydrus_version, + }) + } + + /// Returns a list of available services + pub async fn services(&mut self) -> Result { + let response = self.client.get_services().await?; + + Ok(Services::from_response(self.client.clone(), response)) + } +} diff --git a/src/models/mod.rs b/src/models/mod.rs new file mode 100644 index 0000000..7a1c167 --- /dev/null +++ b/src/models/mod.rs @@ -0,0 +1,4 @@ +pub mod builders; +pub mod hydrus; +pub mod service; +pub mod version; diff --git a/src/models/service.rs b/src/models/service.rs new file mode 100644 index 0000000..a0f36d3 --- /dev/null +++ b/src/models/service.rs @@ -0,0 +1,113 @@ +use crate::endpoints::access_management::GetServicesResponse; +use crate::endpoints::access_management::{ + SERVICE_TYPE_ALL_KNOWN_FILES, SERVICE_TYPE_ALL_KNOWN_TAGS, SERVICE_TYPE_ALL_LOCAL_FILES, + SERVICE_TYPE_FILE_REPOSITORIES, SERVICE_TYPE_LOCAL_FILES, SERVICE_TYPE_LOCAL_TAGS, + SERVICE_TYPE_TAG_REPOSITORIES, SERVICE_TYPE_TRASH, +}; +use crate::error::Error; +use crate::Client; +use std::collections::HashMap; +use std::convert::TryFrom; + +#[derive(Clone, PartialOrd, PartialEq, Hash)] +pub enum ServiceType { + LocalTags, + TagRepositories, + LocalFiles, + FileRepositories, + AllLocalFiles, + AllKnownFiles, + AllKnownTags, + Trash, +} + +impl Eq for ServiceType {} + +impl TryFrom for ServiceType { + type Error = Error; + + fn try_from(value: String) -> Result { + match value.as_str() { + s if s == SERVICE_TYPE_LOCAL_TAGS => Ok(Self::LocalTags), + s if s == SERVICE_TYPE_TAG_REPOSITORIES => Ok(Self::TagRepositories), + s if s == SERVICE_TYPE_LOCAL_FILES => Ok(Self::LocalFiles), + s if s == SERVICE_TYPE_FILE_REPOSITORIES => Ok(Self::FileRepositories), + s if s == SERVICE_TYPE_ALL_LOCAL_FILES => Ok(Self::AllLocalFiles), + s if s == SERVICE_TYPE_ALL_KNOWN_FILES => Ok(Self::AllKnownFiles), + s if s == SERVICE_TYPE_ALL_KNOWN_TAGS => Ok(Self::AllKnownTags), + s if s == SERVICE_TYPE_TRASH => Ok(Self::Trash), + _ => Err(Error::InvalidServiceType(value)), + } + } +} + +impl ToString for ServiceType { + fn to_string(&self) -> String { + match self { + ServiceType::LocalTags => String::from(SERVICE_TYPE_LOCAL_TAGS), + ServiceType::TagRepositories => String::from(SERVICE_TYPE_TAG_REPOSITORIES), + ServiceType::LocalFiles => String::from(SERVICE_TYPE_LOCAL_FILES), + ServiceType::FileRepositories => String::from(SERVICE_TYPE_FILE_REPOSITORIES), + ServiceType::AllLocalFiles => String::from(SERVICE_TYPE_ALL_LOCAL_FILES), + ServiceType::AllKnownFiles => String::from(SERVICE_TYPE_ALL_KNOWN_FILES), + ServiceType::AllKnownTags => String::from(SERVICE_TYPE_ALL_KNOWN_TAGS), + ServiceType::Trash => String::from(SERVICE_TYPE_TRASH), + } + } +} + +#[derive(Clone)] +pub struct Service { + client: Client, + pub name: String, + pub key: String, + pub service_type: ServiceType, +} + +#[derive(Clone)] +pub struct Services { + inner: HashMap>, +} + +impl Services { + pub fn from_response(client: Client, response: GetServicesResponse) -> Self { + let mut response = response.0; + let mut mapped_types = HashMap::with_capacity(response.keys().len()); + let keys = response.keys().cloned().collect::>().clone(); + + for service_type in &keys { + if let Ok(mapped_type) = ServiceType::try_from(service_type.clone()) { + let basic_services = response.remove(service_type).unwrap(); + let mut service_list = Vec::new(); + + for basic_service in basic_services { + service_list.push(Service { + service_type: mapped_type.clone(), + name: basic_service.name, + key: basic_service.service_key, + client: client.clone(), + }) + } + + mapped_types.insert(mapped_type, service_list); + } + } + + Self { + inner: mapped_types, + } + } + + /// Returns a list of all services of the given type + pub fn get_services(&self, service_type: ServiceType) -> Vec<&Service> { + if let Some(services) = self.inner.get(&service_type) { + let mut borrowed_services = Vec::with_capacity(services.len()); + for service in services { + borrowed_services.push(service) + } + borrowed_services + } else { + Vec::with_capacity(0) + } + } +} diff --git a/src/models/version.rs b/src/models/version.rs new file mode 100644 index 0000000..5150d26 --- /dev/null +++ b/src/models/version.rs @@ -0,0 +1,4 @@ +pub struct Version { + pub api: u32, + pub hydrus: u32, +} diff --git a/tests/client/mod.rs b/tests/client/mod.rs new file mode 100644 index 0000000..3c81da6 --- /dev/null +++ b/tests/client/mod.rs @@ -0,0 +1,5 @@ +mod test_access_management; +mod test_adding_files; +mod test_adding_tags; +mod test_adding_urls; +mod test_searching_and_fetching_files; diff --git a/tests/test_access_management.rs b/tests/client/test_access_management.rs similarity index 97% rename from tests/test_access_management.rs rename to tests/client/test_access_management.rs index a06313f..1834fca 100644 --- a/tests/test_access_management.rs +++ b/tests/client/test_access_management.rs @@ -1,4 +1,4 @@ -mod common; +use super::super::common; #[tokio::test] async fn it_returns_the_api_version() { diff --git a/tests/test_adding_files.rs b/tests/client/test_adding_files.rs similarity index 97% rename from tests/test_adding_files.rs rename to tests/client/test_adding_files.rs index 5c4bc2d..aa12bcc 100644 --- a/tests/test_adding_files.rs +++ b/tests/client/test_adding_files.rs @@ -1,4 +1,4 @@ -mod common; +use super::super::common; #[tokio::test] async fn it_adds_files() { diff --git a/tests/test_adding_tags.rs b/tests/client/test_adding_tags.rs similarity index 97% rename from tests/test_adding_tags.rs rename to tests/client/test_adding_tags.rs index eb98b11..819cdc1 100644 --- a/tests/test_adding_tags.rs +++ b/tests/client/test_adding_tags.rs @@ -1,5 +1,5 @@ +use super::super::common; use hydrus_api::endpoints::adding_tags::{AddTagsRequestBuilder, TagAction}; -mod common; #[tokio::test] async fn it_cleans_tags() { diff --git a/tests/test_adding_urls.rs b/tests/client/test_adding_urls.rs similarity index 98% rename from tests/test_adding_urls.rs rename to tests/client/test_adding_urls.rs index f8672ef..01e5af0 100644 --- a/tests/test_adding_urls.rs +++ b/tests/client/test_adding_urls.rs @@ -1,7 +1,6 @@ +use super::super::common; use hydrus_api::endpoints::adding_urls::{AddUrlRequestBuilder, URL_TYPE_POST}; -mod common; - #[tokio::test] async fn it_returns_files_for_an_url() { let mut client = common::get_client(); diff --git a/tests/test_searching_and_fetching_files.rs b/tests/client/test_searching_and_fetching_files.rs similarity index 97% rename from tests/test_searching_and_fetching_files.rs rename to tests/client/test_searching_and_fetching_files.rs index 3403579..42d4940 100644 --- a/tests/test_searching_and_fetching_files.rs +++ b/tests/client/test_searching_and_fetching_files.rs @@ -1,8 +1,7 @@ +use super::super::common; use hydrus_api::endpoints::common::FileIdentifier; use hydrus_api::endpoints::searching_and_fetching_files::FileSearchLocation; -mod common; - #[tokio::test] async fn is_searches_files() { let mut client = common::get_client(); diff --git a/tests/common.rs b/tests/common.rs index d84f128..48d41c4 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -1,4 +1,5 @@ use hydrus_api::client::Client; +use hydrus_api::Hydrus; use log::LevelFilter; use std::env; use std::sync::atomic::{AtomicBool, Ordering}; @@ -15,9 +16,16 @@ pub fn setup() { pub fn get_client() -> Client { setup(); + Client::new( env::var("HYDRUS_URL").unwrap(), env::var("HYDRUS_ACCESS_KEY").unwrap(), ) .unwrap() } + +pub fn get_hydrus() -> Hydrus { + let client = get_client(); + + Hydrus::new(client) +} diff --git a/tests/mod.rs b/tests/mod.rs new file mode 100644 index 0000000..aad7eab --- /dev/null +++ b/tests/mod.rs @@ -0,0 +1,3 @@ +mod client; +mod common; +mod wrapper; diff --git a/tests/wrapper/mod.rs b/tests/wrapper/mod.rs new file mode 100644 index 0000000..bcb74a6 --- /dev/null +++ b/tests/wrapper/mod.rs @@ -0,0 +1 @@ +mod test_hydrus; diff --git a/tests/wrapper/test_hydrus.rs b/tests/wrapper/test_hydrus.rs new file mode 100644 index 0000000..4f46687 --- /dev/null +++ b/tests/wrapper/test_hydrus.rs @@ -0,0 +1,20 @@ +use super::super::common; +use hydrus_api::service::ServiceType; + +#[tokio::test] +async fn it_retrieves_version_info() { + let mut hydrus = common::get_hydrus(); + let version = hydrus.version().await.unwrap(); + assert!(version.hydrus > 0); + assert!(version.api > 0); +} + +#[tokio::test] +async fn it_retrieves_services() { + let mut hydrus = common::get_hydrus(); + let services = hydrus.services().await.unwrap(); + + // assuming hydrus is configured correctly + assert!(services.get_services(ServiceType::AllKnownFiles).len() > 0); + assert!(services.get_services(ServiceType::AllKnownTags).len() > 0); +}