Compare commits
3
Commits
d177e7b710
..
v1.0.6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
560effe226 | ||
|
|
e14797e57b | ||
|
|
efba2f2825 |
@@ -11,6 +11,12 @@ jobs:
|
|||||||
rust:
|
rust:
|
||||||
name: cargo test
|
name: cargo test
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
# The job ran out of memory linking the test binaries (ld killed with
|
||||||
|
# signal 9, run 705 twice): no debug info, and at most two links at once.
|
||||||
|
# What is tested does not change.
|
||||||
|
env:
|
||||||
|
CARGO_PROFILE_DEV_DEBUG: "0"
|
||||||
|
CARGO_BUILD_JOBS: "2"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
# rust-toolchain.toml pins the channel (with clippy); rustup installs it on
|
# rust-toolchain.toml pins the channel (with clippy); rustup installs it on
|
||||||
|
|||||||
@@ -44,6 +44,26 @@ struct Binding {
|
|||||||
by_title: bool,
|
by_title: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// One etext's binding: the identifier Open Library records, else — only for an
|
||||||
|
/// etext it lists under no work — the title match. Shared by the search path and
|
||||||
|
/// the redeem path so the two cannot bind the same book differently.
|
||||||
|
async fn bind(
|
||||||
|
client: &OpenLibraryClient,
|
||||||
|
id: u64,
|
||||||
|
title: &str,
|
||||||
|
author: &str,
|
||||||
|
deadline: std::time::Duration,
|
||||||
|
) -> Result<Option<Binding>, GatewayError> {
|
||||||
|
Ok(match client.gutenberg_etext(id, deadline).await? {
|
||||||
|
Etext::One(work) => Some(Binding { work, by_title: false }),
|
||||||
|
Etext::Several => None,
|
||||||
|
Etext::Unlisted => client
|
||||||
|
.title_work(title, author, deadline)
|
||||||
|
.await?
|
||||||
|
.map(|work| Binding { work, by_title: true }),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Canonical Gutendex base URL. Overridable via [`GutenbergPlugin::with_base_url`]
|
/// Canonical Gutendex base URL. Overridable via [`GutenbergPlugin::with_base_url`]
|
||||||
/// for tests + private mirrors.
|
/// for tests + private mirrors.
|
||||||
const DEFAULT_BASE_URL: &str = "https://gutendex.com";
|
const DEFAULT_BASE_URL: &str = "https://gutendex.com";
|
||||||
@@ -451,19 +471,7 @@ impl GutenbergPlugin {
|
|||||||
.collect();
|
.collect();
|
||||||
stream::iter(wanted)
|
stream::iter(wanted)
|
||||||
.map(|(id, title, author)| async move {
|
.map(|(id, title, author)| async move {
|
||||||
// The identifier first; a title match only for an etext Open
|
let lookup = bind(client, id, &title, &author, openlibrary_client::BINDING_DEADLINE);
|
||||||
// Library lists under no work at all.
|
|
||||||
let lookup = async {
|
|
||||||
let deadline = openlibrary_client::BINDING_DEADLINE;
|
|
||||||
Ok::<_, GatewayError>(match client.gutenberg_etext(id, deadline).await? {
|
|
||||||
Etext::One(work) => Some(Binding { work, by_title: false }),
|
|
||||||
Etext::Several => None,
|
|
||||||
Etext::Unlisted => client
|
|
||||||
.title_work(&title, &author, deadline)
|
|
||||||
.await?
|
|
||||||
.map(|work| Binding { work, by_title: true }),
|
|
||||||
})
|
|
||||||
};
|
|
||||||
match tokio::time::timeout(BINDING_TIMEOUT, lookup).await {
|
match tokio::time::timeout(BINDING_TIMEOUT, lookup).await {
|
||||||
Ok(Ok(binding)) => binding,
|
Ok(Ok(binding)) => binding,
|
||||||
Ok(Err(e)) => {
|
Ok(Err(e)) => {
|
||||||
@@ -628,9 +636,9 @@ impl FeederPlugin for GutenbergPlugin {
|
|||||||
// Not user-facing, so the binding lookup may queue for the full search
|
// Not user-facing, so the binding lookup may queue for the full search
|
||||||
// deadline. A failure still leaves the record unbound, never fails the
|
// deadline. A failure still leaves the record unbound, never fails the
|
||||||
// compute.
|
// compute.
|
||||||
|
let author = book.authors.first().map(|a| a.name.clone()).unwrap_or_default();
|
||||||
let work = match self.openlibrary.as_ref() {
|
let work = match self.openlibrary.as_ref() {
|
||||||
Some(ol) => ol
|
Some(ol) => bind(ol, book.id, &book.title, &author, openlibrary_client::SEARCH_DEADLINE)
|
||||||
.gutenberg_work(book.id, openlibrary_client::SEARCH_DEADLINE)
|
|
||||||
.await
|
.await
|
||||||
.unwrap_or_else(|e| {
|
.unwrap_or_else(|e| {
|
||||||
debug!(
|
debug!(
|
||||||
@@ -704,8 +712,8 @@ impl FeederPlugin for GutenbergPlugin {
|
|||||||
|
|
||||||
/// Convert a `GutendexBook` into our wire-level `DiscoveryRecord`.
|
/// Convert a `GutendexBook` into our wire-level `DiscoveryRecord`.
|
||||||
///
|
///
|
||||||
/// `openlibrary_work` is the binding from [`OpenLibraryClient::gutenberg_work`]:
|
/// `openlibrary_work` is the binding from [`bind`]: the one work Open Library
|
||||||
/// `Some` only when Open Library records exactly one work for this etext.
|
/// records for this etext, or, when it records none, the title match.
|
||||||
fn into_discovery_record(
|
fn into_discovery_record(
|
||||||
book: GutendexBook,
|
book: GutendexBook,
|
||||||
cover_cid: Option<String>,
|
cover_cid: Option<String>,
|
||||||
|
|||||||
@@ -769,7 +769,7 @@ fn etext_of(body: &Value, gutenberg_id: u64) -> Etext {
|
|||||||
pub fn main_title(raw: &str) -> String {
|
pub fn main_title(raw: &str) -> String {
|
||||||
let lower = raw.to_lowercase();
|
let lower = raw.to_lowercase();
|
||||||
let head = lower
|
let head = lower
|
||||||
.split(|c: char| matches!(c, ';' | ':' | '(' | '['))
|
.split([';', ':', '(', '['])
|
||||||
.next()
|
.next()
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let head = head.find(", or").map_or(head, |i| &head[..i]);
|
let head = head.find(", or").map_or(head, |i| &head[..i]);
|
||||||
|
|||||||
Reference in New Issue
Block a user