Files
opds-server/src/infrastructure/repository/inmem/books.rs
2025-09-09 07:39:16 +03:00

251 lines
7.2 KiB
Rust

use crate::domain::repository::{BookFilter, Repository};
use crate::domain::{author, book};
use std::collections::HashMap;
use uuid::Uuid;
#[derive(Clone)]
struct Book {
id: Uuid,
title: String,
author: Vec<String>,
language: String,
description: String,
tags: Vec<String>,
published_at: String,
publisher: String,
updated: String,
}
impl From<book::Book> for Book {
fn from(book: book::Book) -> Self {
Book {
id: book.id,
title: book.title,
author: book.author.iter().map(|a| a.id.to_string()).collect(),
language: book.language,
description: book.description,
tags: book.tags,
published_at: book.published_at,
publisher: book.publisher,
updated: book.updated,
}
}
}
impl Into<book::Book> for Book {
fn into(self) -> book::Book {
book::Book {
id: self.id,
title: self.title,
author: self
.author
.iter()
.map(|a| {
let mut na: author::Author = Default::default();
if let Ok(id) = Uuid::parse_str(a) {
na.id = id;
}
na
})
.collect(),
language: self.language,
description: self.description,
tags: self.tags,
published_at: self.published_at,
publisher: self.publisher,
updated: self.updated,
}
}
}
pub struct BookRepository {
books: Vec<Book>,
authors: HashMap<Uuid, author::Author>,
author_uniques: HashMap<String, Uuid>,
}
impl BookRepository {
pub fn new() -> Self {
BookRepository {
books: vec![],
authors: HashMap::new(),
author_uniques: HashMap::new(),
}
}
fn populate_authors(&self, book: &mut book::Book) {
for a in &mut book.author {
if let Some(stored) = self.authors.get(&a.id) {
a.first_name = stored.first_name.clone();
a.last_name = stored.last_name.clone();
a.middle_name = stored.middle_name.clone();
}
}
}
fn extract_authors(&mut self, item: &mut book::Book) {
for a in item.author.iter_mut() {
let uniq = a.uniq_id().to_string();
if let Some(&id) = self.author_uniques.get(&uniq) {
a.id = id;
} else {
self.author_uniques.insert(uniq, a.id);
}
self.authors.insert(a.id, a.clone());
}
}
}
impl Repository<book::Book, BookFilter> for BookRepository {
fn add(&mut self, mut item: book::Book) {
if self.get(item.id.to_string()).is_some() {
return;
}
self.extract_authors(&mut item);
self.books.push(item.into());
}
fn bulk_add(&mut self, items: Vec<book::Book>) {
items.into_iter().for_each(|item| {
if self.get(item.id.to_string()).is_none() {
self.add(item)
}
});
}
fn remove(&mut self, item: book::Book) {
self.books.retain(|book| book.id != item.id);
}
fn get(&self, id: String) -> Option<book::Book> {
let id_uuid: Uuid = id.parse().unwrap();
let mut book: Option<book::Book> = self
.books
.iter()
.cloned()
.find(|x| x.id.eq(&id_uuid))
.and_then(|x| Some(x.into()));
if book.is_none() {
return None;
}
if let Some(book) = book.as_mut() {
self.populate_authors(book);
}
book
}
fn update(&mut self, mut item: book::Book) {
self.extract_authors(&mut item);
for res in &mut self.books {
if res.id == item.id {
*res = item.into();
break;
}
}
}
fn filter(&self, f: BookFilter) -> Box<dyn Iterator<Item = book::Book>> {
let mut author_ids: Vec<String> = vec![];
let mut use_author = false;
if let Some(author) = f.author {
if let Some(id) = author.id {
author_ids.push(id);
use_author = true;
}
if let Some(name) = author.name {
for (id, author) in self.authors.iter() {
if author.first_name.contains(&name)
|| (author.last_name.is_some()
&& author.clone().last_name.unwrap().contains(&name))
|| (author.middle_name.is_some()
&& author.clone().middle_name.unwrap().contains(&name))
{
author_ids.push(id.to_string());
use_author = true;
}
}
}
}
if author_ids.is_empty() && use_author {
return Box::new(std::iter::empty::<book::Book>())
}
let mut res = self
.books
.iter()
.filter(move |book| {
// Фильтр по названию
if let Some(ref search_title) = f.title {
if !book.title.contains(search_title) {
return false;
}
}
// Фильтр по описанию
if let Some(ref descr) = f.description {
if !book.description.contains(descr) {
return false;
}
}
// Фильтр по языку
if let Some(ref lang) = f.language {
if !book.language.eq(lang) {
return false;
}
}
// Фильтр по тегам
if let Some(ref tags) = f.tags {
if !tags.iter().all(|tag| book.tags.contains(tag)) {
return false;
}
}
// Фильтр по издателю
if let Some(ref publisher) = f.publisher {
if !book.publisher.eq(publisher) {
return false;
}
}
// Фильтр по датам (пример, можно доработать)
if let Some(ref published_at) = f.published_at {
if book.published_at != *published_at {
return false;
}
}
if let Some(ref updated) = f.updated {
if book.updated != *updated {
return false;
}
}
if !author_ids.is_empty() {
if !book.author.iter().all(|x| author_ids.contains(x)) {
return false;
}
}
true
})
.cloned()
.map(Into::into)
.collect::<Vec<book::Book>>();
for book in &mut res {
self.populate_authors(book);
}
Box::new(res.into_iter())
}
}