Skip to content

Commit

Permalink
Add support for removing documents
Browse files Browse the repository at this point in the history
Fixes #138.
  • Loading branch information
baskerville committed Sep 20, 2020
1 parent dbb6a6c commit 1a0405f
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
/.metadata.json
/.reading-states
/.fat32-epoch
/.trash
/css/*-user.css
/hyphenation-patterns
/dictionaries
Expand Down
93 changes: 93 additions & 0 deletions src/library.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use indexmap::IndexMap;
use fxhash::{FxHashMap, FxHashSet, FxBuildHasher};
use chrono::{Local, TimeZone};
use filetime::{FileTime, set_file_handle_times};
use anyhow::{Error, format_err};
use crate::metadata::{Info, ReaderInfo, FileInfo, SimpleStatus, SortMethod};
use crate::metadata::{sort, sorter, extract_metadata_from_epub};
use crate::settings::{LibraryMode, ImportSettings};
Expand Down Expand Up @@ -320,6 +321,98 @@ impl Library {
self.has_db_changed = true;
}

pub fn remove<P: AsRef<Path>>(&mut self, path: P) -> Result<(), Error> {
let full_path = self.home.join(path.as_ref());

let fp = self.paths.get(path.as_ref()).cloned().or_else(|| {
full_path.metadata().ok()
.and_then(|md| md.fingerprint(self.fat32_epoch).ok())
}).ok_or_else(|| format_err!("Can't get fingerprint of {}.", path.as_ref().display()))?;

if full_path.exists() {
fs::remove_file(&full_path)?;
if let Some(parent) = full_path.parent() {
if parent != self.home {
fs::remove_dir(parent).ok();
}
}
}

let rsp = self.reading_state_path(fp);
if rsp.exists() {
fs::remove_file(rsp)?;
}

if self.mode == LibraryMode::Database {
self.paths.remove(path.as_ref());
if self.db.shift_remove(&fp).is_some() {
self.has_db_changed = true;
}
} else {
self.reading_states.remove(&fp);
}

self.modified_reading_states.remove(&fp);

Ok(())
}

pub fn move_to<P: AsRef<Path>>(&mut self, path: P, other: &mut Library) -> Result<(), Error> {
if !self.home.join(path.as_ref()).exists() {
return Err(format_err!("Can't move non-existing file {}.", path.as_ref().display()));
}

let fp = self.paths.get(path.as_ref()).cloned().or_else(|| {
self.home.join(path.as_ref())
.metadata().ok()
.and_then(|md| md.fingerprint(self.fat32_epoch).ok())
}).ok_or_else(|| format_err!("Can't get fingerprint of {}.", path.as_ref().display()))?;

let src = self.home.join(path.as_ref());
let mut dest = other.home.join(path.as_ref());
if let Some(parent) = dest.parent() {
fs::create_dir_all(parent)?;
}

if dest.exists() {
let prefix = Local::now().format("%Y%m%d_%H%M%S ");
let name = dest.file_name().and_then(|name| name.to_str())
.map(|name| prefix.to_string() + name)
.ok_or_else(|| format_err!("Can't compute new name for {}.", dest.display()))?;
dest.set_file_name(name);
}

fs::rename(&src, &dest)?;

let rsp_src = self.reading_state_path(fp);
if rsp_src.exists() {
let rsp_dest = other.reading_state_path(fp);
fs::rename(&rsp_src, &rsp_dest)?;
}

if self.mode == LibraryMode::Database {
if let Some(mut info) = self.db.shift_remove(&fp) {
let dest_path = dest.strip_prefix(&other.home)?;
info.file.path = dest_path.to_path_buf();
other.db.insert(fp, info);
self.paths.remove(path.as_ref());
other.paths.insert(dest_path.to_path_buf(), fp);
self.has_db_changed = true;
other.has_db_changed = true;
}
} else {
if let Some(reader_info) = self.reading_states.remove(&fp) {
other.reading_states.insert(fp, reader_info);
}
}

if self.modified_reading_states.remove(&fp) {
other.modified_reading_states.insert(fp);
}

Ok(())
}

pub fn clean_up(&mut self) {
if self.mode == LibraryMode::Database {
let home = &self.home;
Expand Down
2 changes: 2 additions & 0 deletions src/settings/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ pub struct HomeSettings {
pub address_bar: bool,
pub navigation_bar: bool,
pub max_levels: usize,
pub max_trash_size: u64,
}


Expand Down Expand Up @@ -303,6 +304,7 @@ impl Default for HomeSettings {
address_bar: false,
navigation_bar: true,
max_levels: 3,
max_trash_size: 32 * (1 << 20),
}
}
}
Expand Down
66 changes: 66 additions & 0 deletions src/view/home/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ mod shelf;
mod book;
mod bottom_bar;

use std::fs;
use std::mem;
use std::thread;
use std::path::{Path, PathBuf};
Expand Down Expand Up @@ -48,6 +49,8 @@ use crate::color::BLACK;
use crate::font::Fonts;
use crate::app::Context;

pub const TRASH_DIRNAME: &str = ".trash";

#[derive(Debug)]
pub struct Home {
id: Id,
Expand Down Expand Up @@ -855,6 +858,23 @@ impl Home {
entries.push(EntryKind::SubMenu("Set As".to_string(), submenu))
}

entries.push(EntryKind::Separator);
let selected_library = context.settings.selected_library;
let libraries = context.settings.libraries.iter().enumerate()
.filter(|(index, _)| *index != selected_library)
.map(|(index, lib)| {
EntryKind::Command(lib.name.clone(),
EntryId::MoveTo(path.clone(), index))
}).collect::<Vec<EntryKind>>();
if !libraries.is_empty() {
entries.push(EntryKind::SubMenu("Move To".to_string(), libraries));

}

entries.push(EntryKind::Command("Remove".to_string(),
EntryId::Remove(path.clone())));


let book_menu = Menu::new(rect, ViewId::BookMenu, MenuKind::Contextual, entries, context);
rq.add(RenderData::new(book_menu.id(), *book_menu.rect(), UpdateMode::Gui));
self.children.push(Box::new(book_menu) as Box<dyn View>);
Expand Down Expand Up @@ -956,6 +976,40 @@ impl Home {
self.refresh_visibles(true, false, rq, context);
}

fn remove(&mut self, path: &Path, rq: &mut RenderQueue, context: &mut Context) -> Result<(), Error> {
let trash_path = context.library.home.join(TRASH_DIRNAME);
if !trash_path.is_dir() {
fs::create_dir_all(&trash_path)?;
}
let mut trash = Library::new(trash_path, LibraryMode::Database);
context.library.move_to(path, &mut trash)?;
let (mut files, _) = trash.list(&trash.home, None, false);
let mut size = files.iter().map(|info| info.file.size).sum::<u64>();
if size > context.settings.home.max_trash_size {
sort(&mut files, SortMethod::Added, true);
while size > context.settings.home.max_trash_size {
let info = files.pop().unwrap();
if let Err(e) = trash.remove(&info.file.path) {
eprintln!("{}", e);
break;
}
size -= info.file.size;
}
}
trash.flush();
self.refresh_visibles(true, false, rq, context);
Ok(())
}

fn move_to(&mut self, path: &Path, index: usize, rq: &mut RenderQueue, context: &mut Context) -> Result<(), Error> {
let library_settings = &context.settings.libraries[index];
let mut library = Library::new(&library_settings.path, library_settings.mode);
context.library.move_to(path, &mut library)?;
library.flush();
self.refresh_visibles(true, false, rq, context);
Ok(())
}

fn set_reverse_order(&mut self, value: bool, rq: &mut RenderQueue, context: &mut Context) {
self.reverse_order = value;
self.current_page = 0;
Expand Down Expand Up @@ -1374,6 +1428,18 @@ impl View for Home {
}
true
},
Event::Select(EntryId::Remove(ref path)) => {
self.remove(path, rq, context)
.map_err(|e| eprintln!("{}", e))
.ok();
true
},
Event::Select(EntryId::MoveTo(ref path, index)) => {
self.move_to(path, index, rq, context)
.map_err(|e| eprintln!("{}", e))
.ok();
true
},
Event::Select(EntryId::ToggleShowHidden) => {
context.library.show_hidden = !context.library.show_hidden;
self.refresh_visibles(true, false, rq, context);
Expand Down
2 changes: 2 additions & 0 deletions src/view/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,8 @@ pub enum EntryId {
CleanUp,
Sort(SortMethod),
ReverseOrder,
Remove(PathBuf),
MoveTo(PathBuf, usize),
AddDirectory(PathBuf),
SelectDirectory(PathBuf),
ToggleSelectDirectory(PathBuf),
Expand Down

0 comments on commit 1a0405f

Please sign in to comment.