From b329ba8261e6e56ad12202d111933cfb4c7159f1 Mon Sep 17 00:00:00 2001 From: Ross Andrews Date: Sun, 24 Aug 2025 01:13:17 -0500 Subject: [PATCH] rename and clippy --- src/fleen_app.rs | 56 ++++++++++++++---------- src/main.rs | 108 +++++++++++++++++++++++++++++------------------ 2 files changed, 102 insertions(+), 62 deletions(-) diff --git a/src/fleen_app.rs b/src/fleen_app.rs index d9d6abb..fe18fc3 100644 --- a/src/fleen_app.rs +++ b/src/fleen_app.rs @@ -1,24 +1,23 @@ use std::cell::RefCell; use std::fs; -use std::io::Error; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::process::Command; use thiserror::Error; -use crate::fleen_app::FleenError::{RootDirNonexistenceError, RootDirPopulatedError}; +use crate::fleen_app::FleenError::{RootDirNonexistence, RootDirPopulated}; use crate::fleen_app::TreeEntry::{CloseDir, Dir}; #[derive(Error, Debug, Clone)] pub enum FleenError { #[error("Can't reach root dir {0}")] - RootDirNonexistenceError(PathBuf), + RootDirNonexistence(PathBuf), #[error("Root dir is nonempty, you probably don't want to create an app here: {0}")] - RootDirPopulatedError(PathBuf), - #[error("Failed to open {0}: {1}")] - FileIoError(String, String), + RootDirPopulated(PathBuf), + #[error("IO error on {0}: {1}")] + FileIo(String, String), #[error("Can't create {0} because it already exists")] - FileExistsError(PathBuf), + FileExists(PathBuf), #[error("Can't create {0}: {1}")] - FileCreateError(PathBuf, String) + FileCreate(PathBuf, String) } #[derive(Clone, Debug)] @@ -42,7 +41,7 @@ impl FleenApp { pub fn open(root: PathBuf) -> Result { match root.try_exists() { Ok(true) => Ok(Self { root, files_cache: RefCell::new(None) }), - _ => Err(RootDirNonexistenceError(root)) + _ => Err(RootDirNonexistence(root)) } } @@ -50,13 +49,13 @@ impl FleenApp { match root.read_dir() { Ok(mut iter) => { if iter.next().is_some() { - Err(RootDirPopulatedError(root)) + Err(RootDirPopulated(root)) } else { Ok(Self { root, files_cache: RefCell::new(None) }) } } Err(_) => { - Err(RootDirNonexistenceError(root)) + Err(RootDirNonexistence(root)) } } } @@ -66,7 +65,7 @@ impl FleenApp { if self.files_cache.borrow().is_none() || force { let mut entries = vec![]; - fn visit_dir(dir: &PathBuf, entries: &mut Vec) { + fn visit_dir(dir: &Path, entries: &mut Vec) { for entry in dir.read_dir().unwrap() { let path = entry.unwrap().path(); if path.is_file() && !path.file_name().unwrap().to_str().unwrap().starts_with('.') { @@ -92,31 +91,31 @@ impl FleenApp { self.files_cache.borrow().clone().expect("Can't happen because we just refreshed the cache") } - pub fn open_filename(&self, filename: &String) -> Result<(), FleenError> { - Command::new("open").arg(filename.clone()).spawn().map_err(|err| { - FleenError::FileIoError(filename.clone(), err.to_string()) + pub fn open_filename(&self, filename: &str) -> Result<(), FleenError> { + Command::new("open").arg(filename).spawn().map_err(|err| { + FleenError::FileIo(filename.to_owned(), err.to_string()) })?; Ok(()) } - pub fn create_page(&self, file_type: FileType, name: &String, parent: Option<&String>) -> Result<(), FleenError> { + pub fn create_page(&self, file_type: FileType, name: &str, parent: Option<&String>) -> Result<(), FleenError> { let mut target = match parent { Some(s) => PathBuf::from(s), None => self.root.clone() }; - target.push(name.clone()); + target.push(name); if target.exists() { - return Err(FleenError::FileExistsError(target)) + return Err(FleenError::FileExists(target)) } match file_type { FileType::File => std::fs::write(target.clone(), []), FileType::Dir => std::fs::create_dir(target.clone()) - }.map_err(|err| FleenError::FileCreateError(target.clone(), err.to_string()))?; + }.map_err(|err| FleenError::FileCreate(target.clone(), err.to_string()))?; self.refresh_file_cache(true); if file_type == FileType::File { - self.open_filename(&target.to_string_lossy().to_string())? + self.open_filename(target.to_string_lossy().as_ref())? } Ok(()) } @@ -127,8 +126,21 @@ impl FleenApp { fs::remove_dir_all(target) } else { fs::remove_file(target) - }.map_err(|err| FleenError::FileIoError(path.clone(), err.to_string()))?; + }.map_err(|err| FleenError::FileIo(path.clone(), err.to_string()))?; self.refresh_file_cache(true); Ok(()) } + + pub fn rename_page(&self, target: &String, new_name: &str) -> Result<(), FleenError> { + let path = PathBuf::from(target); + let mut new_path = path.clone(); + new_path.set_file_name(new_name); + fs::rename(path, new_path).map_err(|err| FleenError::FileIo(target.clone(), err.to_string()))?; + self.refresh_file_cache(true); + Ok(()) + } + + pub fn root_path(&self) -> String { + self.root.to_string_lossy().to_string() + } } diff --git a/src/main.rs b/src/main.rs index 654a181..83c1aea 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ mod fleen_app; -use std::path::PathBuf; -use eframe::egui::{Color32, Context, Id, Stroke}; +use std::path::{Path, PathBuf}; +use eframe::egui::{Color32, Context, Id, RichText}; use eframe::{egui, Frame}; use egui_ltreeview::Action; use crate::fleen_app::{FileType, FleenApp, FleenError, TreeEntry}; @@ -19,6 +19,7 @@ enum DialogMode { RenameFile(String) } +#[derive(Default)] struct FleenUi { app: Option, error: Option, @@ -26,77 +27,67 @@ struct FleenUi { dialog_mode: Option } -impl Default for FleenUi { - fn default() -> Self { - Self { - app: None, - error: None, - selected_file: None, - dialog_mode: None - } - } -} - impl FleenUi { fn site_chooser(&mut self, ctx: &Context) { egui::CentralPanel::default().show(ctx, |ui| { ui.label("No site selected!"); - if ui.button("Open site...").clicked() { - if let Some(path) = rfd::FileDialog::new().pick_folder() { - match FleenApp::open(path) { - Ok(app) => { self.app = Some(app) } - Err(err) => { self.error = Some(err) } - } + if ui.button("Open site...").clicked() && let Some(path) = rfd::FileDialog::new().pick_folder() { + match FleenApp::open(path) { + Ok(app) => { self.app = Some(app) } + Err(err) => { self.error = Some(err) } } } - if ui.button("New site...").clicked() { - if let Some(path) = rfd::FileDialog::new().pick_folder() { - match FleenApp::create(path) { - Ok(app) => { self.app = Some(app) } - Err(err) => { self.error = Some(err) } - } + if ui.button("New site...").clicked() && let Some(path) = rfd::FileDialog::new().pick_folder() { + match FleenApp::create(path) { + Ok(app) => { self.app = Some(app) } + Err(err) => { self.error = Some(err) } } } }); } fn display(&mut self, ctx: &Context) { - let mut new_clicked = false; + let mut just_clicked = false; egui::CentralPanel::default().show(ctx, |ui| { ui.horizontal(|ui| { ui.vertical(|ui| { self.tree_view(ui); - if ui.add(egui::Button::new("Open")).clicked() { - if let Some(fname) = &self.selected_file { + if ui.add(egui::Button::new("Open")).clicked() && + let Some(fname) = &self.selected_file { self.handle_error(self.app.as_ref().unwrap().open_filename(fname)) - } } - if ui.add(egui::Button::new("New page")).clicked() { - new_clicked = true; + let new_btn = egui::Button::new(RichText::new("New page").color(Color32::WHITE)).fill(Color32::DARK_GREEN); + if ui.add(new_btn).clicked() { + just_clicked = true; self.dialog_mode = Some(DialogMode::NewFile(String::new())); } - + let rename_btn = egui::Button::new("Rename"); let delete_btn = egui::Button::new("Delete").fill(Color32::DARK_RED); - if let Some(selected) = &self.selected_file { + if self.root_selected() || self.selected_file.is_none() { + ui.add_enabled(false, rename_btn); + ui.add_enabled(false, delete_btn); + } else if let Some(selected) = &self.selected_file { + if ui.add(rename_btn).clicked() { + self.dialog_mode = Some(DialogMode::RenameFile(label_for_path(&PathBuf::from(&selected)))); + just_clicked = true; + } if ui.add(delete_btn).clicked() { self.dialog_mode = Some(DialogMode::ConfirmDelete(selected.clone())); } - } else { - ui.add_enabled(false, delete_btn); } }); }) }); match self.dialog_mode { - Some(DialogMode::NewFile(_)) => self.new_file_dialog(ctx, new_clicked), + Some(DialogMode::NewFile(_)) => self.new_file_dialog(ctx, just_clicked), Some(DialogMode::ConfirmDelete(_)) => self.confirm_delete_dialog(ctx), - Some(DialogMode::RenameFile(_)) => todo!(), + Some(DialogMode::RenameFile(_)) => self.rename_dialog(ctx, just_clicked), None => {} } } @@ -118,7 +109,7 @@ impl FleenUi { for action in actions { match action { Action::SetSelected(files) => { - self.selected_file = files.first().map(String::clone) + self.selected_file = files.first().cloned() } Action::Activate(activate) => { for fname in activate.selected { @@ -173,6 +164,35 @@ impl FleenUi { }); } + fn rename_dialog(&mut self, ctx: &Context, just_clicked: bool) { + egui::Window::new("Rename").collapsible(false).resizable(false).show(ctx, |ui| { + ui.label("New name"); + let Some(DialogMode::RenameFile(fname)) = &mut self.dialog_mode else { unreachable!() }; + let name_field = egui::TextEdit::singleline(fname); + let resp = ui.add(name_field); + let enter_key = resp.lost_focus(); + if just_clicked { resp.request_focus() } // See new_file_dialog + + ui.horizontal(|ui| { + let btn = ui.button("Rename"); + if enter_key { btn.request_focus() } + if btn.clicked() { + let Some(DialogMode::RenameFile(fname)) = &self.dialog_mode else { unreachable!() }; + let app = self.app.as_ref().unwrap(); + let r = app.rename_page(self.selected_file.as_ref().unwrap(), fname); + if r.is_err() { + self.handle_error(r); + } else { + self.dialog_mode = None; // Close the dialog, we're done + } + } + if ui.button("Cancel").clicked() { + self.dialog_mode = None + } + }) + }); + } + fn confirm_delete_dialog(&mut self, ctx: &Context) { let (mut del, mut cancel) = (false, false); let Some(DialogMode::ConfirmDelete(fname)) = &self.dialog_mode else { unreachable!() }; @@ -201,6 +221,14 @@ impl FleenUi { self.error = Some(e) } } + + fn root_selected(&self) -> bool { + if let Some(path) = &self.selected_file { + path == &self.app.as_ref().unwrap().root_path() + } else { + false + } + } } impl eframe::App for FleenUi { @@ -222,10 +250,10 @@ impl eframe::App for FleenUi { } } -fn label_for_path(path: &PathBuf) -> String { +fn label_for_path(path: &Path) -> String { path.file_name().unwrap().to_string_lossy().to_string() } -fn id_for_path(path: &PathBuf) -> String { +fn id_for_path(path: &Path) -> String { path.to_string_lossy().to_string() } \ No newline at end of file