Add support for or-chains

Signed-off-by: trivernis <trivernis@protonmail.com>
pull/8/head
trivernis 3 years ago
parent daffe24734
commit f9e06a9af2
Signed by: Trivernis
GPG Key ID: DFFFCC2C7A02DB45

@ -24,6 +24,7 @@ use hydrus_api::wrapper::service::ServiceName;
use hydrus_api::wrapper::hydrus_file::FileStatus; use hydrus_api::wrapper::hydrus_file::FileStatus;
use hydrus_api::wrapper::page::PageIdentifier; use hydrus_api::wrapper::page::PageIdentifier;
use hydrus_api::wrapper::builders::search_builder::SortType; use hydrus_api::wrapper::builders::search_builder::SortType;
use hydrus_api::wrapper::builders::or_chain_builder::OrChainBuilder;
use hydrus_api::wrapper::builders::tag_builder::{ use hydrus_api::wrapper::builders::tag_builder::{
SystemTagBuilder, Comparator SystemTagBuilder, Comparator
}; };
@ -38,6 +39,12 @@ async fn main() {
.add_tag(Tag::from("character:megumin")) .add_tag(Tag::from("character:megumin"))
.add_tag(SystemTagBuilder::new().archive().build()) .add_tag(SystemTagBuilder::new().archive().build())
.add_tag(SystemTagBuilder::new().number_of_tags(Comparator::Greater, 12).build()) .add_tag(SystemTagBuilder::new().number_of_tags(Comparator::Greater, 12).build())
.add_or_chain(
OrChainBuilder::new()
.add_tag("summer".into())
.add_tag("winter".into())
.build(),
)
.sort(SortType::ModifiedTime) .sort(SortType::ModifiedTime)
.run().await.unwrap(); .run().await.unwrap();

@ -22,11 +22,13 @@ use crate::api_core::managing_pages::{
}; };
use crate::api_core::searching_and_fetching_files::{ use crate::api_core::searching_and_fetching_files::{
FileMetadata, FileMetadataResponse, FileSearchOptions, GetFile, SearchFiles, FileMetadata, FileMetadataResponse, FileSearchOptions, GetFile, SearchFiles,
SearchFilesResponse, SearchFilesResponse, SearchQueryEntry,
}; };
use crate::api_core::Endpoint; use crate::api_core::Endpoint;
use crate::error::{Error, Result}; use crate::error::{Error, Result};
use crate::utils::{number_list_to_json_array, string_list_to_json_array}; use crate::utils::{
number_list_to_json_array, search_query_list_to_json_array, string_list_to_json_array,
};
use reqwest::Response; use reqwest::Response;
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use serde::Serialize; use serde::Serialize;
@ -225,12 +227,12 @@ impl Client {
/// Searches for files in the inbox, the archive or both /// Searches for files in the inbox, the archive or both
pub async fn search_files( pub async fn search_files(
&self, &self,
tags: Vec<String>, query: Vec<SearchQueryEntry>,
options: FileSearchOptions, options: FileSearchOptions,
) -> Result<SearchFilesResponse> { ) -> Result<SearchFilesResponse> {
log::trace!("Searching for files with tags {:?}", tags); log::trace!("Searching for files with tags {:?}", query);
let mut args = options.into_query_args(); let mut args = options.into_query_args();
args.push(("tags", string_list_to_json_array(tags))); args.push(("tags", search_query_list_to_json_array(query)));
self.get_and_parse::<SearchFiles, [(&str, String)]>(&args) self.get_and_parse::<SearchFiles, [(&str, String)]>(&args)
.await .await
} }

@ -138,3 +138,18 @@ impl Endpoint for GetFile {
String::from("get_files/file") String::from("get_files/file")
} }
} }
#[derive(Clone, Debug)]
pub enum SearchQueryEntry {
Tag(String),
OrChain(Vec<String>),
}
impl<S> From<S> for SearchQueryEntry
where
S: ToString,
{
fn from(s: S) -> Self {
Self::Tag(s.to_string())
}
}

@ -14,6 +14,7 @@
//! use hydrus_api::wrapper::page::PageIdentifier; //! use hydrus_api::wrapper::page::PageIdentifier;
//! use hydrus_api::wrapper::builders::tag_builder::{SystemTagBuilder, Comparator}; //! use hydrus_api::wrapper::builders::tag_builder::{SystemTagBuilder, Comparator};
//! use hydrus_api::wrapper::builders::search_builder::SortType; //! use hydrus_api::wrapper::builders::search_builder::SortType;
//! use hydrus_api::wrapper::builders::or_chain_builder::OrChainBuilder;
//! //!
//! # #[tokio::test] //! # #[tokio::test]
//! # async fn doctest() { //! # async fn doctest() {
@ -24,6 +25,12 @@
//! .add_tag(Tag::from("character:megumin")) //! .add_tag(Tag::from("character:megumin"))
//! .add_tag(SystemTagBuilder::new().archive().build()) //! .add_tag(SystemTagBuilder::new().archive().build())
//! .add_tag(SystemTagBuilder::new().tag_namespace_as_number("page", Comparator::Equal, 5).negate().build()) //! .add_tag(SystemTagBuilder::new().tag_namespace_as_number("page", Comparator::Equal, 5).negate().build())
//! .add_or_chain(
//! OrChainBuilder::new()
//! .add_tag("summer".into())
//! .add_tag("winter".into())
//! .build(),
//! )
//! .sort_by(SortType::NumberOfPixels) //! .sort_by(SortType::NumberOfPixels)
//! .sort_descending() //! .sort_descending()
//! .run().await.unwrap(); //! .run().await.unwrap();

@ -1,7 +1,21 @@
use crate::api_core::common::FileIdentifier; use crate::api_core::common::FileIdentifier;
use crate::api_core::searching_and_fetching_files::SearchQueryEntry;
use crate::wrapper::tag::Tag; use crate::wrapper::tag::Tag;
use chrono::{Datelike, Duration}; use chrono::{Datelike, Duration};
/// Converts a list of Search Query entries into a json array
pub(crate) fn search_query_list_to_json_array(l: Vec<SearchQueryEntry>) -> String {
let entry_list: Vec<String> = l
.into_iter()
.map(|e| match e {
SearchQueryEntry::Tag(t) => format!("\"{}\"", t),
SearchQueryEntry::OrChain(c) => string_list_to_json_array(c),
})
.collect();
format!("[{}]", entry_list.join(","))
}
pub(crate) fn string_list_to_json_array(l: Vec<String>) -> String { pub(crate) fn string_list_to_json_array(l: Vec<String>) -> String {
format!("[\"{}\"]", l.join("\",\"")) format!("[\"{}\"]", l.join("\",\""))
} }

@ -1,4 +1,5 @@
pub mod import_builder; pub mod import_builder;
pub mod tagging_builder; pub mod or_chain_builder;
pub mod tag_builder;
pub mod search_builder; pub mod search_builder;
pub mod tag_builder;
pub mod tagging_builder;

@ -0,0 +1,30 @@
use crate::wrapper::or_chain::OrChain;
use crate::wrapper::tag::Tag;
#[derive(Debug)]
pub struct OrChainBuilder {
tags: Vec<Tag>,
}
impl OrChainBuilder {
pub fn new() -> Self {
Self { tags: Vec::new() }
}
/// Adds a tag to the or expression
pub fn add_tag(mut self, tag: Tag) -> Self {
self.tags.push(tag);
self
}
/// Adds multiple tags to the or expression
pub fn add_tags(mut self, mut tags: Vec<Tag>) -> Self {
self.tags.append(&mut tags);
self
}
/// Builds the or chain
pub fn build(self) -> OrChain {
OrChain::new(self.tags)
}
}

@ -1,7 +1,7 @@
use crate::api_core::searching_and_fetching_files::FileSearchOptions; use crate::api_core::searching_and_fetching_files::{FileSearchOptions, SearchQueryEntry};
use crate::error::Result; use crate::error::Result;
use crate::utils::tag_list_to_string_list;
use crate::wrapper::hydrus_file::HydrusFile; use crate::wrapper::hydrus_file::HydrusFile;
use crate::wrapper::or_chain::OrChain;
use crate::wrapper::service::ServiceName; use crate::wrapper::service::ServiceName;
use crate::wrapper::tag::Tag; use crate::wrapper::tag::Tag;
use crate::Client; use crate::Client;
@ -30,6 +30,7 @@ pub enum SortType {
pub struct SearchBuilder { pub struct SearchBuilder {
client: Client, client: Client,
tags: Vec<Tag>, tags: Vec<Tag>,
or_chains: Vec<OrChain>,
options: FileSearchOptions, options: FileSearchOptions,
} }
@ -38,6 +39,7 @@ impl SearchBuilder {
Self { Self {
client, client,
tags: Vec::new(), tags: Vec::new(),
or_chains: Vec::new(),
options: FileSearchOptions::new(), options: FileSearchOptions::new(),
} }
} }
@ -54,6 +56,12 @@ impl SearchBuilder {
self self
} }
/// Adds a new or chain
pub fn add_or_chain(mut self, chain: OrChain) -> Self {
self.or_chains.push(chain);
self
}
/// Sets the sort type /// Sets the sort type
pub fn sort_by(mut self, sort_type: SortType) -> Self { pub fn sort_by(mut self, sort_type: SortType) -> Self {
self.options = self.options.sort_type(sort_type as u8); self.options = self.options.sort_type(sort_type as u8);
@ -101,9 +109,19 @@ impl SearchBuilder {
/// Runs the search /// Runs the search
pub async fn run(self) -> Result<Vec<HydrusFile>> { pub async fn run(self) -> Result<Vec<HydrusFile>> {
let client = self.client.clone(); let client = self.client.clone();
let response = client let mut entries: Vec<SearchQueryEntry> = self
.search_files(tag_list_to_string_list(self.tags), self.options) .tags
.await?; .into_iter()
.map(|t| SearchQueryEntry::Tag(t.to_string()))
.collect();
entries.append(
&mut self
.or_chains
.into_iter()
.map(|c| SearchQueryEntry::OrChain(c.into_string_list()))
.collect(),
);
let response = client.search_files(entries, self.options).await?;
let files = response let files = response
.file_ids .file_ids
.into_iter() .into_iter()

@ -2,6 +2,7 @@ pub mod address;
pub mod builders; pub mod builders;
pub mod hydrus; pub mod hydrus;
pub mod hydrus_file; pub mod hydrus_file;
pub mod or_chain;
pub mod page; pub mod page;
pub mod service; pub mod service;
pub mod tag; pub mod tag;

@ -0,0 +1,46 @@
use crate::utils::tag_list_to_string_list;
use crate::wrapper::tag::Tag;
#[derive(Clone, Debug)]
pub struct OrChain {
tags: Vec<Tag>,
}
impl OrChain {
/// Creates a new or chain directly from a list of tags
pub fn new(tags: Vec<Tag>) -> Self {
Self { tags }
}
/// Returns the tags of this or chain
pub fn tags(&self) -> &Vec<Tag> {
&self.tags
}
pub(crate) fn into_string_list(self) -> Vec<String> {
tag_list_to_string_list(self.tags)
}
}
impl<S> From<S> for OrChain
where
S: AsRef<str>,
{
fn from(s: S) -> Self {
let s = s.as_ref().to_ascii_lowercase();
let tags = s
.split("or")
.map(|mut t| {
t = t
.trim_start()
.trim_start_matches("'")
.trim_start_matches("\"");
t = t.trim_end().trim_end_matches("'").trim_end_matches("\"");
t
})
.map(Tag::from)
.collect();
Self { tags }
}
}

@ -1,7 +1,7 @@
use super::super::common; use super::super::common;
use hydrus_api::api_core::common::FileIdentifier; use hydrus_api::api_core::common::FileIdentifier;
use hydrus_api::api_core::file_sort_type::SORT_FILE_PIXEL_COUNT; use hydrus_api::api_core::file_sort_type::SORT_FILE_PIXEL_COUNT;
use hydrus_api::api_core::searching_and_fetching_files::FileSearchOptions; use hydrus_api::api_core::searching_and_fetching_files::{FileSearchOptions, SearchQueryEntry};
#[tokio::test] #[tokio::test]
async fn is_searches_files() { async fn is_searches_files() {
@ -11,7 +11,13 @@ async fn is_searches_files() {
.tag_service_name("public tag repository") .tag_service_name("public tag repository")
.file_service_name("all known files"); .file_service_name("all known files");
client client
.search_files(vec!["beach".to_string()], options) .search_files(
vec![
"beach".into(),
SearchQueryEntry::OrChain(vec!["summer".to_string(), "winter".to_string()]),
],
options,
)
.await .await
.unwrap(); .unwrap();
} }

@ -1,5 +1,6 @@
use super::super::common; use super::super::common;
use hydrus_api::api_core::adding_tags::TagAction; use hydrus_api::api_core::adding_tags::TagAction;
use hydrus_api::wrapper::builders::or_chain_builder::OrChainBuilder;
use hydrus_api::wrapper::builders::search_builder::SortType; use hydrus_api::wrapper::builders::search_builder::SortType;
use hydrus_api::wrapper::service::{ServiceName, ServiceType}; use hydrus_api::wrapper::service::{ServiceName, ServiceType};
use hydrus_api::wrapper::url::UrlType; use hydrus_api::wrapper::url::UrlType;
@ -39,6 +40,12 @@ async fn it_searches() {
hydrus hydrus
.search() .search()
.add_tag("character:megumin".into()) .add_tag("character:megumin".into())
.add_or_chain(
OrChainBuilder::new()
.add_tag("summer".into())
.add_tag("winter".into())
.build(),
)
.sort_by(SortType::ModifiedTime) .sort_by(SortType::ModifiedTime)
.run() .run()
.await .await

Loading…
Cancel
Save