fm/modes/menu/
shortcut.rs1use std::borrow::Borrow;
2use std::path::{Path, PathBuf};
4use std::str::FromStr;
5
6use crate::common::{
7 current_uid, path_to_config_folder, tilde, HARDCODED_SHORTCUTS, TRASH_FOLDER_FILES,
8};
9use crate::io::git_root;
11use crate::{impl_content, impl_draw_menu_with_char, impl_selectable, log_info};
12
13#[derive(Debug, Clone)]
16pub struct Shortcut {
17 pub content: Vec<PathBuf>,
20 pub index: usize,
22 start_folder: PathBuf,
23}
24
25impl Shortcut {
26 #[must_use]
29 pub fn empty(start_folder: &Path) -> Self {
30 let content = vec![];
31 Self {
32 content,
33 index: 0,
34 start_folder: start_folder.to_owned(),
35 }
36 }
37
38 fn build_content(start_folder: &Path) -> Vec<PathBuf> {
39 let mut content = Self::hardcoded_shortcuts();
40 Self::push_home_path(&mut content);
41 Self::push_trash_folder(&mut content);
42 Self::push_config_folder(&mut content);
43 Self::push_start_folder(&mut content, start_folder);
44 Self::push_git_root(&mut content);
45 content
46 }
47
48 pub fn update(&mut self) {
49 self.content = Self::build_content(&self.start_folder)
50 }
51
52 fn hardcoded_shortcuts() -> Vec<PathBuf> {
53 HARDCODED_SHORTCUTS.iter().map(PathBuf::from).collect()
54 }
55
56 fn push_home_path(shortcuts: &mut Vec<PathBuf>) {
58 let Ok(home_path) = PathBuf::from_str(tilde("~").borrow());
59 shortcuts.push(home_path);
60 }
61
62 fn push_trash_folder(shortcuts: &mut Vec<PathBuf>) {
64 let Ok(trash_path) = PathBuf::from_str(tilde(TRASH_FOLDER_FILES).borrow());
65 if trash_path.exists() {
66 shortcuts.push(trash_path);
67 }
68 }
69
70 fn push_config_folder(shortcuts: &mut Vec<PathBuf>) {
72 if let Ok(config_folder) = path_to_config_folder() {
73 shortcuts.push(config_folder);
74 }
75 }
76
77 fn push_start_folder(shortcuts: &mut Vec<PathBuf>, start_folder: &Path) {
78 shortcuts.push(start_folder.to_owned());
79 }
80
81 fn git_root_or_cwd() -> PathBuf {
82 git_root().map_or_else(
83 |_| std::env::current_dir().unwrap_or_default(),
84 PathBuf::from,
85 )
86 }
87
88 fn push_git_root(shortcuts: &mut Vec<PathBuf>) {
89 shortcuts.push(Self::git_root_or_cwd());
90 }
91
92 fn clear_doublons(&mut self) {
93 self.content.sort_unstable();
94 self.content.dedup();
95 }
96
97 fn extend_with_mount_points(&mut self, mount_points: &[&Path]) {
99 self.content
100 .extend(mount_points.iter().map(|p| p.to_path_buf()));
101 self.extend_with_mtp();
102 self.clear_doublons();
103 }
104
105 fn extend_with_mtp(&mut self) {
107 let Ok(uid) = current_uid() else {
108 return;
109 };
110 let mtp_mount_point = PathBuf::from(format!("/run/user/{uid}/gvfs/"));
111 if !mtp_mount_point.exists() || !mtp_mount_point.is_dir() {
112 return;
113 }
114
115 let mount_points: Vec<PathBuf> = match std::fs::read_dir(&mtp_mount_point) {
116 Ok(read_dir) => read_dir
117 .filter_map(std::result::Result::ok)
118 .filter(|direntry| direntry.path().is_dir())
119 .map(|direntry| direntry.path())
120 .collect(),
121 Err(error) => {
122 log_info!(
123 "unreadable gvfs {mtp_mount_point}: {error:?} ",
124 mtp_mount_point = mtp_mount_point.display(),
125 );
126 return;
127 }
128 };
129 self.content.extend(mount_points);
130 }
131
132 pub fn refresh(
136 &mut self,
137 mount_points: &[&Path],
138 left_path: &std::path::Path,
139 right_path: &std::path::Path,
140 ) {
141 self.content = Self::build_content(&self.start_folder);
142 self.content.push(left_path.to_owned());
143 self.content.push(right_path.to_owned());
144 self.extend_with_mount_points(mount_points);
145 }
146}
147
148impl_content!(Shortcut, PathBuf);
149impl_draw_menu_with_char!(Shortcut, PathBuf);