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 if let Ok(home_path) = PathBuf::from_str(tilde("~").borrow()) {
59 shortcuts.push(home_path);
60 }
61 }
62
63 fn push_trash_folder(shortcuts: &mut Vec<PathBuf>) {
65 if let Ok(trash_path) = PathBuf::from_str(tilde(TRASH_FOLDER_FILES).borrow()) {
66 if trash_path.exists() {
67 shortcuts.push(trash_path);
68 }
69 }
70 }
71
72 fn push_config_folder(shortcuts: &mut Vec<PathBuf>) {
74 if let Ok(config_folder) = path_to_config_folder() {
75 shortcuts.push(config_folder);
76 }
77 }
78
79 fn push_start_folder(shortcuts: &mut Vec<PathBuf>, start_folder: &Path) {
80 shortcuts.push(start_folder.to_owned());
81 }
82
83 fn git_root_or_cwd() -> PathBuf {
84 git_root().map_or_else(
85 |_| std::env::current_dir().unwrap_or_default(),
86 PathBuf::from,
87 )
88 }
89
90 fn push_git_root(shortcuts: &mut Vec<PathBuf>) {
91 shortcuts.push(Self::git_root_or_cwd());
92 }
93
94 fn clear_doublons(&mut self) {
95 self.content.sort_unstable();
96 self.content.dedup();
97 }
98
99 fn extend_with_mount_points(&mut self, mount_points: &[&Path]) {
101 self.content
102 .extend(mount_points.iter().map(|p| p.to_path_buf()));
103 self.extend_with_mtp();
104 self.clear_doublons();
105 }
106
107 fn extend_with_mtp(&mut self) {
109 let Ok(uid) = current_uid() else {
110 return;
111 };
112 let mtp_mount_point = PathBuf::from(format!("/run/user/{uid}/gvfs/"));
113 if !mtp_mount_point.exists() || !mtp_mount_point.is_dir() {
114 return;
115 }
116
117 let mount_points: Vec<PathBuf> = match std::fs::read_dir(&mtp_mount_point) {
118 Ok(read_dir) => read_dir
119 .filter_map(std::result::Result::ok)
120 .filter(|direntry| direntry.path().is_dir())
121 .map(|direntry| direntry.path())
122 .collect(),
123 Err(error) => {
124 log_info!(
125 "unreadable gvfs {mtp_mount_point}: {error:?} ",
126 mtp_mount_point = mtp_mount_point.display(),
127 );
128 return;
129 }
130 };
131 self.content.extend(mount_points);
132 }
133
134 pub fn refresh(
138 &mut self,
139 mount_points: &[&Path],
140 left_path: &std::path::Path,
141 right_path: &std::path::Path,
142 ) {
143 self.content = Self::build_content(&self.start_folder);
144 self.content.push(left_path.to_owned());
145 self.content.push(right_path.to_owned());
146 self.extend_with_mount_points(mount_points);
147 }
148}
149
150impl_selectable!(Shortcut);
151impl_content!(Shortcut, PathBuf);
152impl_draw_menu_with_char!(Shortcut, PathBuf);