use std::{ error::Error, fmt::{self, Debug, Display}, fs::{File, OpenOptions}, io::{Read, Seek, SeekFrom, Write}, path::Path, }; use eframe::egui::{self, Key, TextEdit, ViewportBuilder}; enum RunError { HomeDir(homedir::GetHomeError), NoHome, OpenFile(std::io::Error), EFrame(eframe::Error), } impl Debug for RunError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { Display::fmt(self, f) } } impl Display for RunError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::HomeDir(e) => f.write_fmt(format_args!("homedir error: {e}")), Self::NoHome => f.write_fmt(format_args!("failed to find home directory")), Self::OpenFile(e) => f.write_fmt(format_args!("error while opening todo.txt: {e}")), Self::EFrame(e) => f.write_fmt(format_args!("eframe error: {e}")), } } } impl Error for RunError {} fn main() -> Result<(), RunError> { std::env::set_var("WINIT_UNIX_BACKEND", "wayland"); let home_dir = homedir::get_my_home() .map_err(RunError::HomeDir)? .ok_or(RunError::NoHome)?; let todo_file = OpenOptions::new() .create(true) .read(true) .write(true) .append(false) .open(Path::join(&home_dir, "todo.txt")) .map_err(RunError::OpenFile)?; let native_options = eframe::NativeOptions { viewport: ViewportBuilder::default().with_app_id("todoodoo"), ..Default::default() }; eframe::run_native( "To-DooDoo", native_options, Box::new(|_cc| Box::new(ToDooDooApp::new(todo_file))), ) .map_err(RunError::EFrame)?; Ok(()) } struct ToDooDooApp { notes: String, file: File, } impl ToDooDooApp { fn new(mut file: File) -> Self { let mut notes = String::with_capacity( file.metadata() .expect("file metadata") .len() .try_into() .expect("file too big"), ); file.read_to_string(&mut notes) .expect("failed to read file"); Self { file, notes } } fn save(&mut self) { self.file.set_len(0).expect("failed to truncate file"); self.file.seek(SeekFrom::Start(0)).expect("failed to seek"); self.file .write_all(self.notes.as_bytes()) .expect("failed to write file"); self.file.flush().expect("failed to flush"); } } impl eframe::App for ToDooDooApp { fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { egui::CentralPanel::default().show(ctx, |ui| { if ctx.input(|i| i.key_pressed(Key::S) && i.modifiers.command) { self.save(); } ui.add_sized(ui.available_size(), TextEdit::multiline(&mut self.notes)); }); } fn save(&mut self, _storage: &mut dyn eframe::Storage) { self.save(); } }