summaryrefslogtreecommitdiffstats
path: root/src/commands/delete_files.rs
blob: 4c698dcfe9d39574793f812509a90024207470c9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
use std::fs;
use std::path;

use termion::event::Key;

use crate::context::AppContext;
use crate::history::DirectoryHistory;
use crate::ui::widgets::TuiPrompt;
use crate::ui::TuiBackend;

use super::reload;

fn trash_error_to_io_error(err: trash::Error) -> std::io::Error {
    match err {
        trash::Error::Unknown => std::io::Error::new(std::io::ErrorKind::Other, "Unknown Error"),
        trash::Error::TargetedRoot => {
            std::io::Error::new(std::io::ErrorKind::Other, "Targeted Root")
        }
        trash::Error::CanonicalizePath { code: _ } => {
            std::io::Error::new(std::io::ErrorKind::NotFound, "Not found")
        }
        trash::Error::Remove { code: Some(1) } => std::io::Error::new(
            std::io::ErrorKind::InvalidData,
            "Cannot move files to trash from mounted system",
        ),
        _ => std::io::Error::new(std::io::ErrorKind::Other, "Unknown Error"),
    }
}

pub fn remove_files<P>(paths: &[P]) -> std::io::Result<()>
where
    P: AsRef<path::Path>,
{
    for path in paths {
        if let Ok(metadata) = fs::symlink_metadata(path) {
            if metadata.is_dir() {
                fs::remove_dir_all(&path)?;
            } else {
                fs::remove_file(&path)?;
            }
        }
    }
    Ok(())
}

pub fn trash_files<P>(paths: &[P]) -> std::io::Result<()>
where
    P: AsRef<path::Path>,
{
    for path in paths {
        if let Err(e) = trash::delete(path) {
            return Err(trash_error_to_io_error(e));
        }
    }
    Ok(())
}

fn delete_files(context: &mut AppContext, backend: &mut TuiBackend) -> std::io::Result<()> {
    let delete_func = if context.config_ref().use_trash {
        trash_files
    } else {
        remove_files
    };

    let tab_index = context.tab_context_ref().index;
    let paths = context
        .tab_context_ref()
        .curr_tab_ref()
        .curr_list_ref()
        .map(|s| s.get_selected_paths())
        .unwrap_or(vec![]);
    let paths_len = paths.len();
    if paths_len == 0 {
        return Err(std::io::Error::new(
            std::io::ErrorKind::Other,
            "no files selected",
        ));
    }

    let ch = {
        let prompt_str = format!("Delete {} files? (Y/n)", paths_len);
        let mut prompt = TuiPrompt::new(&prompt_str);
        prompt.get_key(backend, context)
    };

    match ch {
        Key::Char('y') | Key::Char('\n') => {
            let confirm_delete = if paths_len > 1 {
                // prompt user again for deleting multiple files
                let ch = {
                    let prompt_str = "Are you sure? (y/N)";
                    let mut prompt = TuiPrompt::new(prompt_str);
                    prompt.get_key(backend, context)
                };
                ch == Key::Char('y')
            } else {
                true
            };
            if confirm_delete {
                delete_func(&paths)?;

                // remove directory previews
                for tab in context.tab_context_mut().iter_mut() {
                    for p in &paths {
                        tab.history_mut().remove(p.as_path());
                    }
                }
                reload::reload(context, tab_index)?;
                let msg = format!("Deleted {} files", paths_len);
                context.push_msg(msg);
            }
            Ok(())
        }
        _ => Ok(()),
    }
}

pub fn delete_selected_files(
    context: &mut AppContext,
    backend: &mut TuiBackend,
) -> std::io::Result<()> {
    let res = delete_files(context, backend)?;

    let options = context.config_ref().display_options_ref().clone();
    let curr_path = context.tab_context_ref().curr_tab_ref().cwd().to_path_buf();
    for tab in context.tab_context_mut().iter_mut() {
        tab.history_mut().reload(&curr_path, &options)?;
    }
    Ok(())
}