251 lines
7.2 KiB
Rust
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())
|
|
}
|
|
}
|