From 4d81cd51a6767cd8bc067504f992604b7f6f115d Mon Sep 17 00:00:00 2001 From: Shaun Reed Date: Tue, 20 Jan 2026 20:43:01 -0500 Subject: [PATCH] [tui] Add ComponentOf trait. I think it will help with fetching a component by type from the Components vector attached to App? --- src/tui/app.rs | 113 ++++++++++++++++++++++++++++++------------------- 1 file changed, 70 insertions(+), 43 deletions(-) diff --git a/src/tui/app.rs b/src/tui/app.rs index f10891a..eae2494 100644 --- a/src/tui/app.rs +++ b/src/tui/app.rs @@ -24,6 +24,59 @@ pub enum AppComponents<'a> { AppComponent(Box), } +/// Usage: get_component_mut::() OR get_component::() +/// +/// Implementing this trait for each AppComponent allows for easy lookup in the vector. +trait ComponentOf { + fn as_ref(&self) -> Option<&T>; + fn as_mut(&mut self) -> Option<&mut T>; +} + +impl<'a> ComponentOf for AppComponents<'a> { + fn as_ref(&self) -> Option<&Logger> { + if let AppComponents::AppLogger(ref e) = *self { + return Some(e); + } + None + } + fn as_mut(&mut self) -> Option<&mut Logger> { + if let AppComponents::AppLogger(ref mut e) = *self { + return Some(e); + } + None + } +} + +impl<'a> ComponentOf for AppComponents<'a> { + fn as_ref(&self) -> Option<&Editor> { + if let AppComponents::AppEditor(ref e) = *self { + return Some(e); + } + None + } + fn as_mut(&mut self) -> Option<&mut Editor> { + if let AppComponents::AppEditor(ref mut e) = *self { + return Some(e); + } + None + } +} + +impl<'a> ComponentOf> for AppComponents<'a> { + fn as_ref(&self) -> Option<&Explorer<'a>> { + if let AppComponents::AppExplorer(ref e) = *self { + return Some(e); + } + None + } + fn as_mut(&mut self) -> Option<&mut Explorer<'a>> { + if let AppComponents::AppExplorer(ref mut e) = *self { + return Some(e); + } + None + } +} + pub struct App<'a> { components: Vec>, } @@ -37,7 +90,7 @@ impl<'a> App<'a> { AppComponents::AppLogger(Logger::new()), ], }; - app.get_editor_mut() + app.get_component_mut::() .unwrap() .set_contents(&root_path.join("src/tui/app.rs")) .context(format!( @@ -47,44 +100,18 @@ impl<'a> App<'a> { Ok(app) } - fn get_explorer(&self) -> Result<&Explorer<'a>> { - for component in &self.components { - if let AppComponents::AppExplorer(explorer) = component { - return Ok(explorer); - } - } - bail!("Failed to find project explorer widget.") + fn get_component(&self) -> Option<&T> + where + AppComponents<'a>: ComponentOf, + { + self.components.iter().find_map(|c| c.as_ref()) } - fn get_explorer_mut(&mut self) -> Result<&mut Explorer<'a>> { - for component in &mut self.components { - if let AppComponents::AppExplorer(explorer) = component { - return Ok(explorer); - } - } - bail!("Failed to find project explorer widget.") - } - - fn get_editor(&self) -> Option<&Editor> { - for component in &self.components { - if let AppComponents::AppEditor(editor) = component { - return Some(editor); - } - } - - // There is no editor currently opened. - None - } - - fn get_editor_mut(&mut self) -> Option<&mut Editor> { - for component in &mut self.components { - if let AppComponents::AppEditor(editor) = component { - return Some(editor); - } - } - - // There is no editor currently opened. - None + fn get_component_mut(&mut self) -> Option<&mut T> + where + AppComponents<'a>: ComponentOf, + { + self.components.iter_mut().find_map(|c| c.as_mut()) } pub fn run(mut self, mut terminal: DefaultTerminal) -> Result<()> { @@ -121,7 +148,7 @@ impl<'a> App<'a> { fn draw_tabs(&self, area: Rect, buf: &mut Buffer) { // Determine the tab title from the current file (or use a fallback). let mut title: Option<&str> = None; - if let Some(editor) = self.get_editor() { + if let Some(editor) = self.get_component::() { title = editor .file_path .as_ref() @@ -144,12 +171,12 @@ impl<'a> App<'a> { /// If the selected item is not a file, this does nothing. fn refresh_editor_contents(&mut self) -> Result<()> { // Use the currently selected TreeItem or get an absolute path to this source file. - let selected_pathbuf = match self.get_explorer()?.selected() { + let selected_pathbuf = match self.get_component::().unwrap().selected() { Ok(path) => PathBuf::from(path), Err(_) => PathBuf::from(std::path::absolute(file!())?.to_string_lossy().to_string()), }; let editor = self - .get_editor_mut() + .get_component_mut::() .context("Failed to get active editor while refreshing contents.")?; if let Some(current_file_path) = editor.file_path.clone() { if selected_pathbuf == current_file_path || !selected_pathbuf.is_file() { @@ -194,15 +221,15 @@ impl<'a> Widget for &mut App<'a> { self.draw_status(vertical[0], buf); self.draw_tabs(editor_layout[0], buf); + let id = self.id().to_string(); for component in &mut self.components { match component { AppComponents::AppEditor(editor) => editor.render(editor_layout[1], buf), AppComponents::AppExplorer(explorer) => { - // TODO: What to do about errors during rendering? - // Once there is a debug console, maybe log it and discard? Panic isn't great. explorer .render(horizontal[0], buf) - .expect("Failed to render Explorer"); + .context("Failed to render Explorer") + .unwrap_or_else(|e| error!(target:id.as_str(), "{}", e)); } AppComponents::AppLogger(logger) => logger.render(vertical[2], buf), AppComponents::AppComponent(_) => {}