1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
// v0.0.1
//! # open-library-api-rs
//!
//! Async Rust client for the [Open Library API](https://openlibrary.org/developers/api).
//!
//! Covers every documented public read endpoint: works, editions, books (bibkey),
//! authors, full-text search (5 variants), subjects, covers, user lists, reading
//! logs, the partner/volumes API, recent changes, and generic query/history.
//!
//! ## Features
//!
//! - **Complete coverage** — all 12 Open Library API domains, 30+ methods
//! - **Strongly typed** — dedicated response structs; `#[serde(default)]` so new
//! API fields never break deserialization
//! - **Built-in rate limiting** — token-bucket via [`governor`]; 1 req/s default,
//! 3 req/s with an identifying `User-Agent`
//! - **Input validation** — OLIDs, ISBN-10/13, slugs, usernames, bibkeys, dates
//! all validated before any HTTP call
//! - **Secure** — `rustls` TLS, 10 MB body cap, `#![forbid(unsafe_code)]`
//! - **Blocking API** — sync wrappers behind the `blocking` feature flag
//!
//! ## Quick start
//!
//! ```no_run
//! use open_library_api_rs::{OpenLibraryClient, Result};
//! use open_library_api_rs::models::search::SearchParams;
//!
//! #[tokio::main]
//! async fn main() -> Result<()> {
//! // 1 req/s, rustls TLS, 10 s connect / 30 s request timeout
//! let client = OpenLibraryClient::builder().build()?;
//!
//! // Fetch a work by its Open Library Work ID
//! let work = client.get_work("OL45804W").await?;
//! println!("Title: {:?}", work.title);
//! println!("Subjects: {:?}", work.subjects);
//!
//! // Search across all works
//! let results = client.search(SearchParams {
//! q: Some("rust programming".into()),
//! limit: Some(5),
//! ..Default::default()
//! }).await?;
//! println!("Found {} results", results.num_found);
//! for doc in &results.docs {
//! println!(" {:?}", doc.title);
//! }
//!
//! Ok(())
//! }
//! ```
//!
//! ## Client configuration
//!
//! ```no_run
//! use open_library_api_rs::{OpenLibraryClient, Result};
//!
//! # async fn example() -> Result<()> {
//! let client = OpenLibraryClient::builder()
//! // Supply a contact email to unlock the 3 req/s identified tier
//! .contact_email("me@example.com")?
//! .rate_limit(3)
//! .build()?;
//! # Ok(())
//! # }
//! ```
//!
//! [`OpenLibraryClient`] is `Clone + Send + Sync` — clone it cheaply to share
//! across tasks or threads.
//!
//! See [`client::OpenLibraryClientBuilder`] for the full set of builder options.
//!
//! ## Cargo features
//!
//! | Feature | Default | Description |
//! |---|---|---|
//! | `rustls-tls` | **on** | TLS via `rustls` (no system certs needed) |
//! | `native-tls` | off | TLS via the OS / OpenSSL |
//! | `blocking` | off | Sync wrappers for every async method |
//!
//! [`governor`]: https://docs.rs/governor
// Top-level re-exports for ergonomic imports.
pub use OpenLibraryClient;
pub use ;