1 use ::std::path::PathBuf;
2 use ::tui::buffer::Buffer;
3 use ::tui::layout::Rect;
4 use ::tui::style::{Color, Modifier, Style};
5 use ::tui::widgets::Widget;
6 
7 use crate::ui::format::DisplaySize;
8 use crate::ui::title::{CellSizeOpt, TitleTelescope};
9 use crate::ui::FolderInfo;
10 
11 #[cfg(not(target_os = "windows"))]
12 use crate::os::unix::is_user_admin;
13 #[cfg(target_os = "windows")]
14 use crate::os::windows::is_user_admin;
15 
16 pub struct TitleLine<'a> {
17     base_path_info: FolderInfo<'a>,
18     current_path_info: FolderInfo<'a>,
19     space_freed: u128,
20     show_loading: bool,
21     progress_indicator: u64,
22     read_errors: Option<u64>,
23     flash_space: bool,
24     path_error: bool,
25     zoom_level: Option<usize>,
26 }
27 
28 impl<'a> TitleLine<'a> {
new( base_path_info: FolderInfo<'a>, current_path_info: FolderInfo<'a>, space_freed: u128, ) -> Self29     pub fn new(
30         base_path_info: FolderInfo<'a>,
31         current_path_info: FolderInfo<'a>,
32         space_freed: u128,
33     ) -> Self {
34         Self {
35             base_path_info,
36             current_path_info,
37             space_freed,
38             progress_indicator: 0,
39             read_errors: None,
40             show_loading: false,
41             flash_space: false,
42             path_error: false,
43             zoom_level: None,
44         }
45     }
show_loading(mut self) -> Self46     pub fn show_loading(mut self) -> Self {
47         self.show_loading = true;
48         self
49     }
flash_space(mut self, flash_space: bool) -> Self50     pub fn flash_space(mut self, flash_space: bool) -> Self {
51         self.flash_space = flash_space;
52         self
53     }
path_error(mut self, path_error: bool) -> Self54     pub fn path_error(mut self, path_error: bool) -> Self {
55         self.path_error = path_error;
56         self
57     }
progress_indicator(mut self, progress_indicator: u64) -> Self58     pub fn progress_indicator(mut self, progress_indicator: u64) -> Self {
59         self.progress_indicator = progress_indicator;
60         self
61     }
read_errors(mut self, read_errors: u64) -> Self62     pub fn read_errors(mut self, read_errors: u64) -> Self {
63         if read_errors > 0 {
64             self.read_errors = Some(read_errors);
65         }
66         self
67     }
zoom_level(mut self, zoom_level: usize) -> Self68     pub fn zoom_level(mut self, zoom_level: usize) -> Self {
69         if zoom_level > 0 {
70             self.zoom_level = Some(zoom_level);
71         }
72         self
73     }
74 }
75 
76 impl<'a> Widget for TitleLine<'a> {
render(self, rect: Rect, buf: &mut Buffer)77     fn render(self, rect: Rect, buf: &mut Buffer) {
78         let base_path = &self
79             .base_path_info
80             .path
81             .clone()
82             .into_os_string()
83             .into_string()
84             .expect("could not convert os string to string");
85         let current_path = {
86             let mut current_path_relative_to_base = PathBuf::new();
87             let base_path_len = self.base_path_info.path.iter().count();
88             for folder in self.current_path_info.path.iter().skip(base_path_len) {
89                 current_path_relative_to_base.push(folder);
90             }
91             current_path_relative_to_base.to_string_lossy().into_owned()
92         };
93         let separator = if base_path.ends_with(::std::path::MAIN_SEPARATOR) {
94             // eg. if base_path is "/", we don't want current path to
95             // also start with "/" otherwise we'll have "//path_to_my/location"
96             // instead of "/path_to_my/location"
97             format!("")
98         } else {
99             format!("{}", ::std::path::MAIN_SEPARATOR)
100         };
101         #[cfg(test)]
102         let current_path = str::replace(&current_path, "\\", "/");
103         #[cfg(test)]
104         let base_path = str::replace(&base_path, "\\", "/");
105         #[cfg(test)]
106         let separator = str::replace(&separator, "\\", "/");
107         let total_size = DisplaySize(self.base_path_info.size as f64);
108         let total_descendants = &self.base_path_info.num_descendants;
109         let current_folder_size = DisplaySize(self.current_path_info.size as f64);
110         let current_folder_descendants = self.current_path_info.num_descendants;
111         let space_freed = DisplaySize(self.space_freed as f64);
112 
113         let mut default_style = Style::default().fg(Color::Yellow);
114         if !self.show_loading {
115             default_style = default_style.add_modifier(Modifier::BOLD);
116         };
117         let mut title_telescope = TitleTelescope::new(default_style);
118         if self.show_loading {
119             title_telescope.append_to_left_side(vec![
120                 CellSizeOpt::new(format!(
121                     "Scanning: {} ({} files)",
122                     total_size, total_descendants
123                 )),
124                 CellSizeOpt::new(format!("Scanning: {}", total_size)),
125                 CellSizeOpt::new(format!("{}", total_size)),
126             ]);
127         } else {
128             title_telescope.append_to_left_side(vec![
129                 CellSizeOpt::new(format!(
130                     "Total: {} ({} files), freed: {}",
131                     total_size, total_descendants, space_freed
132                 )),
133                 CellSizeOpt::new(format!("Total: {}, freed: {}", total_size, space_freed)),
134                 CellSizeOpt::new(format!("Total: {}", total_size)),
135                 CellSizeOpt::new(format!("{}", total_size)),
136             ]);
137         };
138         if let Some(read_errors) = self.read_errors {
139             title_telescope.append_to_left_side(vec![
140                 CellSizeOpt::new(format!(" (failed to read {} files)", read_errors))
141                     .style(default_style.fg(Color::Red)),
142                 CellSizeOpt::new(format!(" ({} errors)", read_errors))
143                     .style(default_style.fg(Color::Red)),
144                 CellSizeOpt::new(" (errors)".to_string()).style(default_style.fg(Color::Red)),
145             ]);
146         }
147         if is_user_admin() {
148             title_telescope.append_to_left_side(vec![
149                 CellSizeOpt::new(format!(" (CAUTION: running as root)"))
150                     .style(default_style.fg(Color::Red)),
151                 CellSizeOpt::new(format!(" (running as root)")).style(default_style.fg(Color::Red)),
152                 CellSizeOpt::new(" (root)".to_string()).style(default_style.fg(Color::Red)),
153             ]);
154         }
155         title_telescope.append_to_right_side(vec![CellSizeOpt::new(base_path.to_string())]);
156         if !current_path.is_empty() {
157             title_telescope.append_to_right_side(vec![
158                 CellSizeOpt::new(format!(
159                     "{}{} ({}, {} files)",
160                     separator, current_path, current_folder_size, current_folder_descendants
161                 ))
162                 .style(default_style.fg(Color::Green)),
163                 CellSizeOpt::new(format!(
164                     "{}{} ({})",
165                     separator, current_path, current_folder_size
166                 ))
167                 .style(default_style.fg(Color::Green)),
168                 CellSizeOpt::new(format!("{}{}", separator, current_path))
169                     .style(default_style.fg(Color::Green)),
170             ]);
171         }
172         if let Some(zoom_level) = self.zoom_level {
173             title_telescope.append_to_right_side(vec![
174                 CellSizeOpt::new(format!(
175                     " (+{} larger file(s), zoom out to show)",
176                     zoom_level
177                 ))
178                 .style(default_style.fg(Color::Green)),
179                 CellSizeOpt::new(format!(" (+{} larger file(s))", zoom_level))
180                     .style(default_style.fg(Color::Green)),
181             ]);
182         }
183 
184         title_telescope
185             .loading(self.show_loading, self.progress_indicator)
186             .path_error(self.path_error)
187             .size_flash(self.flash_space)
188             .render(rect, buf);
189     }
190 }
191