1use std::fmt::Formatter;
2use std::fs::Metadata;
3use std::time::SystemTime;
4
5use strum::{EnumIter, IntoEnumIterator};
6
7use crate::event::ActionMap;
8use crate::io::Opener;
9use crate::modes::{extract_datetime, ExtensionKind, FileInfo};
10use crate::{impl_content, impl_draw_menu_with_char, impl_selectable};
11
12const CONTEXT_ACTIONS: [(&str, ActionMap); 10] = [
13 ("Open", ActionMap::OpenFile),
14 ("Open with", ActionMap::Exec),
15 ("Open in Neovim", ActionMap::NvimFilepicker),
16 ("Flag", ActionMap::ToggleFlag),
17 ("Rename", ActionMap::Rename),
18 ("Delete", ActionMap::Delete),
19 ("Trash", ActionMap::TrashMoveFile),
20 ("Chmod", ActionMap::Chmod),
21 ("New File", ActionMap::NewFile),
22 ("New Directory", ActionMap::NewDir),
23];
24
25#[derive(Default)]
28pub struct ContextMenu {
29 pub content: Vec<&'static str>,
30 index: usize,
31 actions: Vec<&'static ActionMap>,
32}
33
34impl ContextMenu {
35 pub fn setup(&mut self) {
36 self.content = CONTEXT_ACTIONS.iter().map(|(s, _)| *s).collect();
37 self.actions = CONTEXT_ACTIONS.iter().map(|(_, a)| a).collect();
38 }
39
40 pub fn matcher(&self) -> &ActionMap {
41 self.actions[self.index]
42 }
43
44 pub fn reset(&mut self) {
45 self.index = 0;
46 }
47}
48
49type StaticStr = &'static str;
50
51impl_content!(ContextMenu, StaticStr);
52impl_draw_menu_with_char!(ContextMenu, StaticStr);
53
54pub struct MoreInfos<'a> {
56 file_info: &'a FileInfo,
57 opener: &'a Opener,
58}
59
60impl<'a> MoreInfos<'a> {
61 pub fn new(file_info: &'a FileInfo, opener: &'a Opener) -> Self {
62 Self { file_info, opener }
63 }
64
65 pub fn to_lines(&self) -> [String; 8] {
67 let mut times = self.system_times();
68 [
69 self.file_path(),
70 self.owner_group(),
71 self.perms(),
72 self.size_inode(),
73 std::mem::take(&mut times[0]),
74 std::mem::take(&mut times[1]),
75 std::mem::take(&mut times[2]),
76 self.kind_opener(),
77 ]
78 }
79
80 fn file_path(&self) -> String {
81 let mut ret = format!("Filepath: {path}", path = self.file_info.path.display());
82 if self.file_info.is_symlink() {
83 self.file_info.expand_symlink(&mut ret);
84 }
85 ret
86 }
87
88 fn owner_group(&self) -> String {
89 format!(
90 "Owner/Group: {owner} / {group}",
91 owner = self.file_info.owner,
92 group = self.file_info.group
93 )
94 }
95
96 fn perms(&self) -> String {
97 if let Ok(perms) = self.file_info.permissions() {
98 format!(
99 "Permissions: {dir_symbol}{perms}",
100 dir_symbol = self.file_info.dir_symbol()
101 )
102 } else {
103 "".to_owned()
104 }
105 }
106
107 fn size_inode(&self) -> String {
108 format!(
109 "{size_kind} {size} / Inode: {inode}",
110 size_kind = self.file_info.file_kind.size_description(),
111 size = self.file_info.size_column.trimed(),
112 inode = self.file_info.ino()
113 )
114 }
115
116 fn kind_opener(&self) -> String {
117 if self.file_info.file_kind.is_normal_file() {
118 let ext_kind = ExtensionKind::matcher(&self.file_info.extension.to_lowercase());
119 if let Some(opener) = self.opener.kind(&self.file_info.path) {
120 format!("Opener: {opener}, Previewer: {ext_kind}")
121 } else {
122 format!("Previewer: {ext_kind}")
123 }
124 } else {
125 let kind = self.file_info.file_kind.long_description();
126 format!("Kind: {kind}")
127 }
128 }
129
130 fn system_times(&self) -> Vec<String> {
131 let Ok(metadata) = &self.file_info.metadata() else {
132 return vec!["".to_owned(), "".to_owned(), "".to_owned()];
133 };
134 TimeKind::iter()
135 .map(|time_kind| time_kind.format_time(metadata))
136 .collect()
137 }
138}
139
140#[derive(EnumIter)]
141enum TimeKind {
142 Modified,
143 Created,
144 Accessed,
145}
146
147impl TimeKind {
148 fn read_time(&self, metadata: &Metadata) -> Result<SystemTime, std::io::Error> {
149 match self {
150 Self::Modified => metadata.modified(),
151 Self::Created => metadata.created(),
152 Self::Accessed => metadata.accessed(),
153 }
154 }
155
156 fn format_time(&self, metadata: &Metadata) -> String {
157 let Ok(dt) = self.read_time(metadata) else {
158 return "".to_owned();
159 };
160 let formated_time = extract_datetime(dt).unwrap_or_default();
161 format!("{self}{formated_time}")
162 }
163}
164
165impl std::fmt::Display for TimeKind {
166 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
167 match self {
168 Self::Modified => write!(f, "Modified: ",),
169 Self::Created => write!(f, "Created: ",),
170 Self::Accessed => write!(f, "Assessed: "),
171 }
172 }
173}