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(¤t_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