From 7db80db8f1a286b74e944be368d64bbee7e7f07e Mon Sep 17 00:00:00 2001 From: Dev Goldy Date: Thu, 16 Nov 2023 23:26:31 +0000 Subject: [PATCH] feat: added new book headers and started adding search endpoint --- Cargo.toml | 2 +- src/book.rs | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++- src/client.rs | 62 +++++++++++++++----------------- src/lib.rs | 11 ++++++ 4 files changed, 137 insertions(+), 35 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0e70ba9..768bd45 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ readme = "README.md" documentation = "https://docs.rs/aghpb" repository = "https://github.com/THEGOLDENPRO/aghpb.rs" edition = "2021" -version = "1.3.2" +version = "1.4.0" [dependencies] reqwest = "0.11.18" diff --git a/src/book.rs b/src/book.rs index 620ffd0..4240c5a 100644 --- a/src/book.rs +++ b/src/book.rs @@ -1,16 +1,111 @@ +use std::collections::HashMap; + use bytes::Bytes; use chrono::{DateTime, FixedOffset}; use image::DynamicImage; +use reqwest::header::HeaderMap; pub struct Book { pub name: String, pub category: String, pub date_added: DateTime, + pub search_id: String, + pub commit_url: String, + pub commit_author: String, pub raw_bytes: Bytes, } impl Book { + /// Creates a book from a response's headers and bytes. + pub fn from_response(headers: HeaderMap, bytes: Bytes) -> Book { + let name = headers.get("book-name").expect( + "Failed acquiring book name header!" + ).to_str().expect("Failed converting book name to string.").to_owned(); + + let category = headers.get("book-category").expect( + "Failed acquiring book category header!" + ).to_str().expect("Failed converting book category to string.").to_owned(); + + let date_added = DateTime::parse_from_str(headers.get("book-date-added").expect( + "Failed acquiring book date added header!" + ).to_str().expect("Failed converting book date time to string."), "%Y-%m-%d %H:%M:%S%z").expect( + "Failed to convert book's date added header to date time object." + ); + + let search_id = headers.get("book-search-id").expect( + "Failed acquiring book search id header!" + ).to_str().expect("Failed converting book search id to string.").to_owned(); + + let commit_url = headers.get("book-commit-url").expect( + "Failed acquiring book commit url header!" + ).to_str().expect("Failed converting book commit url to string.").to_owned(); + + let commit_author = headers.get("book-commit-author").expect( + "Failed acquiring book commit author header!" + ).to_str().expect("Failed converting book commit author to string.").to_owned(); + + Self { + name, + category, + date_added, + search_id, + commit_url, + commit_author, + raw_bytes: bytes + } + } + pub fn to_image(&self) -> DynamicImage { - image::load_from_memory(&self.raw_bytes).expect("Failed to convert bytes into image.") + image::load_from_memory(&self.raw_bytes).expect("Failed to convert bytes into dynamic image object.") + } +} + + +pub struct BookResult { + pub name: String, + pub category: String, + pub date_added: DateTime, + pub search_id: String, + pub commit_url: String, + pub commit_author: String, +} + +impl BookResult { + /// Creates a book from a json dictionary. + pub fn from_json(book_dict: HashMap) -> BookResult { + let name = book_dict.get("name").expect( + "Failed acquiring book name from json!" + ).to_owned(); + + let category = book_dict.get("category").expect( + "Failed acquiring book category from json!" + ).to_owned(); + + let date_added = DateTime::parse_from_str(book_dict.get("date_added").expect( + "Failed acquiring book date added header!" + ), "%Y-%m-%d %H:%M:%S%z").expect( + "Failed to convert book's date added header to date time object." + ); + + let search_id = book_dict.get("search_id").expect( + "Failed acquiring book search id header!" + ).to_owned(); + + let commit_url = book_dict.get("commit_url").expect( + "Failed acquiring book commit url header!" + ).to_owned(); + + let commit_author = book_dict.get("commit_author").expect( + "Failed acquiring book commit author header!" + ).to_owned(); + + Self { + name, + category, + date_added, + search_id, + commit_url, + commit_author + } } } \ No newline at end of file diff --git a/src/client.rs b/src/client.rs index 69aef3d..1045c81 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,10 +1,6 @@ use std::{collections::HashMap, error::Error, fmt}; -use chrono::DateTime; -use reqwest::header::HeaderMap; -use bytes::Bytes; - -use crate::book::Book; +use crate::book::{Book, BookResult}; #[derive(Clone, Debug)] pub struct Client { @@ -34,9 +30,7 @@ impl Client { } } - /// Asynchronously grabs a random anime girl holding a programming book. - /// - /// WARNING: Will panic on incorrect category. + /// Grabs a random anime girl holding a programming book. /// /// Uses the ``/v1/random`` endpoint. pub async fn random(&self, category: Option<&str>) -> Result> { @@ -52,7 +46,7 @@ impl Client { let headers = response.headers().to_owned(); let bytes = response.bytes().await?; - Ok(get_book(headers, bytes)) + Ok(Book::from_response(headers, bytes)) } else { let error_json: HashMap = serde_json::from_str(&response.text().await?).unwrap(); Err( @@ -65,40 +59,42 @@ impl Client { } - /// Asynchronously grabs list of available categories. + /// Grabs list of available categories. /// /// Uses the ``/v1/categories`` endpoint. pub async fn categories(&self) -> Result, reqwest::Error> { - let mut base_url = self.api_url.clone(); - - base_url.push_str("/v1/categories"); - - let res = self.client.get(base_url).send().await?; - let json: Vec = serde_json::from_str(&res.text().await?).expect("Failed to deserialize json response"); + let res = self.client.get(self.api_url.clone() + "/v1/categories").send().await?; + let json: Vec = serde_json::from_str(&res.text().await?).expect("Failed to deserialize json response!"); Ok(json) } -} + /// Allows you to search for anime girls holding programming books. + /// + /// Uses the ``/v1/search`` endpoint. + pub async fn search(&self, query: &str, category: Option<&str>, limit: Option) -> Result, reqwest::Error> { + let mut queries: Vec<(&str, &str)> = Vec::new(); + queries.push(("query", query)); + + if let Some(category) = category { + queries.push(("category", category)); + } -fn get_book(headers: HeaderMap, bytes: Bytes) -> Book { - let name = headers.get("book-name").expect("Failed acquiring book name header!").to_str().expect( - "Failed converting book name to string." - ).to_owned(); + if let Some(limit) = limit { + queries.push(("limit", limit.to_string().as_str())); + } + + let res = self.client.get(self.api_url.clone() + "/v1/search").query(&queries).send().await?; + let json: Vec> = serde_json::from_str(&res.text().await?).expect("Failed to deserialize json response!"); - let category = headers.get("book-category").expect("Failed acquiring book category header!").to_str().expect( - "Failed converting book category to string.").to_owned(); + let mut books: Vec = Vec::new(); - let date_added = DateTime::parse_from_str(headers.get("book-date-added").expect( - "Failed acquiring book date added header!" - ).to_str().expect("Failed converting book date time to string."), "%Y-%m-%d %H:%M:%S%z").expect( - "Failed to convert book's date added header to date time object." - ); + for book_dict in json { + books.push( + BookResult::from_json(book_dict) + ); + } - Book { - name, - category, - date_added, - raw_bytes: bytes + Ok(books) } } \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 339a965..1262f3b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -83,4 +83,15 @@ pub async fn random(category: Option<&str>) -> Result> { /// Uses the ``/v1/categories`` endpoint. pub async fn categories() -> Result, reqwest::Error> { get_client().categories().await +} + +/// Allows you to search for anime girls holding programming books. +/// +/// NOTE: Use aghpb::Client for multiple requests. This uses a global client! +/// If you want more customization/speed it maybe preferable to make +/// your own client. +/// +/// Uses the ``/v1/search`` endpoint. +pub async fn search(query: &str, category: Option<&str>, limit: Option) -> Result, reqwest::Error> { + get_client().search(query, category, limit).await } \ No newline at end of file