feat: BNF API instead of openlibrary to expand the available book search

This commit is contained in:
Ninjdai 2025-08-04 13:15:15 +02:00
parent c36a38cd7a
commit aff6c429ce
4 changed files with 121 additions and 0 deletions

108
src/utils/bnf.rs Normal file
View file

@ -0,0 +1,108 @@
use reqwest::StatusCode;
use sea_orm::ActiveValue::{NotSet, Set};
use serde::Deserialize;
use serde_json::Value;
use crate::entities::book;
#[derive(Deserialize)]
pub struct Response {
#[serde(rename = "numberOfRecords")]
records_number: u32,
#[serde(rename = "records")]
records: Vec<RecordListElement>
}
#[derive(Deserialize)]
pub struct RecordListElement {
#[serde(rename = "record")]
record: Record
}
#[derive(Deserialize)]
pub struct Record {
#[serde(rename = "recordData")]
record_data: RecordData
}
#[derive(Deserialize)]
pub struct RecordData {
#[serde(rename = "record")]
record: RecordDataFields
}
#[derive(Deserialize)]
pub struct RecordDataFields {
#[serde(rename = "datafield")]
datafields: Vec<DataField>
}
#[derive(Deserialize)]
pub struct DataField {
#[serde(rename = "@tag")]
tag: String,
#[serde(rename = "subfield")]
subfields: Vec<SubField>
}
#[derive(Deserialize, Clone)]
pub struct SubField {
#[serde(rename = "@code")]
code: String,
#[serde(rename = "$text")]
value: String
}
pub struct FetchedBook {
pub ean: String,
pub title: String,
pub author: String
}
impl FetchedBook {
pub fn to_active_model(&self) -> book::ActiveModel {
book::ActiveModel {
id: NotSet,
ean: Set(self.ean.clone()),
title: Set(self.title.clone()),
author: Set(self.author.clone())
}
}
}
pub async fn fetch_book_by_ean(web_client: &reqwest::Client, ean: &String) -> Option<FetchedBook> {
let body = web_client.execute(
web_client.get(format!("https://catalogue.bnf.fr/api/SRU?version=1.2&operation=searchRetrieve&query=bib.isbn any \"{ean}\""))
.build()
.expect("get request creation failed")
).await.unwrap();
match body.status() {
StatusCode::OK => {
let res = body.text().await.unwrap().replace("\n", "");
log::debug!(target: "api", "BNF book fetch result: {res:#?}");
let der: Result<Response, quick_xml::DeError> = quick_xml::de::from_str(&res);
match der {
Ok(v) => {
if v.records_number == 0 {
log::debug!(target: "api", "BNF returned 0 records for fetch");
return None;
}
let data_dubfield = v.records.first().unwrap().record_data.record.record.datafields.iter().find(|d| d.tag == "200").unwrap().subfields.clone();
Some(FetchedBook {
ean: ean.to_string(),
title: data_dubfield.iter().find(|p| p.code == "a").unwrap().value.clone(),
author: data_dubfield.iter().find(|p| p.code == "f").unwrap().value.clone(),
})
},
Err(e) => {
log::debug!(target: "api", "Error while deserializing: {e:?}");
None
}
}
},
_ => None
}
}
//https://catalogue.bnf.fr/api/SRU?version=1.2&operation=searchRetrieve&query=bib.isbn%20any%20%{ean}%22

View file

@ -1,4 +1,5 @@
pub mod auth;
pub mod bnf;
pub mod cli;
pub mod events;
pub mod open_library;