Skip to content

Commit

Permalink
feat: added new book headers and started adding search endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
THEGOLDENPRO committed Nov 16, 2023
1 parent 2ed3b80 commit 7db80db
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 35 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
97 changes: 96 additions & 1 deletion src/book.rs
Original file line number Diff line number Diff line change
@@ -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<FixedOffset>,
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<FixedOffset>,
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<String, String>) -> 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
}
}
}
62 changes: 29 additions & 33 deletions src/client.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -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<Book, Box<dyn Error>> {
Expand All @@ -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<String, String> = serde_json::from_str(&response.text().await?).unwrap();
Err(
Expand All @@ -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<Vec<String>, 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<String> = 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<String> = 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<u8>) -> Result<Vec<BookResult>, 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()));

Check failure on line 84 in src/client.rs

View workflow job for this annotation

GitHub Actions / Run AGHPB Tests

temporary value dropped while borrowed
}

let res = self.client.get(self.api_url.clone() + "/v1/search").query(&queries).send().await?;
let json: Vec<HashMap<String, String>> = 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<BookResult> = 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)
}
}
11 changes: 11 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,15 @@ pub async fn random(category: Option<&str>) -> Result<Book, Box<dyn Error>> {
/// Uses the ``/v1/categories`` endpoint.
pub async fn categories() -> Result<Vec<String>, 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<u8>) -> Result<Vec<BookResult>, reqwest::Error> {
get_client().search(query, category, limit).await
}

0 comments on commit 7db80db

Please sign in to comment.