chore: more pedantic clippy suggestions

This commit is contained in:
Cheong Lau 2025-10-26 16:20:51 +10:00
parent 5863671217
commit 5f729829d7
17 changed files with 1063 additions and 998 deletions

File diff suppressed because it is too large Load diff

View file

@ -55,7 +55,7 @@ pub fn extract(
.map(flate2::read::GzDecoder::new) .map(flate2::read::GzDecoder::new)
.map(tar::Archive::new) .map(tar::Archive::new)
.and_then(|mut archive| archive.unpack(new_dir)) .and_then(|mut archive| archive.unpack(new_dir))
.map_err(|e| OperationError::from_err(e, controller))? .map_err(|e| OperationError::from_err(e, controller))?;
} }
"application/x-tar" => OpReader::new(path, controller.clone()) "application/x-tar" => OpReader::new(path, controller.clone())
.map(io::BufReader::new) .map(io::BufReader::new)
@ -93,10 +93,10 @@ pub fn extract(
.map(|reader| lzma_rust2::XzReader::new(reader, true)) .map(|reader| lzma_rust2::XzReader::new(reader, true))
.map(tar::Archive::new) .map(tar::Archive::new)
.and_then(|mut archive| archive.unpack(new_dir)) .and_then(|mut archive| archive.unpack(new_dir))
.map_err(|e| OperationError::from_err(e, controller))? .map_err(|e| OperationError::from_err(e, controller))?;
} }
_ => Err(OperationError::from_err( _ => Err(OperationError::from_err(
format!("unsupported mime type {:?}", mime), format!("unsupported mime type {mime:?}"),
controller, controller,
))?, ))?,
} }
@ -262,7 +262,7 @@ fn zip_extract<R: io::Read + io::Seek, P: AsRef<Path>>(
// Ensure we update children's permissions before making a parent unwritable // Ensure we update children's permissions before making a parent unwritable
files_by_unix_mode.sort_by_key(|(path, _)| Reverse(path.clone())); files_by_unix_mode.sort_by_key(|(path, _)| Reverse(path.clone()));
} }
for (path, mode) in files_by_unix_mode.into_iter() { for (path, mode) in files_by_unix_mode {
fs::set_permissions(&path, fs::Permissions::from_mode(mode))?; fs::set_permissions(&path, fs::Permissions::from_mode(mode))?;
} }
} }

View file

@ -42,7 +42,7 @@ impl ClipboardCopy {
.to_string(); .to_string();
//TODO: do we have to use \r\n? //TODO: do we have to use \r\n?
let cr_nl = "\r\n"; let cr_nl = "\r\n";
for path in paths.iter() { for path in paths {
let path = path.as_ref(); let path = path.as_ref();
match path.to_str() { match path.to_str() {
@ -56,8 +56,8 @@ impl ClipboardCopy {
None => { None => {
//TODO: allow non-UTF-8? //TODO: allow non-UTF-8?
log::warn!( log::warn!(
"{:?} is not valid UTF-8, not adding to text/plain clipboard", "{} is not valid UTF-8, not adding to text/plain clipboard",
path path.display()
); );
} }
} }
@ -74,8 +74,8 @@ impl ClipboardCopy {
} }
Err(()) => { Err(()) => {
log::warn!( log::warn!(
"{:?} cannot be turned into a URL, not adding to text/uri-list clipboard", "{} cannot be turned into a URL, not adding to text/uri-list clipboard",
path path.display()
); );
} }
} }
@ -135,7 +135,7 @@ impl TryFrom<(Vec<u8>, String)> for ClipboardPaste {
let url = Url::parse(line)?; let url = Url::parse(line)?;
match url.to_file_path() { match url.to_file_path() {
Ok(path) => paths.push(path), Ok(path) => paths.push(path),
Err(()) => Err(format!("invalid file URL {:?}", url))?, Err(()) => Err(format!("invalid file URL {url:?}"))?,
} }
} }
} }
@ -146,18 +146,18 @@ impl TryFrom<(Vec<u8>, String)> for ClipboardPaste {
kind = match line { kind = match line {
"copy" => ClipboardKind::Copy, "copy" => ClipboardKind::Copy,
"cut" => ClipboardKind::Cut { is_dnd: false }, "cut" => ClipboardKind::Cut { is_dnd: false },
_ => Err(format!("unsupported clipboard operation {:?}", line))?, _ => Err(format!("unsupported clipboard operation {line:?}"))?,
}; };
} else { } else {
let url = Url::parse(line)?; let url = Url::parse(line)?;
match url.to_file_path() { match url.to_file_path() {
Ok(path) => paths.push(path), Ok(path) => paths.push(path),
Err(()) => Err(format!("invalid file URL {:?}", url))?, Err(()) => Err(format!("invalid file URL {url:?}"))?,
} }
} }
} }
} }
_ => Err(format!("unsupported mime type {:?}", mime))?, _ => Err(format!("unsupported mime type {mime:?}"))?,
} }
Ok(Self { kind, paths }) Ok(Self { kind, paths })
} }

View file

@ -135,18 +135,18 @@ impl State {
pub fn load() -> (Option<cosmic_config::Config>, Self) { pub fn load() -> (Option<cosmic_config::Config>, Self) {
match cosmic_config::Config::new_state(App::APP_ID, CONFIG_VERSION) { match cosmic_config::Config::new_state(App::APP_ID, CONFIG_VERSION) {
Ok(config_handler) => { Ok(config_handler) => {
let config = match State::get_entry(&config_handler) { let config = match Self::get_entry(&config_handler) {
Ok(ok) => ok, Ok(ok) => ok,
Err((errs, config)) => { Err((errs, config)) => {
log::info!("errors loading config: {:?}", errs); log::info!("errors loading config: {errs:?}");
config config
} }
}; };
(Some(config_handler), config) (Some(config_handler), config)
} }
Err(err) => { Err(err) => {
log::error!("failed to create config handler: {}", err); log::error!("failed to create config handler: {err}");
(None, State::default()) (None, Self::default())
} }
} }
} }
@ -178,18 +178,18 @@ impl Config {
pub fn load() -> (Option<cosmic_config::Config>, Self) { pub fn load() -> (Option<cosmic_config::Config>, Self) {
match cosmic_config::Config::new(App::APP_ID, CONFIG_VERSION) { match cosmic_config::Config::new(App::APP_ID, CONFIG_VERSION) {
Ok(config_handler) => { Ok(config_handler) => {
let config = match Config::get_entry(&config_handler) { let config = match Self::get_entry(&config_handler) {
Ok(ok) => ok, Ok(ok) => ok,
Err((errs, config)) => { Err((errs, config)) => {
log::info!("errors loading config: {:?}", errs); log::info!("errors loading config: {errs:?}");
config config
} }
}; };
(Some(config_handler), config) (Some(config_handler), config)
} }
Err(err) => { Err(err) => {
log::error!("failed to create config handler: {}", err); log::error!("failed to create config handler: {err}");
(None, Config::default()) (None, Self::default())
} }
} }
} }
@ -204,7 +204,7 @@ impl Config {
} }
/// Construct tab config for dialog /// Construct tab config for dialog
pub fn dialog_tab(&self) -> TabConfig { pub const fn dialog_tab(&self) -> TabConfig {
TabConfig { TabConfig {
folders_first: self.dialog.folders_first, folders_first: self.dialog.folders_first,
icon_sizes: self.dialog.icon_sizes, icon_sizes: self.dialog.icon_sizes,

View file

@ -86,15 +86,15 @@ impl DialogKind {
} }
} }
pub fn is_dir(&self) -> bool { pub const fn is_dir(&self) -> bool {
matches!(self, Self::OpenFolder | Self::OpenMultipleFolders) matches!(self, Self::OpenFolder | Self::OpenMultipleFolders)
} }
pub fn multiple(&self) -> bool { pub const fn multiple(&self) -> bool {
matches!(self, Self::OpenMultipleFiles | Self::OpenMultipleFolders) matches!(self, Self::OpenMultipleFiles | Self::OpenMultipleFolders)
} }
pub fn save(&self) -> bool { pub const fn save(&self) -> bool {
matches!(self, Self::SaveFile { .. }) matches!(self, Self::SaveFile { .. })
} }
} }
@ -200,7 +200,7 @@ impl<T: AsRef<str>> From<T> for DialogLabel {
impl<'a, M: Clone + 'static> From<&'a DialogLabel> for Element<'a, M> { impl<'a, M: Clone + 'static> From<&'a DialogLabel> for Element<'a, M> {
fn from(label: &'a DialogLabel) -> Self { fn from(label: &'a DialogLabel) -> Self {
let mut iced_spans = Vec::with_capacity(label.spans.len()); let mut iced_spans = Vec::with_capacity(label.spans.len());
for span in label.spans.iter() { for span in &label.spans {
iced_spans.push(cosmic::iced::widget::span(&span.text).underline(span.underline)); iced_spans.push(cosmic::iced::widget::span(&span.text).underline(span.underline));
} }
cosmic::iced::widget::rich_text(iced_spans).into() cosmic::iced::widget::rich_text(iced_spans).into()
@ -276,7 +276,7 @@ impl<M: Send + 'static> Dialog<M> {
settings.platform_specific.application_id = dialog_settings.app_id; settings.platform_specific.application_id = dialog_settings.app_id;
} }
let (window_id, window_command) = window::open(settings.clone()); let (window_id, window_command) = window::open(settings);
let mut core = Core::default(); let mut core = Core::default();
core.set_main_window_id(Some(window_id)); core.set_main_window_id(Some(window_id));
@ -286,7 +286,7 @@ impl<M: Send + 'static> Dialog<M> {
match fs::canonicalize(path) { match fs::canonicalize(path) {
Ok(ok) => Some(ok), Ok(ok) => Some(ok),
Err(err) => { Err(err) => {
log::warn!("failed to canonicalize {:?}: {}", path, err); log::warn!("failed to canonicalize {}: {}", path.display(), err);
None None
} }
} }
@ -386,7 +386,7 @@ impl<M: Send + 'static> Dialog<M> {
.map(self.mapper) .map(self.mapper)
} }
pub fn window_id(&self) -> window::Id { pub const fn window_id(&self) -> window::Id {
self.cosmic.app.flags.window_id self.cosmic.app.flags.window_id
} }
} }
@ -452,24 +452,24 @@ enum Message {
} }
impl From<AppMessage> for Message { impl From<AppMessage> for Message {
fn from(app_message: AppMessage) -> Message { fn from(app_message: AppMessage) -> Self {
match app_message { match app_message {
AppMessage::None => Message::None, AppMessage::None => Self::None,
AppMessage::Preview(_entity_opt) => Message::Preview, AppMessage::Preview(_entity_opt) => Self::Preview,
AppMessage::SearchActivate => Message::SearchActivate, AppMessage::SearchActivate => Self::SearchActivate,
AppMessage::ScrollTab(scroll_speed) => Message::ScrollTab(scroll_speed), AppMessage::ScrollTab(scroll_speed) => Self::ScrollTab(scroll_speed),
AppMessage::TabMessage(_entity_opt, tab_message) => Message::TabMessage(tab_message), AppMessage::TabMessage(_entity_opt, tab_message) => Self::TabMessage(tab_message),
AppMessage::TabView(_entity_opt, view) => Message::TabView(view), AppMessage::TabView(_entity_opt, view) => Self::TabView(view),
AppMessage::ToggleFoldersFirst => Message::ToggleFoldersFirst, AppMessage::ToggleFoldersFirst => Self::ToggleFoldersFirst,
AppMessage::ToggleShowHidden => Message::ToggleShowHidden, AppMessage::ToggleShowHidden => Self::ToggleShowHidden,
AppMessage::ZoomDefault(_entity_opt) => Message::ZoomDefault, AppMessage::ZoomDefault(_entity_opt) => Self::ZoomDefault,
AppMessage::ZoomIn(_entity_opt) => Message::ZoomIn, AppMessage::ZoomIn(_entity_opt) => Self::ZoomIn,
AppMessage::ZoomOut(_entity_opt) => Message::ZoomOut, AppMessage::ZoomOut(_entity_opt) => Self::ZoomOut,
AppMessage::NewItem(_entity_opt, true) => Message::NewFolder, AppMessage::NewItem(_entity_opt, true) => Self::NewFolder,
AppMessage::Surface(action) => Message::Surface(action), AppMessage::Surface(action) => Self::Surface(action),
unsupported => { unsupported => {
log::warn!("{unsupported:?} not supported in dialog mode"); log::warn!("{unsupported:?} not supported in dialog mode");
Message::None Self::None
} }
} }
} }
@ -550,7 +550,7 @@ impl App {
} }
let mut row = widget::row::with_capacity( let mut row = widget::row::with_capacity(
if !self.filters.is_empty() { 1 } else { 0 } usize::from(!self.filters.is_empty())
+ self.choices.len() * 2 + self.choices.len() * 2
+ if is_condensed { 0 } else { 3 }, + if is_condensed { 0 } else { 3 },
) )
@ -566,9 +566,10 @@ impl App {
for (choice_i, choice) in self.choices.iter().enumerate() { for (choice_i, choice) in self.choices.iter().enumerate() {
match choice { match choice {
DialogChoice::CheckBox { label, value, .. } => { DialogChoice::CheckBox { label, value, .. } => {
row = row.push(widget::checkbox(label, *value).on_toggle(move |checked| { row =
Message::Choice(choice_i, if checked { 1 } else { 0 }) row.push(widget::checkbox(label, *value).on_toggle(move |checked| {
})); Message::Choice(choice_i, usize::from(checked))
}));
} }
DialogChoice::ComboBox { DialogChoice::ComboBox {
label, label,
@ -595,7 +596,7 @@ impl App {
let mut has_selected = false; let mut has_selected = false;
if let Some(items) = self.tab.items_opt() { if let Some(items) = self.tab.items_opt() {
for item in items.iter() { for item in items {
if item.selected { if item.selected {
has_selected = true; has_selected = true;
break; break;
@ -641,7 +642,7 @@ impl App {
} }
PreviewKind::Location(location) => { PreviewKind::Location(location) => {
if let Some(items) = self.tab.items_opt() { if let Some(items) = self.tab.items_opt() {
for item in items.iter() { for item in items {
if item.location_opt.as_ref() == Some(location) { if item.location_opt.as_ref() == Some(location) {
children.push(item.preview_view(None, military_time)); children.push(item.preview_view(None, military_time));
// Only show one property view to avoid issues like hangs when generating // Only show one property view to avoid issues like hangs when generating
@ -653,7 +654,7 @@ impl App {
} }
PreviewKind::Selected => { PreviewKind::Selected => {
if let Some(items) = self.tab.items_opt() { if let Some(items) = self.tab.items_opt() {
for item in items.iter() { for item in items {
if item.selected { if item.selected {
children.push(item.preview_view(None, military_time)); children.push(item.preview_view(None, military_time));
// Only show one property view to avoid issues like hangs when generating // Only show one property view to avoid issues like hangs when generating
@ -686,7 +687,7 @@ impl App {
let mounter_paths: Vec<_> = mounter_items let mounter_paths: Vec<_> = mounter_items
.iter() .iter()
.flat_map(|item| item.1.iter()) .flat_map(|item| item.1.iter())
.filter_map(|item| item.path()) .filter_map(MounterItem::path)
.collect(); .collect();
if !mounter_paths.is_empty() { if !mounter_paths.is_empty() {
for item in &mut items { for item in &mut items {
@ -703,7 +704,7 @@ impl App {
)) ))
} }
Err(err) => { Err(err) => {
log::warn!("failed to rescan: {}", err); log::warn!("failed to rescan: {err}");
cosmic::action::none() cosmic::action::none()
} }
} }
@ -724,7 +725,7 @@ impl App {
Some(term) => self.tab.location.path_opt().map(|path| { Some(term) => self.tab.location.path_opt().map(|path| {
( (
Location::Search( Location::Search(
path.to_path_buf(), path.clone(),
term, term,
self.tab.config.show_hidden, self.tab.config.show_hidden,
Instant::now(), Instant::now(),
@ -733,7 +734,7 @@ impl App {
) )
}), }),
None => match &self.tab.location { None => match &self.tab.location {
Location::Search(path, ..) => Some((Location::Path(path.to_path_buf()), false)), Location::Search(path, ..) => Some((Location::Path(path.clone()), false)),
_ => None, _ => None,
}, },
}; };
@ -763,24 +764,21 @@ impl App {
fn with_dialog_config<F: Fn(&mut DialogConfig)>(&mut self, f: F) -> Task<Message> { fn with_dialog_config<F: Fn(&mut DialogConfig)>(&mut self, f: F) -> Task<Message> {
let mut dialog = self.flags.config.dialog; let mut dialog = self.flags.config.dialog;
f(&mut dialog); f(&mut dialog);
if dialog != self.flags.config.dialog { if dialog == self.flags.config.dialog {
match &self.flags.config_handler { Task::none()
Some(config_handler) => { } else {
match self.flags.config.set_dialog(config_handler, dialog) { if let Some(config_handler) = &self.flags.config_handler {
Ok(_) => {} match self.flags.config.set_dialog(config_handler, dialog) {
Err(err) => { Ok(_) => {}
log::warn!("failed to save config \"dialog\": {}", err); Err(err) => {
} log::warn!("failed to save config \"dialog\": {err}");
} }
} }
None => { } else {
self.flags.config.dialog = dialog; self.flags.config.dialog = dialog;
log::warn!("failed to save config \"dialog\": no config handler",); log::warn!("failed to save config \"dialog\": no config handler",);
}
} }
self.update_config() self.update_config()
} else {
Task::none()
} }
} }
@ -788,8 +786,7 @@ impl App {
let nav_bar_id = self.nav_model.iter().find(|&id| { let nav_bar_id = self.nav_model.iter().find(|&id| {
self.nav_model self.nav_model
.data::<Location>(id) .data::<Location>(id)
.map(|l| l == location) .is_some_and(|l| l == location)
.unwrap_or_default()
}); });
if let Some(id) = nav_bar_id { if let Some(id) = nav_bar_id {
@ -809,7 +806,7 @@ impl App {
.data(Location::Recents) .data(Location::Recents)
}); });
for favorite in self.flags.config.favorites.iter() { for favorite in &self.flags.config.favorites {
if let Some(path) = favorite.path_opt() { if let Some(path) = favorite.path_opt() {
let name = if matches!(favorite, Favorite::Home) { let name = if matches!(favorite, Favorite::Home) {
fl!("home") fl!("home")
@ -837,8 +834,8 @@ impl App {
// Collect all mounter items // Collect all mounter items
let mut nav_items = Vec::new(); let mut nav_items = Vec::new();
for (key, items) in self.mounter_items.iter() { for (key, items) in &self.mounter_items {
for item in items.iter() { for item in items {
nav_items.push((*key, item)); nav_items.push((*key, item));
} }
} }
@ -849,7 +846,7 @@ impl App {
nav_model = nav_model.insert(|mut b| { nav_model = nav_model.insert(|mut b| {
b = b.text(item.name()).data(MounterData(key, item.clone())); b = b.text(item.name()).data(MounterData(key, item.clone()));
if let Some(path) = item.path() { if let Some(path) = item.path() {
b = b.data(Location::Path(path.clone())); b = b.data(Location::Path(path));
} }
if let Some(icon) = item.icon(true) { if let Some(icon) = item.icon(true) {
b = b.icon(widget::icon::icon(icon).size(16)); b = b.icon(widget::icon::icon(icon).size(16));
@ -878,33 +875,33 @@ impl App {
if let Some((mut watcher, old_paths)) = self.watcher_opt.take() { if let Some((mut watcher, old_paths)) = self.watcher_opt.take() {
let mut new_paths = FxHashSet::default(); let mut new_paths = FxHashSet::default();
if let Some(path) = &self.tab.location.path_opt() { if let Some(path) = &self.tab.location.path_opt() {
new_paths.insert(path.to_path_buf()); new_paths.insert((*path).clone());
} }
// Unwatch paths no longer used // Unwatch paths no longer used
for path in old_paths.iter() { for path in &old_paths {
if !new_paths.contains(path) { if !new_paths.contains(path) {
match watcher.unwatch(path) { match watcher.unwatch(path) {
Ok(()) => { Ok(()) => {
log::debug!("unwatching {:?}", path); log::debug!("unwatching {}", path.display());
} }
Err(err) => { Err(err) => {
log::debug!("failed to unwatch {:?}: {}", path, err); log::debug!("failed to unwatch {}: {}", path.display(), err);
} }
} }
} }
} }
// Watch new paths // Watch new paths
for path in new_paths.iter() { for path in &new_paths {
if !old_paths.contains(path) { if !old_paths.contains(path) {
//TODO: should this be recursive? //TODO: should this be recursive?
match watcher.watch(path, notify::RecursiveMode::NonRecursive) { match watcher.watch(path, notify::RecursiveMode::NonRecursive) {
Ok(()) => { Ok(()) => {
log::debug!("watching {:?}", path); log::debug!("watching {}", path.display());
} }
Err(err) => { Err(err) => {
log::debug!("failed to watch {:?}: {}", path, err); log::debug!("failed to watch {}: {}", path.display(), err);
} }
} }
} }
@ -951,7 +948,7 @@ impl Application for App {
let accept_label = flags.kind.accept_label(); let accept_label = flags.kind.accept_label();
let location = Location::Path(match &flags.path_opt { let location = Location::Path(match &flags.path_opt {
Some(path) => path.to_path_buf(), Some(path) => path.clone(),
None => match env::current_dir() { None => match env::current_dir() {
Ok(path) => path, Ok(path) => path,
Err(_) => home_dir(), Err(_) => home_dir(),
@ -972,7 +969,7 @@ impl Application for App {
let key_binds = key_binds(&tab.mode); let key_binds = key_binds(&tab.mode);
let mut app = App { let mut app = Self {
core, core,
flags, flags,
title, title,
@ -1015,16 +1012,16 @@ impl Application for App {
ContextPage::Preview(_, kind) => { ContextPage::Preview(_, kind) => {
let mut actions = Vec::with_capacity(3); let mut actions = Vec::with_capacity(3);
if let Some(items) = self.tab.items_opt() { if let Some(items) = self.tab.items_opt() {
for item in items.iter() { for item in items {
if item.selected { if item.selected {
actions.extend( actions.extend(
item.preview_header() item.preview_header()
.into_iter() .into_iter()
.map(|element| element.map(Message::TabMessage)), .map(|element| element.map(Message::TabMessage)),
) );
} }
} }
}; }
Some( Some(
context_drawer::context_drawer( context_drawer::context_drawer(
self.preview(kind).map(Message::TabMessage), self.preview(kind).map(Message::TabMessage),
@ -1111,9 +1108,7 @@ impl Application for App {
name, name,
}) })
}) })
.on_submit_maybe( .on_submit_maybe(complete_maybe.map(|maybe| move |_| maybe.clone()))
complete_maybe.clone().map(|maybe| move |_| maybe.clone()),
)
.into(), .into(),
]) ])
.spacing(space_xxs), .spacing(space_xxs),
@ -1237,7 +1232,7 @@ impl Application for App {
if let Some(mounter) = MOUNTERS.get(&data.0) { if let Some(mounter) = MOUNTERS.get(&data.0) {
return mounter return mounter
.mount(data.1.clone()) .mount(data.1.clone())
.map(|_| cosmic::action::none()); .map(|()| cosmic::action::none());
} }
} }
Task::none() Task::none()
@ -1328,12 +1323,12 @@ impl Application for App {
Ok(()) => { Ok(()) => {
// cd to directory // cd to directory
let message = Message::TabMessage(tab::Message::Location( let message = Message::TabMessage(tab::Message::Location(
Location::Path(path.clone()), Location::Path(path),
)); ));
return self.update(message); return self.update(message);
} }
Err(err) => { Err(err) => {
log::warn!("failed to create {:?}: {}", path, err); log::warn!("failed to create {}: {}", path.display(), err);
} }
} }
} }
@ -1366,7 +1361,7 @@ impl Application for App {
return self.rescan_tab(None); return self.rescan_tab(None);
} }
Message::Key(modifiers, key, text) => { Message::Key(modifiers, key, text) => {
for (key_bind, action) in self.key_binds.iter() { for (key_bind, action) in &self.key_binds {
if key_bind.matches(modifiers, &key) { if key_bind.matches(modifiers, &key) {
return self.update(Message::from(action.message())); return self.update(Message::from(action.message()));
} }
@ -1423,11 +1418,11 @@ impl Application for App {
// Check for unmounted folders // Check for unmounted folders
let mut unmounted = Vec::new(); let mut unmounted = Vec::new();
if let Some(old_items) = self.mounter_items.get(&mounter_key) { if let Some(old_items) = self.mounter_items.get(&mounter_key) {
for old_item in old_items.iter() { for old_item in old_items {
if let Some(old_path) = old_item.path() { if let Some(old_path) = old_item.path() {
if old_item.is_mounted() { if old_item.is_mounted() {
let mut still_mounted = false; let mut still_mounted = false;
for item in mounter_items.iter() { for item in &mounter_items {
if let Some(path) = item.path() { if let Some(path) = item.path() {
if path == old_path && item.is_mounted() { if path == old_path && item.is_mounted() {
still_mounted = true; still_mounted = true;
@ -1466,61 +1461,56 @@ impl Application for App {
Message::NewFolder => { Message::NewFolder => {
if let Some(path) = self.tab.location.path_opt() { if let Some(path) = self.tab.location.path_opt() {
self.dialog_pages.push_back(DialogPage::NewFolder { self.dialog_pages.push_back(DialogPage::NewFolder {
parent: path.to_path_buf(), parent: path.clone(),
name: String::new(), name: String::new(),
}); });
return widget::text_input::focus(self.dialog_text_input.clone()); return widget::text_input::focus(self.dialog_text_input.clone());
} }
} }
Message::NotifyEvents(events) => { Message::NotifyEvents(events) => {
log::debug!("{:?}", events); log::debug!("{events:?}");
if let Some(path) = self.tab.location.path_opt() { if let Some(path) = self.tab.location.path_opt() {
let mut contains_change = false; let mut contains_change = false;
for event in events.iter() { for event in &events {
for event_path in event.paths.iter() { for event_path in &event.paths {
if event_path.starts_with(path) { if event_path.starts_with(path) {
match event.kind { if let notify::EventKind::Modify(
notify::EventKind::Modify( notify::event::ModifyKind::Metadata(_)
notify::event::ModifyKind::Metadata(_), | notify::event::ModifyKind::Data(_),
) ) = event.kind
| notify::EventKind::Modify(notify::event::ModifyKind::Data( {
_, // If metadata or data changed, find the matching item and reload it
)) => { //TODO: this could be further optimized by looking at what exactly changed
// If metadata or data changed, find the matching item and reload it if let Some(items) = &mut self.tab.items_opt {
//TODO: this could be further optimized by looking at what exactly changed for item in items.iter_mut() {
if let Some(items) = &mut self.tab.items_opt { if item.path_opt() == Some(event_path) {
for item in items.iter_mut() { //TODO: reload more, like mime types?
if item.path_opt() == Some(event_path) { match fs::metadata(event_path) {
//TODO: reload more, like mime types? Ok(new_metadata) => {
match fs::metadata(event_path) { if let ItemMetadata::Path {
Ok(new_metadata) => { metadata, ..
if let ItemMetadata::Path { } = &mut item.metadata
metadata, {
.. *metadata = new_metadata;
} = &mut item.metadata
{
*metadata = new_metadata;
}
}
Err(err) => {
log::warn!(
"failed to reload metadata for {:?}: {}",
path,
err
);
} }
} }
//TODO item.thumbnail_opt = Err(err) => {
log::warn!(
"failed to reload metadata for {}: {}",
path.display(),
err
);
}
} }
//TODO item.thumbnail_opt =
} }
} }
} }
_ => { } else {
// Any other events reload the whole tab // Any other events reload the whole tab
contains_change = true; contains_change = true;
break; break;
}
} }
} }
} }
@ -1543,13 +1533,13 @@ impl Application for App {
Message::Open => { Message::Open => {
let mut paths = Vec::new(); let mut paths = Vec::new();
if let Some(items) = self.tab.items_opt() { if let Some(items) = self.tab.items_opt() {
for item in items.iter() { for item in items {
if item.selected { if item.selected {
if let Some(path) = item.path_opt() { if let Some(path) = item.path_opt() {
paths.push(path.clone()); paths.push(path.clone());
let _ = update_recently_used( let _ = update_recently_used(
&path.clone(), &path.clone(),
App::APP_ID.to_string(), Self::APP_ID.to_string(),
"cosmic-files".to_string(), "cosmic-files".to_string(),
None, None,
); );
@ -1560,7 +1550,7 @@ impl Application for App {
// Ensure selection is allowed // Ensure selection is allowed
//TODO: improve tab logic so this doesn't block the open button so often //TODO: improve tab logic so this doesn't block the open button so often
for path in paths.iter() { for path in &paths {
let path_is_dir = path.is_dir(); let path_is_dir = path.is_dir();
if path_is_dir != self.flags.kind.is_dir() { if path_is_dir != self.flags.kind.is_dir() {
if path_is_dir && paths.len() == 1 { if path_is_dir && paths.len() == 1 {
@ -1569,10 +1559,10 @@ impl Application for App {
Location::Path(path.clone()), Location::Path(path.clone()),
)); ));
return self.update(message); return self.update(message);
} else {
// Otherwise, this is not a legal selection
return Task::none();
} }
// Otherwise, this is not a legal selection
return Task::none();
} }
} }
@ -1604,7 +1594,7 @@ impl Application for App {
if path.is_dir() { if path.is_dir() {
// cd to directory // cd to directory
let message = Message::TabMessage(tab::Message::Location( let message = Message::TabMessage(tab::Message::Location(
Location::Path(path.clone()), Location::Path(path),
)); ));
return self.update(message); return self.update(message);
} else if !replace && path.exists() { } else if !replace && path.exists() {
@ -1612,17 +1602,16 @@ impl Application for App {
filename: filename.clone(), filename: filename.clone(),
}); });
return widget::button::focus(REPLACE_BUTTON_ID.clone()); return widget::button::focus(REPLACE_BUTTON_ID.clone());
} else {
self.result_opt = Some(DialogResult::Open(vec![path]));
return window::close(self.flags.window_id);
} }
self.result_opt = Some(DialogResult::Open(vec![path]));
return window::close(self.flags.window_id);
} }
} }
} }
} }
Message::ScrollTab(scroll_speed) => { Message::ScrollTab(scroll_speed) => {
return self.update(Message::TabMessage(tab::Message::ScrollTab( return self.update(Message::TabMessage(tab::Message::ScrollTab(
(scroll_speed as f32) / 10.0, f32::from(scroll_speed) / 10.0,
))); )));
} }
Message::SearchActivate => { Message::SearchActivate => {
@ -1652,7 +1641,7 @@ impl Application for App {
if let Some(items) = self.tab.items_opt() { if let Some(items) = self.tab.items_opt() {
if let Some(item) = items.get(click_i) { if let Some(item) = items.get(click_i) {
if item.selected && !item.metadata.is_dir() { if item.selected && !item.metadata.is_dir() {
*filename = item.name.clone(); filename.clone_from(&item.name);
} }
} }
} }
@ -1689,7 +1678,7 @@ impl Application for App {
let autosize_id = widget::Id::unique(); let autosize_id = widget::Id::unique();
commands.push(self.update(Message::Surface( commands.push(self.update(Message::Surface(
cosmic::surface::action::app_popup( cosmic::surface::action::app_popup(
move |app: &mut App| -> SctkPopupSettings { move |app: &mut Self| -> SctkPopupSettings {
let anchor_rect = Rectangle { let anchor_rect = Rectangle {
x: point.x as i32, x: point.x as i32,
y: point.y as i32, y: point.y as i32,
@ -1715,7 +1704,7 @@ impl Application for App {
input_zone: None, input_zone: None,
} }
}, },
Some(Box::new(move |app: &App| { Some(Box::new(move |app: &Self| {
widget::autosize::autosize( widget::autosize::autosize(
menu::context_menu( menu::context_menu(
&app.tab, &app.tab,
@ -1789,17 +1778,13 @@ impl Application for App {
// Parse filters // Parse filters
let mut parsed_globs = Vec::new(); let mut parsed_globs = Vec::new();
let mut parsed_mimes = Vec::new(); let mut parsed_mimes = Vec::new();
for pattern in filter.patterns.iter() { for pattern in &filter.patterns {
match pattern { match pattern {
DialogFilterPattern::Glob(value) => { DialogFilterPattern::Glob(value) => {
match glob::Pattern::new(value) { match glob::Pattern::new(value) {
Ok(glob) => parsed_globs.push(glob), Ok(glob) => parsed_globs.push(glob),
Err(err) => { Err(err) => {
log::warn!( log::warn!("failed to parse glob {value:?}: {err}");
"failed to parse glob {:?}: {}",
value,
err
);
} }
} }
} }
@ -1807,11 +1792,7 @@ impl Application for App {
match mime_guess::Mime::from_str(value) { match mime_guess::Mime::from_str(value) {
Ok(mime) => parsed_mimes.push(mime), Ok(mime) => parsed_mimes.push(mime),
Err(err) => { Err(err) => {
log::warn!( log::warn!("failed to parse mime {value:?}: {err}");
"failed to parse mime {:?}: {}",
value,
err
);
} }
} }
} }
@ -1825,14 +1806,14 @@ impl Application for App {
} }
// Check for mime type match (first because it is faster) // Check for mime type match (first because it is faster)
for mime in parsed_mimes.iter() { for mime in &parsed_mimes {
if mime == &item.mime { if mime == &item.mime {
return true; return true;
} }
} }
// Check for glob match (last because it is slower) // Check for glob match (last because it is slower)
for glob in parsed_globs.iter() { for glob in &parsed_globs {
if glob.matches(&item.name) { if glob.matches(&item.name) {
return true; return true;
} }
@ -1846,7 +1827,7 @@ impl Application for App {
// Select based on filename // Select based on filename
if let DialogKind::SaveFile { filename } = &self.flags.kind { if let DialogKind::SaveFile { filename } = &self.flags.kind {
for item in items.iter_mut() { for item in &mut items {
item.selected = &item.name == filename; item.selected = &item.name == filename;
} }
} }
@ -1864,9 +1845,8 @@ impl Application for App {
// Reset focus on location change // Reset focus on location change
if self.search_get().is_some() { if self.search_get().is_some() {
return widget::text_input::focus(self.search_id.clone()); return widget::text_input::focus(self.search_id.clone());
} else {
return widget::text_input::focus(self.filename_id.clone());
} }
return widget::text_input::focus(self.filename_id.clone());
} }
} }
Message::TabView(view) => { Message::TabView(view) => {
@ -1959,7 +1939,7 @@ impl Application for App {
.on_input(Message::SearchInput), .on_input(Message::SearchInput),
) )
.padding(space_xxs), .padding(space_xxs),
) );
} }
} }
@ -2055,15 +2035,14 @@ impl Application for App {
Ok(()) => {} Ok(()) => {}
Err(err) => { Err(err) => {
log::warn!( log::warn!(
"failed to send notify events: {:?}", "failed to send notify events: {err:?}"
err
); );
} }
} }
} }
} }
Err(err) => { Err(err) => {
log::warn!("failed to watch files: {:?}", err); log::warn!("failed to watch files: {err:?}");
} }
} }
}, },
@ -2080,12 +2059,12 @@ impl Application for App {
{ {
Ok(()) => {} Ok(()) => {}
Err(err) => { Err(err) => {
log::warn!("failed to send notify watcher: {:?}", err); log::warn!("failed to send notify watcher: {err:?}");
} }
} }
} }
Err(err) => { Err(err) => {
log::warn!("failed to create file watcher: {:?}", err); log::warn!("failed to create file watcher: {err:?}");
} }
} }
@ -2113,15 +2092,17 @@ impl Application for App {
for (key, mounter) in MOUNTERS.iter() { for (key, mounter) in MOUNTERS.iter() {
subscriptions.push( subscriptions.push(
mounter.subscription().with(*key).map( mounter
|(key, mounter_message)| match mounter_message { .subscription()
MounterMessage::Items(items) => Message::MounterItems(key, items), .with(*key)
_ => { .map(|(key, mounter_message)| {
log::warn!("{:?} not supported in dialog mode", mounter_message); if let MounterMessage::Items(items) = mounter_message {
Message::MounterItems(key, items)
} else {
log::warn!("{mounter_message:?} not supported in dialog mode");
Message::None Message::None
} }
}, }),
),
); );
} }

View file

@ -34,24 +34,28 @@ pub(crate) fn err_str<T: ToString>(err: T) -> String {
} }
pub fn desktop_dir() -> PathBuf { pub fn desktop_dir() -> PathBuf {
match dirs::desktop_dir() { if let Some(path) = dirs::desktop_dir() {
Some(path) => path, path
None => { } else {
let path = home_dir().join("Desktop"); let path = home_dir().join("Desktop");
log::warn!("failed to locate desktop directory, falling back to {path:?}"); log::warn!(
path "failed to locate desktop directory, falling back to {}",
} path.display()
);
path
} }
} }
pub fn home_dir() -> PathBuf { pub fn home_dir() -> PathBuf {
match dirs::home_dir() { if let Some(home) = dirs::home_dir() {
Some(home) => home, home
None => { } else {
let path = PathBuf::from("/"); let path = PathBuf::from("/");
log::warn!("failed to locate home directory, falling back to {path:?}"); log::warn!(
path "failed to locate home directory, falling back to {}",
} path.display()
);
path
} }
} }
@ -123,12 +127,9 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
} else { } else {
//TODO: support more URLs //TODO: support more URLs
let path = match url::Url::parse(&arg) { let path = match url::Url::parse(&arg) {
Ok(url) if url.scheme() == "file" => match url.to_file_path() { Ok(url) if url.scheme() == "file" => if let Ok(path) = url.to_file_path() { path } else {
Ok(path) => path, log::warn!("invalid argument {arg:?}");
Err(()) => { continue;
log::warn!("invalid argument {:?}", arg);
continue;
}
}, },
Ok(url) => { Ok(url) => {
uris.push(url); uris.push(url);
@ -139,7 +140,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
match fs::canonicalize(&path) { match fs::canonicalize(&path) {
Ok(absolute) => Location::Path(absolute), Ok(absolute) => Location::Path(absolute),
Err(err) => { Err(err) => {
log::warn!("failed to canonicalize {:?}: {}", path, err); log::warn!("failed to canonicalize {}: {}", path.display(), err);
continue; continue;
} }
} }
@ -153,7 +154,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
Ok(fork::Fork::Child) => (), Ok(fork::Fork::Child) => (),
Ok(fork::Fork::Parent(_child_pid)) => process::exit(0), Ok(fork::Fork::Parent(_child_pid)) => process::exit(0),
Err(err) => { Err(err) => {
eprintln!("failed to daemonize: {:?}", err); eprintln!("failed to daemonize: {err:?}");
process::exit(1); process::exit(1);
} }
} }

View file

@ -94,6 +94,6 @@ pub fn localize() {
let requested_languages = i18n_embed::DesktopLanguageRequester::requested_languages(); let requested_languages = i18n_embed::DesktopLanguageRequester::requested_languages();
if let Err(error) = localizer.select(&requested_languages) { if let Err(error) = localizer.select(&requested_languages) {
eprintln!("Error while loading language for COSMIC Files {}", error); eprintln!("Error while loading language for COSMIC Files {error}");
} }
} }

View file

@ -43,7 +43,7 @@ macro_rules! menu_button {
); );
} }
fn menu_button_optional( const fn menu_button_optional(
label: String, label: String,
action: Action, action: Action,
enabled: bool, enabled: bool,
@ -61,7 +61,7 @@ pub fn context_menu<'a>(
modifiers: &Modifiers, modifiers: &Modifiers,
) -> Element<'a, tab::Message> { ) -> Element<'a, tab::Message> {
let find_key = |action: &Action| -> String { let find_key = |action: &Action| -> String {
for (key_bind, key_action) in key_binds.iter() { for (key_bind, key_action) in key_binds {
if action == key_action { if action == key_action {
return key_bind.to_string(); return key_bind.to_string();
} }
@ -110,11 +110,11 @@ pub fn context_menu<'a>(
let mut selected_types: Vec<Mime> = vec![]; let mut selected_types: Vec<Mime> = vec![];
let mut selected_mount_point = 0; let mut selected_mount_point = 0;
if let Some(items) = tab.items_opt() { if let Some(items) = tab.items_opt() {
for item in items.iter() { for item in items {
if item.selected { if item.selected {
selected += 1; selected += 1;
if item.metadata.is_dir() { if item.metadata.is_dir() {
selected_mount_point += item.is_mount_point as i32; selected_mount_point += i32::from(item.is_mount_point);
selected_dir += 1; selected_dir += 1;
} }
match &item.location_opt { match &item.location_opt {
@ -131,7 +131,7 @@ pub fn context_menu<'a>(
selected_types.push(item.mime.clone()); selected_types.push(item.mime.clone());
} }
} }
}; }
selected_types.sort_unstable(); selected_types.sort_unstable();
selected_types.dedup(); selected_types.dedup();
selected_trash_only = selected_trash_only && selected == 1; selected_trash_only = selected_trash_only && selected == 1;
@ -168,7 +168,7 @@ pub fn context_menu<'a>(
#[cfg(feature = "desktop")] #[cfg(feature = "desktop")]
{ {
for (i, action) in entry.desktop_actions.into_iter().enumerate() { for (i, action) in entry.desktop_actions.into_iter().enumerate() {
children.push(menu_item(action.name, Action::ExecEntryAction(i)).into()) children.push(menu_item(action.name, Action::ExecEntryAction(i)).into());
} }
} }
children.push(divider::horizontal::light().into()); children.push(divider::horizontal::light().into());
@ -396,12 +396,12 @@ pub fn dialog_menu(
let mut selected_gallery = 0; let mut selected_gallery = 0;
if let Some(items) = tab.items_opt() { if let Some(items) = tab.items_opt() {
for item in items.iter() { for item in items {
if item.selected && item.can_gallery() { if item.selected && item.can_gallery() {
selected_gallery += 1; selected_gallery += 1;
} }
} }
}; }
MenuBar::new(vec![ MenuBar::new(vec![
menu::Tree::with_children( menu::Tree::with_children(
@ -530,7 +530,7 @@ pub fn menu_bar<'a>(
modifiers: &Modifiers, modifiers: &Modifiers,
key_binds: &HashMap<KeyBind, Action>, key_binds: &HashMap<KeyBind, Action>,
) -> Element<'a, Message> { ) -> Element<'a, Message> {
let sort_options = tab_opt.map(|tab| tab.sort_options()); let sort_options = tab_opt.map(Tab::sort_options);
let sort_item = |label, sort, dir| { let sort_item = |label, sort, dir| {
menu::Item::CheckBox( menu::Item::CheckBox(
label, label,
@ -547,7 +547,7 @@ pub fn menu_bar<'a>(
let mut selected = 0; let mut selected = 0;
let mut selected_gallery = 0; let mut selected_gallery = 0;
if let Some(items) = tab_opt.and_then(|tab| tab.items_opt()) { if let Some(items) = tab_opt.and_then(|tab| tab.items_opt()) {
for item in items.iter() { for item in items {
if item.selected { if item.selected {
selected += 1; selected += 1;
if item.metadata.is_dir() { if item.metadata.is_dir() {
@ -558,7 +558,7 @@ pub fn menu_bar<'a>(
} }
} }
} }
}; }
let (delete_item, delete_item_action) = if in_trash || modifiers.shift() { let (delete_item, delete_item_action) = if in_trash || modifiers.shift() {
(fl!("delete-permanently"), Action::Delete) (fl!("delete-permanently"), Action::Delete)

View file

@ -54,7 +54,7 @@ pub fn exec_to_command(
// Number of args before the field code. // Number of args before the field code.
// This won't be an off by one err below because take is not zero indexed. // This won't be an off by one err below because take is not zero indexed.
let field_code_pos = field_code_pos.unwrap_or_default(); let field_code_pos = field_code_pos.unwrap_or_default();
let mut processes = match args_handler.map(|s| s.as_str()) { let mut processes = match args_handler.map(String::as_str) {
Some("%f") => { Some("%f") => {
let mut processes = Vec::with_capacity(path_opt.len()); let mut processes = Vec::with_capacity(path_opt.len());
@ -137,7 +137,7 @@ pub fn exec_to_command(
if !EXEC_HANDLERS.contains(&field_code) if !EXEC_HANDLERS.contains(&field_code)
&& !DEPRECATED_HANDLERS.contains(&field_code) && !DEPRECATED_HANDLERS.contains(&field_code)
{ {
log::warn!("unsupported Exec code {:?} in {:?}", field_code, exec); log::warn!("unsupported Exec code {field_code:?} in {exec:?}");
return None; return None;
} }
} }
@ -215,8 +215,7 @@ fn filename_eq(path_opt: &Option<PathBuf>, filename: &str) -> bool {
path_opt path_opt
.as_ref() .as_ref()
.and_then(|path| path.file_name()) .and_then(|path| path.file_name())
.map(|x| x == filename) .is_some_and(|x| x == filename)
.unwrap_or(false)
} }
pub struct MimeAppCache { pub struct MimeAppCache {
@ -259,12 +258,12 @@ impl MimeAppCache {
// Load desktop applications by supported mime types // Load desktop applications by supported mime types
//TODO: hashmap for all apps by id? //TODO: hashmap for all apps by id?
let all_apps: Vec<_> = desktop::load_applications(locale, false, None).collect(); let all_apps: Vec<_> = desktop::load_applications(locale, false, None).collect();
for app in all_apps.iter() { for app in &all_apps {
//TODO: just collect apps that can be executed with a file argument? //TODO: just collect apps that can be executed with a file argument?
if !app.mime_types.is_empty() { if !app.mime_types.is_empty() {
self.apps.push(MimeApp::from(app)); self.apps.push(MimeApp::from(app));
} }
for mime in app.mime_types.iter() { for mime in &app.mime_types {
let apps = self let apps = self
.cache .cache
.entry(mime.clone()) .entry(mime.clone())
@ -273,7 +272,7 @@ impl MimeAppCache {
apps.push(MimeApp::from(app)); apps.push(MimeApp::from(app));
} }
} }
for category in app.categories.iter() { for category in &app.categories {
if category == "TerminalEmulator" { if category == "TerminalEmulator" {
self.terminals.push(MimeApp::from(app)); self.terminals.push(MimeApp::from(app));
break; break;
@ -284,7 +283,7 @@ impl MimeAppCache {
let desktops: Vec<String> = env::var("XDG_CURRENT_DESKTOP") let desktops: Vec<String> = env::var("XDG_CURRENT_DESKTOP")
.unwrap_or_default() .unwrap_or_default()
.split(':') .split(':')
.map(|x| x.to_ascii_lowercase()) .map(str::to_ascii_lowercase)
.collect(); .collect();
// Load mimeapps.list files // Load mimeapps.list files
@ -315,7 +314,7 @@ impl MimeAppCache {
let entry = match freedesktop_entry_parser::parse_entry(&path) { let entry = match freedesktop_entry_parser::parse_entry(&path) {
Ok(ok) => ok, Ok(ok) => ok,
Err(err) => { Err(err) => {
log::warn!("failed to parse {:?}: {}", path, err); log::warn!("failed to parse {}: {}", path.display(), err);
continue; continue;
} }
}; };
@ -328,7 +327,7 @@ impl MimeAppCache {
if let Ok(mime) = attr.name.parse::<Mime>() { if let Ok(mime) = attr.name.parse::<Mime>() {
if let Some(filenames) = attr.value { if let Some(filenames) = attr.value {
for filename in filenames.split_terminator(';') { for filename in filenames.split_terminator(';') {
log::trace!("add {}={}", mime, filename); log::trace!("add {mime}={filename}");
let apps = self let apps = self
.cache .cache
.entry(mime.clone()) .entry(mime.clone())
@ -340,9 +339,7 @@ impl MimeAppCache {
apps.push(MimeApp::from(app)); apps.push(MimeApp::from(app));
} else { } else {
log::info!( log::info!(
"failed to add association for {:?}: application {:?} not found", "failed to add association for {mime:?}: application {filename:?} not found"
mime,
filename
); );
} }
} }
@ -355,7 +352,7 @@ impl MimeAppCache {
if let Ok(mime) = attr.name.parse::<Mime>() { if let Ok(mime) = attr.name.parse::<Mime>() {
if let Some(filenames) = attr.value { if let Some(filenames) = attr.value {
for filename in filenames.split_terminator(';') { for filename in filenames.split_terminator(';') {
log::trace!("remove {}={}", mime, filename); log::trace!("remove {mime}={filename}");
if let Some(apps) = self.cache.get_mut(&mime) { if let Some(apps) = self.cache.get_mut(&mime) {
apps.retain(|x| !filename_eq(&x.path, filename)); apps.retain(|x| !filename_eq(&x.path, filename));
} }
@ -368,7 +365,7 @@ impl MimeAppCache {
if let Ok(mime) = attr.name.parse::<Mime>() { if let Ok(mime) = attr.name.parse::<Mime>() {
if let Some(filenames) = attr.value { if let Some(filenames) = attr.value {
for filename in filenames.split_terminator(';') { for filename in filenames.split_terminator(';') {
log::trace!("default {}={}", mime, filename); log::trace!("default {mime}={filename}");
if let Some(apps) = self.cache.get_mut(&mime) { if let Some(apps) = self.cache.get_mut(&mime) {
let mut found = false; let mut found = false;
for app in apps.iter_mut() { for app in apps.iter_mut() {
@ -381,13 +378,10 @@ impl MimeAppCache {
} }
if found { if found {
break; break;
} else {
log::debug!(
"failed to set default for {:?}: application {:?} not found",
mime,
filename
);
} }
log::debug!(
"failed to set default for {mime:?}: application {filename:?} not found"
);
} }
} }
} }
@ -412,7 +406,7 @@ impl MimeAppCache {
// Copy icons to special cache // Copy icons to special cache
//TODO: adjust dropdown API so this is no longer needed //TODO: adjust dropdown API so this is no longer needed
for (mime, apps) in self.cache.iter() { for (mime, apps) in &self.cache {
self.icons.insert( self.icons.insert(
mime.clone(), mime.clone(),
apps.iter().map(|app| app.icon.clone()).collect(), apps.iter().map(|app| app.icon.clone()).collect(),
@ -420,7 +414,7 @@ impl MimeAppCache {
} }
let elapsed = start.elapsed(); let elapsed = start.elapsed();
log::info!("loaded mime app cache in {:?}", elapsed); log::info!("loaded mime app cache in {elapsed:?}");
} }
pub fn apps(&self) -> &[MimeApp] { pub fn apps(&self) -> &[MimeApp] {
@ -428,13 +422,11 @@ impl MimeAppCache {
} }
pub fn get(&self, key: &Mime) -> &[MimeApp] { pub fn get(&self, key: &Mime) -> &[MimeApp] {
static EMPTY: Vec<MimeApp> = Vec::new(); self.cache.get(key).map_or(&[], Vec::as_slice)
self.cache.get(key).unwrap_or(&EMPTY)
} }
pub fn icons(&self, key: &Mime) -> &[widget::icon::Handle] { pub fn icons(&self, key: &Mime) -> &[widget::icon::Handle] {
static EMPTY: Vec<widget::icon::Handle> = Vec::new(); self.icons.get(key).map_or(&[], Vec::as_slice)
self.icons.get(key).unwrap_or(&EMPTY)
} }
fn get_default_terminal(&self) -> Option<String> { fn get_default_terminal(&self) -> Option<String> {
@ -466,7 +458,7 @@ impl MimeAppCache {
} }
for id in &preference_order { for id in &preference_order {
for terminal in self.terminals.iter() { for terminal in &self.terminals {
if &terminal.id == id { if &terminal.id == id {
return Some(terminal); return Some(terminal);
} }
@ -498,7 +490,7 @@ impl MimeAppCache {
} }
Err(err) => { Err(err) => {
if err.kind() != io::ErrorKind::NotFound { if err.kind() != io::ErrorKind::NotFound {
log::warn!("failed to read {path:?}: {err}"); log::warn!("failed to read {}: {}", path.display(), err);
return; return;
} }
} }
@ -517,7 +509,7 @@ impl MimeAppCache {
self.reload(); self.reload();
} }
Err(err) => { Err(err) => {
log::warn!("failed to write {path:?}: {err}"); log::warn!("failed to write {}: {}", path.display(), err);
} }
} }
} }

View file

@ -100,9 +100,10 @@ fn network_scan(uri: &str, sizes: IconSizes) -> Result<Vec<tab::Item>, String> {
let metadata = if !force_dir && !info.boolean(gio::FILE_ATTRIBUTE_FILESYSTEM_REMOTE) { let metadata = if !force_dir && !info.boolean(gio::FILE_ATTRIBUTE_FILESYSTEM_REMOTE) {
let mtime = info.attribute_uint64(gio::FILE_ATTRIBUTE_TIME_MODIFIED); let mtime = info.attribute_uint64(gio::FILE_ATTRIBUTE_TIME_MODIFIED);
let is_dir = matches!(info.file_type(), gio::FileType::Directory); let is_dir = matches!(info.file_type(), gio::FileType::Directory);
let size_opt = match is_dir { let size_opt = if is_dir {
true => None, None
false => Some(info.size() as u64), } else {
Some(info.size() as u64)
}; };
let mut children_opt = None; let mut children_opt = None;
@ -114,7 +115,7 @@ fn network_scan(uri: &str, sizes: IconSizes) -> Result<Vec<tab::Item>, String> {
children_opt = Some(entries.count()); children_opt = Some(entries.count());
} }
Err(err) => { Err(err) => {
log::warn!("failed to read directory {:?}: {}", path, err); log::warn!("failed to read directory {}: {}", path.display(), err);
children_opt = Some(0); children_opt = Some(0);
} }
} }
@ -287,7 +288,7 @@ impl Item {
self.name.clone() self.name.clone()
} }
pub fn is_mounted(&self) -> bool { pub const fn is_mounted(&self) -> bool {
self.is_mounted self.is_mounted
} }
@ -395,7 +396,7 @@ impl Gvfs {
continue; continue;
} }
log::info!("mount {}", name); log::info!("mount {name}");
//TODO: do not use name as a URI for mount_op //TODO: do not use name as a URI for mount_op
let mount_op = mount_op(name.to_string(), event_tx.clone()); let mount_op = mount_op(name.to_string(), event_tx.clone());
let event_tx = event_tx.clone(); let event_tx = event_tx.clone();
@ -406,7 +407,7 @@ impl Gvfs {
Some(&mount_op), Some(&mount_op),
gio::Cancellable::NONE, gio::Cancellable::NONE,
move |res| { move |res| {
log::info!("mount {}: result {:?}", name, res); log::info!("mount {name}: result {res:?}");
event_tx.send(Event::MountResult(mounter_item, match res { event_tx.send(Event::MountResult(mounter_item, match res {
Ok(()) => { Ok(()) => {
_ = complete_tx.send(Ok(())); _ = complete_tx.send(Ok(()));
@ -416,7 +417,7 @@ impl Gvfs {
_ = complete_tx.send(Err(anyhow::anyhow!("{err:?}"))); _ = complete_tx.send(Err(anyhow::anyhow!("{err:?}")));
match err.kind::<gio::IOErrorEnum>() { match err.kind::<gio::IOErrorEnum>() {
Some(gio::IOErrorEnum::FailedHandled) => Ok(false), Some(gio::IOErrorEnum::FailedHandled) => Ok(false),
_ => Err(format!("{}", err)) _ => Err(format!("{err}"))
}} }}
})).unwrap(); })).unwrap();
}, },
@ -433,7 +434,7 @@ impl Gvfs {
Some(&mount_op), Some(&mount_op),
gio::Cancellable::NONE, gio::Cancellable::NONE,
move |res| { move |res| {
log::info!("network drive {}: result {:?}", uri, res); log::info!("network drive {uri}: result {res:?}");
event_tx.send(Event::NetworkResult(uri, match res { event_tx.send(Event::NetworkResult(uri, match res {
Ok(()) => { Ok(()) => {
_ = result_tx.send(Ok(())); _ = result_tx.send(Ok(()));
@ -442,7 +443,7 @@ impl Gvfs {
_ = result_tx.send(Err(anyhow::anyhow!("{err:?}"))); _ = result_tx.send(Err(anyhow::anyhow!("{err:?}")));
match err.kind::<gio::IOErrorEnum>() { match err.kind::<gio::IOErrorEnum>() {
Some(gio::IOErrorEnum::FailedHandled) => Ok(false), Some(gio::IOErrorEnum::FailedHandled) => Ok(false),
_ => Err(format!("{}", err)) _ => Err(format!("{err}"))
}} }}
})).unwrap(); })).unwrap();
} }
@ -475,7 +476,7 @@ impl Gvfs {
Some(&mount_op), Some(&mount_op),
gio::Cancellable::NONE, gio::Cancellable::NONE,
move |res| { move |res| {
log::info!("network scan mounted {}: result {:?}", uri, res); log::info!("network scan mounted {uri}: result {res:?}");
// FIXME sometimes a uri can be mounted and then not recognized as mounted... // FIXME sometimes a uri can be mounted and then not recognized as mounted...
// seems to be related to uri with a path // seems to be related to uri with a path
items_tx.blocking_send(network_scan(&original_uri, sizes)).unwrap(); items_tx.blocking_send(network_scan(&original_uri, sizes)).unwrap();
@ -485,7 +486,7 @@ impl Gvfs {
}, },
Err(err) => match err.kind::<gio::IOErrorEnum>() { Err(err) => match err.kind::<gio::IOErrorEnum>() {
Some(gio::IOErrorEnum::FailedHandled) => Ok(false), Some(gio::IOErrorEnum::FailedHandled) => Ok(false),
_ => Err(format!("{}", err)) _ => Err(format!("{err}"))
} }
})).unwrap(); })).unwrap();
} }
@ -509,25 +510,25 @@ impl Gvfs {
} }
if MountExt::can_eject(&mount) { if MountExt::can_eject(&mount) {
log::info!("eject {}", name); log::info!("eject {name}");
MountExt::eject_with_operation( MountExt::eject_with_operation(
&mount, &mount,
gio::MountUnmountFlags::NONE, gio::MountUnmountFlags::NONE,
gio::MountOperation::NONE, gio::MountOperation::NONE,
gio::Cancellable::NONE, gio::Cancellable::NONE,
move |result| { move |result| {
log::info!("eject {}: result {:?}", name, result); log::info!("eject {name}: result {result:?}");
}, },
); );
} else { } else {
log::info!("unmount {}", name); log::info!("unmount {name}");
MountExt::unmount_with_operation( MountExt::unmount_with_operation(
&mount, &mount,
gio::MountUnmountFlags::NONE, gio::MountUnmountFlags::NONE,
gio::MountOperation::NONE, gio::MountOperation::NONE,
gio::Cancellable::NONE, gio::Cancellable::NONE,
move |result| { move |result| {
log::info!("unmount {}: result {:?}", name, result); log::info!("unmount {name}: result {result:?}");
}, },
); );
} }
@ -536,7 +537,7 @@ impl Gvfs {
} }
} }
}); });
main_loop.run() main_loop.run();
}); });
Self { Self {
command_tx, command_tx,
@ -615,7 +616,7 @@ impl Mounter for Gvfs {
match event { match event {
Event::Changed => command_tx.send(Cmd::Rescan).unwrap(), Event::Changed => command_tx.send(Cmd::Rescan).unwrap(),
Event::Items(items) => { Event::Items(items) => {
output.send(MounterMessage::Items(items)).await.unwrap() output.send(MounterMessage::Items(items)).await.unwrap();
} }
Event::MountResult(item, res) => output Event::MountResult(item, res) => output
.send(MounterMessage::MountResult(item, res)) .send(MounterMessage::MountResult(item, res))

View file

@ -199,7 +199,7 @@ impl<'a, Message> MouseArea<'a, Message> {
} }
#[must_use] #[must_use]
pub fn show_drag_rect(mut self, show_drag_rect: bool) -> Self { pub const fn show_drag_rect(mut self, show_drag_rect: bool) -> Self {
self.show_drag_rect = show_drag_rect; self.show_drag_rect = show_drag_rect;
self self
} }
@ -379,7 +379,7 @@ where
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
viewport: &Rectangle, viewport: &Rectangle,
) -> event::Status { ) -> event::Status {
if let event::Status::Captured = self.content.as_widget_mut().on_event( if self.content.as_widget_mut().on_event(
&mut tree.children[0], &mut tree.children[0],
event.clone(), event.clone(),
layout, layout,
@ -388,7 +388,8 @@ where
clipboard, clipboard,
shell, shell,
viewport, viewport,
) { ) == event::Status::Captured
{
return event::Status::Captured; return event::Status::Captured;
} }
@ -507,7 +508,7 @@ where
Renderer: 'a + renderer::Renderer, Renderer: 'a + renderer::Renderer,
Theme: 'a, Theme: 'a,
{ {
fn from(area: MouseArea<'a, Message>) -> Element<'a, Message> { fn from(area: MouseArea<'a, Message>) -> Self {
Element::new(area) Element::new(area)
} }
} }
@ -538,12 +539,12 @@ fn update<Message: Clone>(
match (position_in, state.last_position) { match (position_in, state.last_position) {
(None, Some(_)) => { (None, Some(_)) => {
if let Some(message) = widget.on_exit.as_ref() { if let Some(message) = widget.on_exit.as_ref() {
shell.publish(message()) shell.publish(message());
} }
} }
(Some(_), None) => { (Some(_), None) => {
if let Some(message) = widget.on_enter.as_ref() { if let Some(message) = widget.on_enter.as_ref() {
shell.publish(message()) shell.publish(message());
} }
} }
_ => {} _ => {}
@ -632,8 +633,7 @@ fn update<Message: Clone>(
let recent_click = state let recent_click = state
.prev_click .prev_click
.as_ref() .as_ref()
.map(|(_, i)| Instant::now().duration_since(*i) <= DOUBLE_CLICK_DURATION) .is_some_and(|(_, i)| Instant::now().duration_since(*i) <= DOUBLE_CLICK_DURATION);
.unwrap_or_default();
if matches!( if matches!(
event, event,
Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left))
@ -653,7 +653,10 @@ fn update<Message: Clone>(
} }
if let Some(message) = widget.on_right_press.as_ref() { if let Some(message) = widget.on_right_press.as_ref() {
if let Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Right)) = event { if matches!(
event,
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Right))
) {
let point_opt = if widget.on_right_press_window_position { let point_opt = if widget.on_right_press_window_position {
cursor.position_over(layout_bounds).map(|mut p| { cursor.position_over(layout_bounds).map(|mut p| {
p.x -= offset.x; p.x -= offset.x;
@ -667,14 +670,16 @@ fn update<Message: Clone>(
if widget.on_right_press_no_capture { if widget.on_right_press_no_capture {
return event::Status::Ignored; return event::Status::Ignored;
} else {
return event::Status::Captured;
} }
return event::Status::Captured;
} }
} }
if let Some(message) = widget.on_right_release.as_ref() { if let Some(message) = widget.on_right_release.as_ref() {
if let Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Right)) = event { if matches!(
event,
Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Right))
) {
shell.publish(message(cursor.position_in(layout_bounds))); shell.publish(message(cursor.position_in(layout_bounds)));
return event::Status::Captured; return event::Status::Captured;
@ -682,7 +687,10 @@ fn update<Message: Clone>(
} }
if let Some(message) = widget.on_middle_press.as_ref() { if let Some(message) = widget.on_middle_press.as_ref() {
if let Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Middle)) = event { if matches!(
event,
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Middle))
) {
shell.publish(message(cursor.position_in(layout_bounds))); shell.publish(message(cursor.position_in(layout_bounds)));
return event::Status::Captured; return event::Status::Captured;
@ -690,7 +698,10 @@ fn update<Message: Clone>(
} }
if let Some(message) = widget.on_middle_release.as_ref() { if let Some(message) = widget.on_middle_release.as_ref() {
if let Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Middle)) = event { if matches!(
event,
Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Middle))
) {
shell.publish(message(cursor.position_in(layout_bounds))); shell.publish(message(cursor.position_in(layout_bounds)));
return event::Status::Captured; return event::Status::Captured;
@ -698,7 +709,10 @@ fn update<Message: Clone>(
} }
if let Some(message) = widget.on_back_press.as_ref() { if let Some(message) = widget.on_back_press.as_ref() {
if let Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Back)) = event { if matches!(
event,
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Back))
) {
shell.publish(message(cursor.position_in(layout_bounds))); shell.publish(message(cursor.position_in(layout_bounds)));
return event::Status::Captured; return event::Status::Captured;
@ -706,7 +720,10 @@ fn update<Message: Clone>(
} }
if let Some(message) = widget.on_back_release.as_ref() { if let Some(message) = widget.on_back_release.as_ref() {
if let Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Back)) = event { if matches!(
event,
Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Back))
) {
shell.publish(message(cursor.position_in(layout_bounds))); shell.publish(message(cursor.position_in(layout_bounds)));
return event::Status::Captured; return event::Status::Captured;
@ -714,7 +731,10 @@ fn update<Message: Clone>(
} }
if let Some(message) = widget.on_forward_press.as_ref() { if let Some(message) = widget.on_forward_press.as_ref() {
if let Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Forward)) = event { if matches!(
event,
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Forward))
) {
shell.publish(message(cursor.position_in(layout_bounds))); shell.publish(message(cursor.position_in(layout_bounds)));
return event::Status::Captured; return event::Status::Captured;
@ -722,7 +742,10 @@ fn update<Message: Clone>(
} }
if let Some(message) = widget.on_forward_release.as_ref() { if let Some(message) = widget.on_forward_release.as_ref() {
if let Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Forward)) = event { if matches!(
event,
Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Forward))
) {
shell.publish(message(cursor.position_in(layout_bounds))); shell.publish(message(cursor.position_in(layout_bounds)));
return event::Status::Captured; return event::Status::Captured;

View file

@ -1,7 +1,7 @@
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use tokio::sync::Notify; use tokio::sync::Notify;
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ControllerState { pub enum ControllerState {
Cancelled, Cancelled,
Failed, Failed,

View file

@ -36,7 +36,7 @@ async fn handle_replace(
let item_from = match tab::item_from_path(file_from, IconSizes::default()) { let item_from = match tab::item_from_path(file_from, IconSizes::default()) {
Ok(ok) => ok, Ok(ok) => ok,
Err(err) => { Err(err) => {
log::warn!("{}", err); log::warn!("{err}");
return ReplaceResult::Cancel; return ReplaceResult::Cancel;
} }
}; };
@ -44,7 +44,7 @@ async fn handle_replace(
let item_to = match tab::item_from_path(file_to, IconSizes::default()) { let item_to = match tab::item_from_path(file_to, IconSizes::default()) {
Ok(ok) => ok, Ok(ok) => ok,
Err(err) => { Err(err) => {
log::warn!("{}", err); log::warn!("{err}");
return ReplaceResult::Cancel; return ReplaceResult::Cancel;
} }
}; };
@ -98,13 +98,13 @@ async fn copy_or_move(
compio::runtime::spawn(async move { compio::runtime::spawn(async move {
let controller = controller_c; let controller = controller_c;
log::info!( log::info!(
"{} {:?} to {:?}", "{} {:?} to {}",
match method { match method {
Method::Copy => "Copy", Method::Copy => "Copy",
Method::Move { .. } => "Move", Method::Move { .. } => "Move",
}, },
paths, paths,
to to.display()
); );
// Handle duplicate file names by renaming paths // Handle duplicate file names by renaming paths
@ -141,12 +141,15 @@ async fn copy_or_move(
//TODO: use compio::fs::rename? //TODO: use compio::fs::rename?
match fs::rename(from, to) { match fs::rename(from, to) {
Ok(()) => { Ok(()) => {
log::info!("renamed {from:?} to {to:?}"); log::info!("renamed {} to {}", from.display(), to.display());
false false
} }
Err(err) => { Err(err) => {
log::info!( log::info!(
"failed to rename {from:?} to {to:?}, fallback to recursive move: {err}" "failed to rename {} to {}, fallback to recursive move: {}",
from.display(),
to.display(),
err
); );
true true
} }
@ -227,15 +230,14 @@ fn copy_unique_path(from: &Path, to: &Path) -> PathBuf {
.unwrap_or_else(|| { .unwrap_or_else(|| {
from.file_stem() from.file_stem()
.and_then(|s| s.to_str()) .and_then(|s| s.to_str())
.map(|stem| { .map_or((file_name, None), |stem| {
( (
stem.to_string(), stem.to_string(),
from.extension() from.extension()
.and_then(|e| e.to_str()) .and_then(|e| e.to_str())
.map(|e| e.to_string()), .map(str::to_string),
) )
}) })
.unwrap_or((file_name, None))
}) })
}; };
@ -283,7 +285,7 @@ fn paths_parent_name(paths: &[PathBuf]) -> Cow<'_, str> {
return fl!("unknown-folder").into(); return fl!("unknown-folder").into();
}; };
for path in paths.iter() { for path in paths {
//TODO: is it possible to have different parents, and what should be returned? //TODO: is it possible to have different parents, and what should be returned?
if path.parent() != Some(parent) { if path.parent() != Some(parent) {
return fl!("unknown-folder").into(); return fl!("unknown-folder").into();
@ -397,18 +399,18 @@ impl OperationError {
pub fn from_err<T: ToString>(err: T, controller: &Controller) -> Self { pub fn from_err<T: ToString>(err: T, controller: &Controller) -> Self {
controller.set_state(ControllerState::Failed); controller.set_state(ControllerState::Failed);
OperationError { Self {
kind: OperationErrorType::Generic(err.to_string()), kind: OperationErrorType::Generic(err.to_string()),
} }
} }
pub fn from_kind(kind: OperationErrorType, controller: &Controller) -> Self { pub fn from_kind(kind: OperationErrorType, controller: &Controller) -> Self {
controller.set_state(ControllerState::Failed); controller.set_state(ControllerState::Failed);
OperationError { kind } Self { kind }
} }
pub fn from_msg(m: impl Into<String>) -> Self { pub fn from_msg(m: impl Into<String>) -> Self {
OperationError { Self {
kind: OperationErrorType::Generic(m.into()), kind: OperationErrorType::Generic(m.into()),
} }
} }
@ -572,7 +574,7 @@ impl Operation {
} }
} }
pub fn show_progress_notification(&self) -> bool { pub const fn show_progress_notification(&self) -> bool {
// Long running operations show a progress notification // Long running operations show a progress notification
match self { match self {
Self::Compress { .. } Self::Compress { .. }
@ -625,7 +627,7 @@ impl Operation {
let controller = controller_c; let controller = controller_c;
let Some(relative_root) = to.parent() else { let Some(relative_root) = to.parent() else {
return Err(OperationError::from_err( return Err(OperationError::from_err(
format!("path {:?} has no parent directory", to), format!("path {} has no parent directory", to.display()),
&controller, &controller,
)); ));
}; };
@ -636,7 +638,7 @@ impl Operation {
}; };
let mut paths = paths; let mut paths = paths;
for path in paths.clone().iter() { for path in &paths.clone() {
if path.is_dir() { if path.is_dir() {
let new_paths_it = WalkDir::new(path).into_iter(); let new_paths_it = WalkDir::new(path).into_iter();
for entry in new_paths_it.skip(1) { for entry in new_paths_it.skip(1) {
@ -1011,7 +1013,7 @@ impl Operation {
} }
Self::RemoveFromRecents { paths } => { Self::RemoveFromRecents { paths } => {
tokio::task::spawn_blocking(move || { tokio::task::spawn_blocking(move || {
let path_refs = paths.iter().map(|p| p.as_ref()).collect::<Vec<&Path>>(); let path_refs = paths.iter().map(PathBuf::as_path).collect::<Vec<&Path>>();
recently_used_xbel::remove_recently_used(&path_refs) recently_used_xbel::remove_recently_used(&path_refs)
}) })
.await .await
@ -1205,7 +1207,7 @@ mod tests {
debug!("[{id}] Replace request"); debug!("[{id}] Replace request");
tx.send(ReplaceResult::Cancel) tx.send(ReplaceResult::Cancel)
.await .await
.expect("Sending a response to a replace request should succeed") .expect("Sending a response to a replace request should succeed");
} }
_ => unreachable!( _ => unreachable!(
"Only [ `Message::PendingProgress`, `Message::DialogPush(DialogPage::Replace)` ] are sent from operation" "Only [ `Message::PendingProgress`, `Message::DialogPush(DialogPage::Replace)` ] are sent from operation"

View file

@ -68,7 +68,7 @@ impl Context {
continue; continue;
} }
for entry in WalkDir::new(&from_parent).into_iter() { for entry in WalkDir::new(&from_parent) {
self.controller self.controller
.check() .check()
.await .await
@ -76,7 +76,11 @@ impl Context {
let entry = entry.map_err(|err| { let entry = entry.map_err(|err| {
OperationError::from_err( OperationError::from_err(
format!("failed to walk directory {:?}: {}", from_parent, err), format!(
"failed to walk directory {}: {}",
from_parent.display(),
err
),
&self.controller, &self.controller,
) )
})?; })?;
@ -92,7 +96,7 @@ impl Context {
} else if file_type.is_symlink() { } else if file_type.is_symlink() {
let target = fs::read_link(&from).map_err(|err| { let target = fs::read_link(&from).map_err(|err| {
OperationError::from_err( OperationError::from_err(
format!("failed to read link {:?}: {}", from, err), format!("failed to read link {}: {}", from_parent.display(), err),
&self.controller, &self.controller,
) )
})?; })?;
@ -111,8 +115,10 @@ impl Context {
let relative = from.strip_prefix(&from_parent).map_err(|err| { let relative = from.strip_prefix(&from_parent).map_err(|err| {
OperationError::from_err( OperationError::from_err(
format!( format!(
"failed to remove prefix {:?} from {:?}: {}", "failed to remove prefix {} from {}: {}",
from_parent, from, err from_parent.display(),
from.display(),
err
), ),
&self.controller, &self.controller,
) )
@ -163,8 +169,11 @@ impl Context {
if op.run(self, progress).await.map_err(|err| { if op.run(self, progress).await.map_err(|err| {
OperationError::from_err( OperationError::from_err(
format!( format!(
"failed to {:?} {:?} to {:?}: {}", "failed to {:?} {} to {}: {}",
op.kind, op.from, op.to, err op.kind,
op.from.display(),
op.to.display(),
err
), ),
&self.controller, &self.controller,
) )
@ -209,7 +218,7 @@ impl Context {
} }
ReplaceResult::KeepBoth => match op.to.parent() { ReplaceResult::KeepBoth => match op.to.parent() {
Some(to_parent) => Ok(ControlFlow::Continue(copy_unique_path(&op.from, to_parent))), Some(to_parent) => Ok(ControlFlow::Continue(copy_unique_path(&op.from, to_parent))),
None => Err(format!("failed to get parent of {:?}", op.to).into()), None => Err(format!("failed to get parent of {}", op.to.display()).into()),
}, },
ReplaceResult::Skip(apply_to_all) => { ReplaceResult::Skip(apply_to_all) => {
if apply_to_all { if apply_to_all {
@ -319,7 +328,11 @@ impl Op {
(ctx.on_progress)(self, &progress); (ctx.on_progress)(self, &progress);
if let Err(err) = to_file.set_permissions(metadata.permissions()).await { if let Err(err) = to_file.set_permissions(metadata.permissions()).await {
// This error is not propagated upwards as some filesystems do not support setting permissions // This error is not propagated upwards as some filesystems do not support setting permissions
log::warn!("failed to set permissions for {:?}: {}", self.to, err); log::warn!(
"failed to set permissions for {}: {}",
self.to.display(),
err
);
} }
// Prevent spamming the progress callbacks. // Prevent spamming the progress callbacks.
@ -402,7 +415,7 @@ impl Op {
self.skipped.cleanup.set(true); self.skipped.cleanup.set(true);
} }
// Try standard copy if hard link fails with cross device error // Try standard copy if hard link fails with cross device error
let mut copy_op = Op { let mut copy_op = Self {
kind: OpKind::Copy, kind: OpKind::Copy,
from: self.from.clone(), from: self.from.clone(),
to: self.to.clone(), to: self.to.clone(),
@ -410,9 +423,8 @@ impl Op {
is_cleanup: self.is_cleanup, is_cleanup: self.is_cleanup,
}; };
return Box::pin(copy_op.run(ctx, progress)).await; return Box::pin(copy_op.run(ctx, progress)).await;
} else {
return Err(err.into());
} }
return Err(err.into());
} }
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -14,7 +14,7 @@ use tempfile::NamedTempFile;
use url::Url; use url::Url;
/// Implements thumbnail caching based on the freedesktop.org Thumbnail Managing Standard. /// Implements thumbnail caching based on the freedesktop.org Thumbnail Managing Standard.
/// https://specifications.freedesktop.org/thumbnail-spec/latest/ /// <https://specifications.freedesktop.org/thumbnail-spec/latest>/
pub struct ThumbnailCacher { pub struct ThumbnailCacher {
file_path: PathBuf, file_path: PathBuf,
file_uri: String, file_uri: String,
@ -27,18 +27,22 @@ pub struct ThumbnailCacher {
impl ThumbnailCacher { impl ThumbnailCacher {
pub fn new(file_path: &Path, thumbnail_size: ThumbnailSize) -> Result<Self, String> { pub fn new(file_path: &Path, thumbnail_size: ThumbnailSize) -> Result<Self, String> {
let file_uri = thumbnail_uri(file_path) let file_uri = thumbnail_uri(file_path)
.map_err(|err| format!("failed to create URI for {file_path:?}: {err}"))?; .map_err(|err| format!("failed to create URI for {}: {}", file_path.display(), err))?;
let cache_base_dir = THUMBNAIL_CACHE_BASE_DIR let cache_base_dir = THUMBNAIL_CACHE_BASE_DIR
.as_ref() .as_ref()
.ok_or("failed to get thumbnail cache directory".to_string())?; .ok_or("failed to get thumbnail cache directory".to_string())?;
let thumbnail_filename = thumbnail_cache_filename(&file_uri); let thumbnail_filename = thumbnail_cache_filename(&file_uri);
let thumbnail_dir = cache_base_dir.join(thumbnail_size.subdirectory_name()); let thumbnail_dir = cache_base_dir.join(thumbnail_size.subdirectory_name());
if !thumbnail_dir.is_dir() { if !thumbnail_dir.is_dir() {
log::warn!("{:?} is not a directory, creating one now", &thumbnail_dir); log::warn!(
fs::create_dir_all(&thumbnail_dir).unwrap_or(log::error!( "{} is not a directory, creating one now",
"{:?} failed to create directory, this error can be expected on first run", thumbnail_dir.display()
&thumbnail_dir );
)); let _: () = log::error!(
"{} failed to create directory, this error can be expected on first run",
thumbnail_dir.display()
);
fs::create_dir_all(&thumbnail_dir).unwrap_or(());
} }
let thumbnail_path = thumbnail_dir.join(&thumbnail_filename); let thumbnail_path = thumbnail_dir.join(&thumbnail_filename);
let thumbnail_fail_marker_path = cache_base_dir let thumbnail_fail_marker_path = cache_base_dir
@ -64,7 +68,7 @@ impl ThumbnailCacher {
std::fs::metadata(&self.file_path), std::fs::metadata(&self.file_path),
) { ) {
if metadata.is_file() && self.file_path.starts_with(cache_base_dir) { if metadata.is_file() && self.file_path.starts_with(cache_base_dir) {
return CachedThumbnail::Valid((self.file_path.to_path_buf(), None)); return CachedThumbnail::Valid((self.file_path.clone(), None));
} }
} }
@ -204,7 +208,11 @@ impl ThumbnailCacher {
let reader = match decoder.read_info() { let reader = match decoder.read_info() {
Ok(reader) => reader, Ok(reader) => reader,
Err(err) => { Err(err) => {
log::warn!("failed to decode {thumbnail_path:?} as PNG: {err}"); log::warn!(
"failed to decode {} as PNG: {}",
thumbnail_path.display(),
err
);
return false; return false;
} }
}; };
@ -227,7 +235,11 @@ impl ThumbnailCacher {
let metadata = match std::fs::metadata(&self.file_path) { let metadata = match std::fs::metadata(&self.file_path) {
Ok(m) => m, Ok(m) => m,
Err(err) => { Err(err) => {
log::warn!("failed to get metatdata of {:?}: {}", self.file_path, err); log::warn!(
"failed to get metatdata of {}: {}",
self.file_path.display(),
err
);
return false; return false;
} }
}; };
@ -242,8 +254,8 @@ impl ThumbnailCacher {
Ok(m) => m, Ok(m) => m,
Err(err) => { Err(err) => {
log::warn!( log::warn!(
"failed to get modified from metatdata of {:?}, {}", "failed to get modified from metatdata of {}, {}",
self.file_path, self.file_path.display(),
err err
); );
return false; return false;
@ -279,16 +291,17 @@ impl ThumbnailCacher {
fn thumbnail_uri(path: &Path) -> io::Result<String> { fn thumbnail_uri(path: &Path) -> io::Result<String> {
let absolute_path = fs::canonicalize(path)?; let absolute_path = fs::canonicalize(path)?;
let url = Url::from_file_path(&absolute_path).map_err(|_| { let url = Url::from_file_path(&absolute_path).map_err(|()| {
io::Error::other(format!( io::Error::other(format!(
"failed to create URI for thumbnail_file: {absolute_path:?}" "failed to create URI for thumbnail_file: {}",
absolute_path.display()
)) ))
})?; })?;
// Technically square brackets don't need to be percent encoded, // Technically square brackets don't need to be percent encoded,
// and they aren't by the url crate, but the thumbnailer used by // and they aren't by the url crate, but the thumbnailer used by
// Gnome Files does. In order to share thumbnails and not get duplicates // Gnome Files does. In order to share thumbnails and not get duplicates
// we should do the same. // we should do the same.
let url = url.to_string().replace("[", "%5B").replace("]", "%5D"); let url = url.to_string().replace('[', "%5B").replace(']', "%5D");
Ok(url) Ok(url)
} }
@ -319,11 +332,11 @@ impl ThumbnailSize {
} }
} }
pub fn pixel_size(self) -> u32 { pub const fn pixel_size(self) -> u32 {
self as u32 self as u32
} }
pub fn subdirectory_name(self) -> &'static str { pub const fn subdirectory_name(self) -> &'static str {
match self { match self {
Self::Normal => "normal", Self::Normal => "normal",
Self::Large => "large", Self::Large => "large",

View file

@ -36,7 +36,7 @@ impl Thumbnailer {
command.arg(output); command.arg(output);
} }
"%s" => { "%s" => {
command.arg(format!("{}", thumbnail_size)); command.arg(format!("{thumbnail_size}"));
} }
_ => { _ => {
log::warn!( log::warn!(
@ -89,20 +89,24 @@ impl ThumbnailerCache {
let mut thumbnailer_paths = Vec::new(); let mut thumbnailer_paths = Vec::new();
for dir in search_dirs { for dir in search_dirs {
log::trace!("looking for thumbnailers in {:?}", dir); log::trace!("looking for thumbnailers in {}", dir.display());
match fs::read_dir(&dir) { match fs::read_dir(&dir) {
Ok(entries) => { Ok(entries) => {
for entry_res in entries { for entry_res in entries {
match entry_res { match entry_res {
Ok(entry) => thumbnailer_paths.push(entry.path()), Ok(entry) => thumbnailer_paths.push(entry.path()),
Err(err) => { Err(err) => {
log::warn!("failed to read entry in directory {:?}: {}", dir, err); log::warn!(
"failed to read entry in directory {}: {}",
dir.display(),
err
);
} }
} }
} }
} }
Err(err) => { Err(err) => {
log::warn!("failed to read directory {:?}: {}", dir, err); log::warn!("failed to read directory {}: {}", dir.display(), err);
} }
} }
} }
@ -112,7 +116,7 @@ impl ThumbnailerCache {
let entry = match freedesktop_entry_parser::parse_entry(&path) { let entry = match freedesktop_entry_parser::parse_entry(&path) {
Ok(ok) => ok, Ok(ok) => ok,
Err(err) => { Err(err) => {
log::warn!("failed to parse {:?}: {}", path, err); log::warn!("failed to parse {}: {}", path.display(), err);
continue; continue;
} }
}; };
@ -120,20 +124,23 @@ impl ThumbnailerCache {
//TODO: use TryExec? //TODO: use TryExec?
let section = entry.section("Thumbnailer Entry"); let section = entry.section("Thumbnailer Entry");
let Some(exec) = section.attr("Exec") else { let Some(exec) = section.attr("Exec") else {
log::warn!("missing Exec attribute for thumbnailer {:?}", path); log::warn!("missing Exec attribute for thumbnailer {}", path.display());
continue; continue;
}; };
let Some(mime_types) = section.attr("MimeType") else { let Some(mime_types) = section.attr("MimeType") else {
log::warn!("missing MimeType attribute for thumbnailer {:?}", path); log::warn!(
"missing MimeType attribute for thumbnailer {}",
path.display()
);
continue; continue;
}; };
for mime_type in mime_types.split_terminator(';') { for mime_type in mime_types.split_terminator(';') {
if let Ok(mime) = mime_type.parse::<Mime>() { if let Ok(mime) = mime_type.parse::<Mime>() {
log::trace!("thumbnailer {}={:?}", mime, path); log::trace!("thumbnailer {}={}", mime, path.display());
let apps = self let apps = self
.cache .cache
.entry(mime.clone()) .entry(mime)
.or_insert_with(|| Vec::with_capacity(1)); .or_insert_with(|| Vec::with_capacity(1));
apps.push(Thumbnailer { apps.push(Thumbnailer {
exec: exec.to_string(), exec: exec.to_string(),
@ -143,11 +150,11 @@ impl ThumbnailerCache {
} }
let elapsed = start.elapsed(); let elapsed = start.elapsed();
log::info!("loaded thumbnailer cache in {:?}", elapsed); log::info!("loaded thumbnailer cache in {elapsed:?}");
} }
pub fn get(&self, key: &Mime) -> Vec<Thumbnailer> { pub fn get(&self, key: &Mime) -> Vec<Thumbnailer> {
self.cache.get(key).map_or_else(Vec::new, |x| x.clone()) self.cache.get(key).map_or_else(Vec::new, Vec::clone)
} }
} }