open-library-api-rs 0.1.0

Async Rust client for the Open Library API
Documentation
// v0.0.1
//! Synchronous (blocking) wrappers around the async [`OpenLibraryClient`].
//!
//! Enable with the `blocking` Cargo feature.

#[cfg(feature = "blocking")]
mod inner {
    use std::collections::HashMap;

    use tokio::runtime::Runtime;

    use crate::client::OpenLibraryClient;
    use crate::error::Result;
    use crate::models::author::{Author, AuthorWorks};
    use crate::models::changes::{ChangesParams, RecentChange};
    use crate::models::common::{BooksJsCmd, ChangeKind, CoverKey, ImageSize, VolumeIdType};
    use crate::models::covers::CoverMeta;
    use crate::models::edition::Edition;
    use crate::models::list::{List, ListEditions, ListSeeds, ListSubjects, UserLists};
    use crate::models::partner::VolumesResponse;
    use crate::models::query::{BooksApiEntry, HistoryEntry, QueryResponse};
    use crate::models::reading_log::ReadingLog;
    use crate::models::search::{
        AuthorDoc, AuthorSearchParams, BookDoc, InsideDoc, ListDoc, SearchParams, SearchResponse,
        SubjectDoc, SubjectParams,
    };
    use crate::models::subject::Subject;
    use crate::models::work::{Work, WorkBookshelves, WorkEditions, WorkRatings};

    /// Synchronous wrapper around [`OpenLibraryClient`].
    ///
    /// Construct via [`OpenLibraryClient::into_blocking()`].
    pub struct BlockingClient {
        inner: OpenLibraryClient,
        rt: Runtime,
    }

    impl OpenLibraryClient {
        /// Convert this async client into a blocking client.
        pub fn into_blocking(self) -> Result<BlockingClient> {
            let rt = Runtime::new().map_err(|e| {
                crate::error::Error::InvalidInput(format!(
                    "failed to create tokio runtime: {e}"
                ))
            })?;
            Ok(BlockingClient { inner: self, rt })
        }
    }

    impl BlockingClient {
        // ── Works ─────────────────────────────────────────────────────────────

        pub fn get_work(&self, id: &str) -> Result<Work> {
            self.rt.block_on(self.inner.get_work(id))
        }

        pub fn get_work_editions(
            &self,
            id: &str,
            limit: u32,
            offset: u32,
        ) -> Result<WorkEditions> {
            self.rt.block_on(self.inner.get_work_editions(id, limit, offset))
        }

        pub fn get_work_ratings(&self, id: &str) -> Result<WorkRatings> {
            self.rt.block_on(self.inner.get_work_ratings(id))
        }

        pub fn get_work_bookshelves(&self, id: &str) -> Result<WorkBookshelves> {
            self.rt.block_on(self.inner.get_work_bookshelves(id))
        }

        // ── Editions ──────────────────────────────────────────────────────────

        pub fn get_edition(&self, id: &str) -> Result<Edition> {
            self.rt.block_on(self.inner.get_edition(id))
        }

        pub fn get_edition_by_isbn(&self, isbn: &str) -> Result<Edition> {
            self.rt.block_on(self.inner.get_edition_by_isbn(isbn))
        }

        // ── Books (bibkey) ────────────────────────────────────────────────────

        pub fn get_books(
            &self,
            bibkeys: &[String],
            jscmd: BooksJsCmd,
        ) -> Result<HashMap<String, BooksApiEntry>> {
            self.rt.block_on(self.inner.get_books(bibkeys, jscmd))
        }

        // ── Authors ───────────────────────────────────────────────────────────

        pub fn get_author(&self, id: &str) -> Result<Author> {
            self.rt.block_on(self.inner.get_author(id))
        }

        pub fn get_author_works(
            &self,
            id: &str,
            limit: u32,
            offset: u32,
        ) -> Result<AuthorWorks> {
            self.rt.block_on(self.inner.get_author_works(id, limit, offset))
        }

        // ── Search ────────────────────────────────────────────────────────────

        pub fn search(&self, params: SearchParams) -> Result<SearchResponse<BookDoc>> {
            self.rt.block_on(self.inner.search(params))
        }

        pub fn search_authors(
            &self,
            params: AuthorSearchParams,
        ) -> Result<SearchResponse<AuthorDoc>> {
            self.rt.block_on(self.inner.search_authors(params))
        }

        pub fn search_subjects(&self, query: &str) -> Result<SearchResponse<SubjectDoc>> {
            self.rt.block_on(self.inner.search_subjects(query))
        }

        pub fn search_lists(
            &self,
            query: &str,
            limit: Option<u32>,
        ) -> Result<SearchResponse<ListDoc>> {
            self.rt.block_on(self.inner.search_lists(query, limit))
        }

        pub fn search_inside(
            &self,
            query: &str,
            limit: Option<u32>,
        ) -> Result<SearchResponse<InsideDoc>> {
            self.rt.block_on(self.inner.search_inside(query, limit))
        }

        // ── Subjects ──────────────────────────────────────────────────────────

        pub fn get_subject(&self, slug: &str, params: SubjectParams) -> Result<Subject> {
            self.rt.block_on(self.inner.get_subject(slug, params))
        }

        // ── Covers ────────────────────────────────────────────────────────────

        pub fn cover_url(&self, key: CoverKey, value: &str, size: ImageSize) -> url::Url {
            self.inner.cover_url(key, value, size)
        }

        pub fn author_photo_url(&self, olid: &str, size: ImageSize) -> Result<url::Url> {
            self.inner.author_photo_url(olid, size)
        }

        pub fn cover_meta(&self, key: CoverKey, value: &str) -> Result<Vec<CoverMeta>> {
            self.rt.block_on(self.inner.cover_meta(key, value))
        }

        // ── Lists ─────────────────────────────────────────────────────────────

        pub fn get_user_lists(
            &self,
            username: &str,
            limit: u32,
            offset: u32,
        ) -> Result<UserLists> {
            self.rt.block_on(self.inner.get_user_lists(username, limit, offset))
        }

        pub fn get_list(&self, username: &str, list_id: &str) -> Result<List> {
            self.rt.block_on(self.inner.get_list(username, list_id))
        }

        pub fn get_list_editions(
            &self,
            username: &str,
            list_id: &str,
            limit: u32,
            offset: u32,
        ) -> Result<ListEditions> {
            self.rt
                .block_on(self.inner.get_list_editions(username, list_id, limit, offset))
        }

        pub fn get_list_subjects(&self, username: &str, list_id: &str) -> Result<ListSubjects> {
            self.rt.block_on(self.inner.get_list_subjects(username, list_id))
        }

        pub fn get_list_seeds(&self, username: &str, list_id: &str) -> Result<ListSeeds> {
            self.rt.block_on(self.inner.get_list_seeds(username, list_id))
        }

        // ── Reading log ───────────────────────────────────────────────────────

        pub fn get_want_to_read(&self, username: &str) -> Result<ReadingLog> {
            self.rt.block_on(self.inner.get_want_to_read(username))
        }

        pub fn get_currently_reading(&self, username: &str) -> Result<ReadingLog> {
            self.rt.block_on(self.inner.get_currently_reading(username))
        }

        pub fn get_already_read(&self, username: &str) -> Result<ReadingLog> {
            self.rt.block_on(self.inner.get_already_read(username))
        }

        // ── Partner / Volumes ─────────────────────────────────────────────────

        pub fn read_volume(
            &self,
            id_type: VolumeIdType,
            id_value: &str,
        ) -> Result<VolumesResponse> {
            self.rt.block_on(self.inner.read_volume(id_type, id_value))
        }

        pub fn read_volumes_batch(&self, requests: &[String]) -> Result<VolumesResponse> {
            self.rt.block_on(self.inner.read_volumes_batch(requests))
        }

        // ── Recent Changes ────────────────────────────────────────────────────

        pub fn get_recent_changes(&self, params: ChangesParams) -> Result<Vec<RecentChange>> {
            self.rt.block_on(self.inner.get_recent_changes(params))
        }

        pub fn get_changes_by_date(
            &self,
            date: &str,
            params: ChangesParams,
        ) -> Result<Vec<RecentChange>> {
            self.rt.block_on(self.inner.get_changes_by_date(date, params))
        }

        pub fn get_changes_by_kind(
            &self,
            kind: &ChangeKind,
            params: ChangesParams,
        ) -> Result<Vec<RecentChange>> {
            self.rt.block_on(self.inner.get_changes_by_kind(kind, params))
        }

        pub fn get_changes_by_date_and_kind(
            &self,
            date: &str,
            kind: &ChangeKind,
            params: ChangesParams,
        ) -> Result<Vec<RecentChange>> {
            self.rt
                .block_on(self.inner.get_changes_by_date_and_kind(date, kind, params))
        }

        // ── Generic Query & History ───────────────────────────────────────────

        pub fn query(
            &self,
            type_: &str,
            fields: &HashMap<String, String>,
            limit: u32,
            offset: u32,
        ) -> Result<QueryResponse> {
            self.rt.block_on(self.inner.query(type_, fields, limit, offset))
        }

        pub fn get_resource_history(&self, key: &str) -> Result<Vec<HistoryEntry>> {
            self.rt.block_on(self.inner.get_resource_history(key))
        }
    }
}

#[cfg(feature = "blocking")]
pub use inner::BlockingClient;