use crate::application::loaders::fs; use crate::application::loaders::inotify; use crate::domain::book::Book; use crate::domain::feed::{BooksFeed, Entry}; use crate::domain::repository::{BookFilter, Repository}; use std::path::PathBuf; use std::sync::{Arc, Mutex}; use std::{io, thread}; use url::Url; const AUTHOR_URL_PREFIX: &str = "author"; pub struct Books { pub repo: Arc + 'static>>>, root: PathBuf, base_url: Url, } impl Books { pub fn new( repo: Arc + 'static>>>, root: PathBuf, base_url: String, ) -> Self { Books { repo, root, base_url: Url::parse(&base_url).unwrap(), } } fn book_entries(&self, filter: BookFilter) -> Vec { let mut res = self .repo .lock() .unwrap() .filter(filter) .map(|book| Entry::from(&book)) .collect::>(); for entry in &mut res { for author in &mut entry.author { author.url = self.build_author_url(author.url.as_str()).to_string(); } } res } fn build_author_url(&self, author_url: &str) -> Url { let mut url = self.base_url.clone(); match url.join([AUTHOR_URL_PREFIX, author_url].join("/").as_str()) { Ok(u) => url = u, Err(err) => { println!("{}", err); } } url } pub fn books_feed(&self, filter: BookFilter) -> BooksFeed { let mut feed: BooksFeed = Default::default(); feed.entry = self.book_entries(filter); feed } pub fn add_books_from_path(&mut self) { let books = fs::Loader::new(PathBuf::from(&self.root)) .into_iter() .map(|mut book| {match book.source.strip_prefix(&self.root) { Ok(path) => book.source = path.to_path_buf(), Err(err) => eprintln!("strip source prefix: {}", err) }; book}) .collect(); match self.repo.lock() { Ok(mut repo) => { repo.bulk_add(books); } Err(err) => eprintln!("{}", err), } } pub fn watch_dir(&mut self) -> Result<(), io::Error> { let root = self.root.clone(); let repo = Arc::clone(&self.repo); let mut loader = inotify::Loader::new(root.clone())?; thread::spawn(move || loop { for book in loader.iter() { repo.lock().unwrap().add(book); } }); Ok(()) } }