Skip to content

Commit

Permalink
wip: Edit assoc. authors for Book using combobox
Browse files Browse the repository at this point in the history
  • Loading branch information
phildenhoff committed Jan 31, 2024
1 parent a51e730 commit 74482f9
Show file tree
Hide file tree
Showing 19 changed files with 444 additions and 108 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub trait BookServiceTrait {
fn update(&mut self, id: i32, dto: UpdateBookDto) -> Result<Book, ()>;
fn find_author_ids_by_book_id(&mut self, book_id: i32) -> Result<Vec<i32>, ()>;
fn link_book_to_author(&mut self, book_id: i32, author_id: i32) -> Result<(), ()>;
fn unlink_book_from_author(&mut self, book_id: i32, author_id: i32) -> Result<(), ()>;
}

pub struct BookService {
Expand Down Expand Up @@ -50,4 +51,9 @@ impl BookServiceTrait for BookService {
self.book_repository
.create_book_author_link(book_id, author_id)
}

fn unlink_book_from_author(&mut self, book_id: i32, author_id: i32) -> Result<(), ()> {
self.book_repository
.remove_book_author_link(book_id, author_id)
}
}
10 changes: 9 additions & 1 deletion src-tauri/libcalibre/src/application/services/library/dto.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use std::path::PathBuf;

use crate::application::services::domain::{author::dto::NewAuthorDto, book::dto::NewBookDto};
use crate::application::services::domain::{
author::dto::NewAuthorDto,
book::dto::{NewBookDto, UpdateBookDto},
};

pub struct NewLibraryFileDto {
pub path: PathBuf,
Expand All @@ -9,6 +12,11 @@ pub struct NewLibraryFileDto {
//pub mime_type: String,
}

pub struct UpdateLibraryEntryDto {
pub book: UpdateBookDto,
pub author_id_list: Option<Vec<String>>,
}

pub struct NewLibraryEntryDto {
pub book: NewBookDto,
pub authors: Vec<NewAuthorDto>,
Expand Down
112 changes: 98 additions & 14 deletions src-tauri/libcalibre/src/application/services/library/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::domain::book::aggregate::BookWithAuthorsAndFiles;
use crate::infrastructure::file_service::FileServiceTrait;
use crate::{Book, BookFile};

use super::dto::{NewLibraryEntryDto, NewLibraryFileDto};
use super::dto::{NewLibraryEntryDto, NewLibraryFileDto, UpdateLibraryEntryDto};

#[derive(Debug)]
pub enum LibSrvcError {
Expand Down Expand Up @@ -95,7 +95,10 @@ where
let primary_author = &author_list[0];

for author in &author_list {
let mut book_service = self.book_service.lock().map_err(|_| LibSrvcError::DatabaseLocked)?;
let mut book_service = self
.book_service
.lock()
.map_err(|_| LibSrvcError::DatabaseLocked)?;
book_service
.link_book_to_author(book.id, author.id)
.map_err(|_| LibSrvcError::BookAuthorLinkFailed)?;
Expand All @@ -104,15 +107,21 @@ where
// 2. Create Directories for Author & Book
// ======================================
let author_dir_name = {
let mut author_service = self.author_service.lock().map_err(|_| LibSrvcError::DatabaseLocked)?;
let mut author_service = self
.author_service
.lock()
.map_err(|_| LibSrvcError::DatabaseLocked)?;
author_service.name_author_dir(primary_author)
};

let book_dir_name = gen_book_folder_name(&dto.book.title, book.id);
let book_dir_relative_path = Path::new(&author_dir_name).join(&book_dir_name);

{
let file_service = self.file_service.lock().map_err(|_| LibSrvcError::DatabaseLocked)?;
let file_service = self
.file_service
.lock()
.map_err(|_| LibSrvcError::DatabaseLocked)?;
file_service.create_directory(Path::new(&author_dir_name).to_path_buf())?;
file_service.create_directory(book_dir_relative_path.clone())?;
}
Expand All @@ -139,8 +148,14 @@ where
// If a Cover Image exists, copy it to library
let primary_file = &files[0];
{
let mut bfs = self.book_file_service.lock().map_err(|_| LibSrvcError::DatabaseLocked)?;
let fs = self.file_service.lock().map_err(|_| LibSrvcError::DatabaseLocked)?;
let mut bfs = self
.book_file_service
.lock()
.map_err(|_| LibSrvcError::DatabaseLocked)?;
let fs = self
.file_service
.lock()
.map_err(|_| LibSrvcError::DatabaseLocked)?;

let cover_data = bfs.cover_img_data_from_path(primary_file.path.as_path())?;
if let Some(cover_data) = cover_data {
Expand Down Expand Up @@ -173,8 +188,13 @@ where
&mut self,
id: i32,
) -> Result<BookWithAuthorsAndFiles, Box<dyn Error>> {
let mut book_service = self.book_service.lock().map_err(|_| LibSrvcError::DatabaseLocked)?;
let book = book_service.find_by_id(id).map_err(|_| LibSrvcError::NotFoundBook)?;
let mut book_service = self
.book_service
.lock()
.map_err(|_| LibSrvcError::DatabaseLocked)?;
let book = book_service
.find_by_id(id)
.map_err(|_| LibSrvcError::NotFoundBook)?;

let author_ids = book_service
.find_author_ids_by_book_id(id)
Expand All @@ -183,7 +203,10 @@ where
let authors: Vec<Author> = author_ids
.into_iter()
.map(|author_id| {
let mut author_service = self.author_service.lock().map_err(|_| LibSrvcError::DatabaseLocked)?;
let mut author_service = self
.author_service
.lock()
.map_err(|_| LibSrvcError::DatabaseLocked)?;
match author_service.find_by_id(author_id) {
Ok(Some(author)) => Ok(author),
_ => Err(LibSrvcError::NotFoundAuthor),
Expand All @@ -208,7 +231,10 @@ where
}

pub fn find_all(&mut self) -> Result<Vec<BookWithAuthorsAndFiles>, Box<dyn Error>> {
let mut book_service = self.book_service.lock().map_err(|_| LibSrvcError::DatabaseLocked)?;
let mut book_service = self
.book_service
.lock()
.map_err(|_| LibSrvcError::DatabaseLocked)?;
let books = book_service
.all()
.map_err(|_| LibSrvcError::DatabaseBookWriteFailed)?;
Expand All @@ -222,7 +248,10 @@ where
let authors: Vec<Author> = author_ids
.into_iter()
.map(|author_id| {
let mut author_service = self.author_service.lock().map_err(|_| LibSrvcError::DatabaseLocked)?;
let mut author_service = self
.author_service
.lock()
.map_err(|_| LibSrvcError::DatabaseLocked)?;
match author_service.find_by_id(author_id) {
Ok(Some(author)) => Ok(author),
_ => Err(LibSrvcError::NotFoundAuthor),
Expand All @@ -249,14 +278,62 @@ where
Ok(book_list)
}

pub fn update(
&mut self,
book_id: i32,
dto: UpdateLibraryEntryDto,
) -> Result<BookWithAuthorsAndFiles, Box<dyn Error>> {
{
let mut book_service = self
.book_service
.lock()
.map_err(|_| LibSrvcError::DatabaseLocked)?;
book_service
.update(book_id, dto.book)
.map_err(|_| LibSrvcError::DatabaseBookWriteFailed);

let authors = book_service
.find_author_ids_by_book_id(book_id)
.map_err(|_| LibSrvcError::NotFoundBook)?;
authors
.iter()
.map(|&author_id| {
book_service
.unlink_book_from_author(book_id, author_id)
.map_err(|_| LibSrvcError::BookAuthorLinkFailed)
})
.collect::<Result<(), LibSrvcError>>()?;

match dto.author_id_list {
Some(author_id_list) => {
author_id_list
.iter()
.map(|author_id| {
let author_id_int = author_id.parse::<i32>().unwrap();
book_service
.link_book_to_author(book_id, author_id_int)
.map_err(|_| LibSrvcError::BookAuthorLinkFailed)
})
.collect::<Result<(), LibSrvcError>>()?;
}
None => {}
}
}

self.find_book_with_authors(book_id)
}

fn create_authors(
&mut self,
authors: Vec<NewAuthorDto>,
) -> Result<Vec<Author>, Box<dyn Error>> {
let author_list = authors
.into_iter()
.map(|dto| {
let mut author_service = self.author_service.lock().map_err(|_| LibSrvcError::DatabaseLocked)?;
let mut author_service = self
.author_service
.lock()
.map_err(|_| LibSrvcError::DatabaseLocked)?;
author_service
.create(dto)
.map_err(|_| LibSrvcError::DatabaseAuthorWriteFailed)
Expand Down Expand Up @@ -310,8 +387,15 @@ where
added_book
}

fn set_book_path(&self, book_id: i32, book_dir_rel_path: PathBuf) -> Result<Book, LibSrvcError> {
let mut book_service = self.book_service.lock().map_err(|_| LibSrvcError::DatabaseLocked)?;
fn set_book_path(
&self,
book_id: i32,
book_dir_rel_path: PathBuf,
) -> Result<Book, LibSrvcError> {
let mut book_service = self
.book_service
.lock()
.map_err(|_| LibSrvcError::DatabaseLocked)?;
book_service
.update(
book_id,
Expand Down
2 changes: 2 additions & 0 deletions src-tauri/libcalibre/src/domain/book/repository.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ pub trait Repository {
fn create(&mut self, book: &NewBook) -> Result<Book, ()>;
/// Link this book to an author
fn create_book_author_link(&mut self, book_id: i32, author_id: i32) -> Result<(), ()>;
/// Unlink this book from an author
fn remove_book_author_link(&mut self, book_id: i32, author_id: i32) -> Result<(), ()>;
/// Find one book by ID.
fn find_by_id(&mut self, id: i32) -> Result<Book, ()>;
/// Find the IDs of all authors for a book
Expand Down
13 changes: 13 additions & 0 deletions src-tauri/libcalibre/src/infrastructure/domain/book/repository.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,19 @@ impl Repository for BookRepository {
}
}

fn remove_book_author_link(&mut self, book_id: i32, author_id: i32) -> Result<(), ()> {
use crate::schema::books_authors_link::dsl::*;

let link =
diesel::delete(books_authors_link.filter(book.eq(book_id).and(author.eq(author_id))))
.execute(&mut self.connection);

match link {
Ok(_) => Ok(()),
Err(_) => Err(()),
}
}

fn find_author_ids_by_book_id(&mut self, book_id: i32) -> Result<Vec<i32>, ()> {
use crate::schema::books_authors_link::dsl::*;

Expand Down
1 change: 1 addition & 0 deletions src-tauri/libcalibre/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ mod persistence;
mod schema;
pub mod util;

pub use domain::author::entity::Author;
pub use domain::book::entity::Book;
pub use domain::book_file::entity::BookFile;
pub use domain::book::aggregate::BookWithAuthorsAndFiles;
10 changes: 6 additions & 4 deletions src-tauri/src/book.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ pub enum BookFile {

#[derive(Serialize, specta::Type, Deserialize, Clone)]
pub struct LibraryBook {
pub title: String,
pub author_list: Vec<String>,
pub id: String,
pub uuid: Option<String>,
pub title: String,
pub author_list: Vec<LibraryAuthor>,

pub sortable_title: Option<String>,
pub author_sort_lookup: Option<HashMap<String, String>>,
Expand All @@ -50,9 +50,11 @@ pub struct LibraryBook {
pub cover_image: Option<LocalOrRemoteUrl>,
}

#[derive(Serialize, specta::Type)]
#[derive(Serialize, specta::Type, Deserialize, Clone)]
pub struct LibraryAuthor {
// Define the fields of LibraryAuthor struct here
pub id: String,
pub name: String,
pub sortable_name: String,
}

#[derive(Serialize, Deserialize, specta::Type)]
Expand Down
37 changes: 37 additions & 0 deletions src-tauri/src/libs/calibre/author.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use std::sync::{Arc, Mutex};

use libcalibre::{
application::services::domain::author::service::{AuthorService, AuthorServiceTrait},
infrastructure::domain::author::repository::AuthorRepository,
};

use crate::book::LibraryAuthor;

impl From<&libcalibre::Author> for LibraryAuthor {
fn from(author: &libcalibre::Author) -> Self {
LibraryAuthor {
id: author.id.to_string(),
name: author.name.clone(),
sortable_name: author.sort.clone().unwrap_or("".to_string()),
}
}
}

pub fn list_all(library_root: String) -> Vec<LibraryAuthor> {
let database_path = libcalibre::util::get_db_path(&library_root);
match database_path {
None => vec![],
Some(database_path) => {
let author_repo = Box::new(AuthorRepository::new(&database_path));
let author_service = Arc::new(Mutex::new(AuthorService::new(author_repo)));

{
let mut guarded_as = author_service.lock().unwrap();
match guarded_as.all() {
Ok(authors) => authors.iter().map(|a| LibraryAuthor::from(a)).collect(),
Err(_) => vec![],
}
}
}
}
}
19 changes: 10 additions & 9 deletions src-tauri/src/libs/calibre/book.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,38 +12,39 @@ use libcalibre::{
file::service::{BookFileService, BookFileServiceTrait},
},
library::service::LibraryService,
},
infrastructure::{
}, infrastructure::{
domain::{
author::repository::AuthorRepository, book::repository::BookRepository,
book_file::repository::BookFileRepository,
},
file_service::FileServiceTrait,
},
}, Author
};

use crate::{
book::{BookFile, LibraryBook, LocalFile, LocalOrRemote, LocalOrRemoteUrl},
book::{BookFile, LibraryAuthor, LibraryBook, LocalFile, LocalOrRemote, LocalOrRemoteUrl},
libs::util,
};

fn to_library_book(
library_path: &String,
book: &libcalibre::Book,
author_names: Vec<String>,
author_list: Vec<Author>,
file_list: Vec<libcalibre::BookFile>,
) -> LibraryBook {
LibraryBook {
title: book.title.clone(),
author_list: author_names.clone(),
author_list: author_list.iter().map(|a| {
LibraryAuthor::from(a)
}).collect(),
id: book.id.to_string(),
uuid: book.uuid.clone(),

sortable_title: book.sort.clone(),
author_sort_lookup: Some(
author_names
author_list
.iter()
.map(|a| (a.clone(), a.clone()))
.map(|a| (a.name.clone(), a.sortable_name()))
.collect::<HashMap<_, _>>(),
),

Expand Down Expand Up @@ -119,7 +120,7 @@ pub fn list_all(library_root: String) -> Vec<LibraryBook> {
let mut calibre_book = to_library_book(
&library_root,
&b.book,
b.authors.iter().map(|a| a.name.clone()).collect(),
b.authors.clone(),
b.files.clone(),
);
calibre_book.cover_image = book_cover_image(&library_root, &b.book);
Expand Down
Loading

0 comments on commit 74482f9

Please sign in to comment.