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_selectable!(ContextMenu);
52impl_content!(ContextMenu, StaticStr);
53impl_draw_menu_with_char!(ContextMenu, StaticStr);
54
55pub struct MoreInfos<'a> {
57 file_info: &'a FileInfo,
58 opener: &'a Opener,
59}
60
61impl<'a> MoreInfos<'a> {
62 pub fn new(file_info: &'a FileInfo, opener: &'a Opener) -> Self {
63 Self { file_info, opener }
64 }
65
66 pub fn to_lines(&self) -> [String; 7] {
68 let mut times = self.system_times();
69 [
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 owner_group(&self) -> String {
81 format!(
82 "Owner/Group: {owner} / {group}",
83 owner = self.file_info.owner,
84 group = self.file_info.group
85 )
86 }
87
88 fn perms(&self) -> String {
89 if let Ok(perms) = self.file_info.permissions() {
90 format!(
91 "Permissions: {dir_symbol}{perms}",
92 dir_symbol = self.file_info.dir_symbol()
93 )
94 } else {
95 "".to_owned()
96 }
97 }
98
99 fn size_inode(&self) -> String {
100 format!(
101 "{size_kind} {size} / Inode: {inode}",
102 size_kind = self.file_info.file_kind.size_description(),
103 size = self.file_info.size_column.trimed(),
104 inode = self.file_info.ino()
105 )
106 }
107
108 fn kind_opener(&self) -> String {
109 if self.file_info.file_kind.is_normal_file() {
110 let ext_kind = ExtensionKind::matcher(&self.file_info.extension.to_lowercase());
111 if let Some(opener) = self.opener.kind(&self.file_info.path) {
112 format!("Opener: {opener}, Previewer: {ext_kind}")
113 } else {
114 format!("Previewer: {ext_kind}")
115 }
116 } else {
117 let kind = self.file_info.file_kind.long_description();
118 format!("Kind: {kind}")
119 }
120 }
121
122 fn system_times(&self) -> Vec<String> {
123 let Ok(metadata) = &self.file_info.metadata() else {
124 return vec!["".to_owned(), "".to_owned(), "".to_owned()];
125 };
126 TimeKind::iter()
127 .map(|time_kind| time_kind.format_time(metadata))
128 .collect()
129 }
130}
131
132#[derive(EnumIter)]
133enum TimeKind {
134 Modified,
135 Created,
136 Accessed,
137}
138
139impl TimeKind {
140 fn read_time(&self, metadata: &Metadata) -> Result<SystemTime, std::io::Error> {
141 match self {
142 Self::Modified => metadata.modified(),
143 Self::Created => metadata.created(),
144 Self::Accessed => metadata.accessed(),
145 }
146 }
147
148 fn format_time(&self, metadata: &Metadata) -> String {
149 let Ok(dt) = self.read_time(metadata) else {
150 return "".to_owned();
151 };
152 let formated_time = extract_datetime(dt).unwrap_or_default();
153 format!("{self}{formated_time}")
154 }
155}
156
157impl std::fmt::Display for TimeKind {
158 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
159 match self {
160 Self::Modified => write!(f, "Modified: ",),
161 Self::Created => write!(f, "Created: ",),
162 Self::Accessed => write!(f, "Assessed: "),
163 }
164 }
165}