From 823d3e8f93c03b354c26cbdccc90573d0e8e4f2f Mon Sep 17 00:00:00 2001 From: Patrick Auernig Date: Fri, 6 Dec 2024 20:56:11 +0100 Subject: [PATCH] feat(tui): Wrap project path in newtype --- src/cli.rs | 21 ++++++++++++--------- src/main.rs | 23 ++++++++++++++++++++++- src/state.rs | 6 +++--- src/tui.rs | 40 +++++++++++++++++++++------------------- 4 files changed, 58 insertions(+), 32 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index a92ea69..f309a37 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -4,7 +4,7 @@ use anyhow::{ensure, Result}; use clap::Subcommand; use crate::state::write_projects_file; -use crate::{dirs, Projects}; +use crate::{dirs, Project, Projects}; #[derive(Debug, Clone, clap::ValueEnum)] @@ -76,16 +76,18 @@ where P: AsRef, { let path = path::absolute(path)?; - ensure!(path.is_dir(), "Project path does not exists"); + + let project = Project::from(path); + ensure!( - !projects.list.contains(&path.to_path_buf()), + !projects.list.contains(&project), "Project path already registered" ); - projects.list.push(path); + projects.list.push(project); write_projects_file(projects)?; - println!("Added {}", projects.list.last().unwrap().display()); + println!("Added {}", projects.list.last().unwrap().path().display()); Ok(()) } @@ -96,16 +98,17 @@ where P: AsRef, { let path = path::absolute(path)?; + let project = Project::from(path); ensure!( - projects.list.contains(&path.to_path_buf()), + projects.list.contains(&project), "Project path not in registry" ); let idx = projects.list.iter().enumerate().find_map( |(idx, elem)| { - if elem == &path { + if elem == &project { Some(idx) } else { None @@ -116,7 +119,7 @@ where if let Some(idx) = idx { let proj = projects.list.remove(idx); write_projects_file(projects)?; - println!("Removed {}", proj.display()); + println!("Removed {}", proj.path().display()); } Ok(()) @@ -125,7 +128,7 @@ where fn list_projects(projects: &Projects) -> Result<()> { for (idx, project) in projects.list.iter().enumerate() { - println!("{}: {}", idx + 1, project.display()) + println!("{}: {} ({})", idx + 1, project.name(), project.path().display()) } Ok(()) diff --git a/src/main.rs b/src/main.rs index aba01c0..c6284de 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,7 +21,28 @@ struct Args { #[derive(Debug, Default, serde::Serialize, serde::Deserialize)] struct Projects { - pub list: Vec, + pub list: Vec, +} + + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +struct Project(PathBuf); + +impl Project { + pub fn path(&self) -> &PathBuf { + &self.0 + } + + pub fn name(&self) -> String { + let name = self.0.file_name().unwrap(); + name.to_string_lossy().to_string() + } +} + +impl From for Project { + fn from(value: PathBuf) -> Self { + Self(value) + } } diff --git a/src/state.rs b/src/state.rs index 7826943..93cae53 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,10 +1,9 @@ use std::fs; use std::io::Write; -use std::path::PathBuf; use anyhow::Result; -use crate::{dirs, Projects}; +use crate::{dirs, Project, Projects}; pub fn clear_selected_project_file() -> Result<()> { @@ -17,8 +16,9 @@ pub fn clear_selected_project_file() -> Result<()> { Ok(()) } -pub fn write_selected_project_file(path: PathBuf) -> Result<()> { +pub fn write_selected_project_file(project: &Project) -> Result<()> { let cache_file = dirs::selected_project_file(); + let path = project.path(); let mut file = fs::OpenOptions::new() .write(true) diff --git a/src/tui.rs b/src/tui.rs index e228352..0291057 100644 --- a/src/tui.rs +++ b/src/tui.rs @@ -1,17 +1,17 @@ -use std::path::PathBuf; use std::sync::mpsc; use anyhow::Result; use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyModifiers}; use ratatui::layout::{Constraint, Layout, Rect}; use ratatui::style::{Color, Style}; +use ratatui::text::{Line, Span}; use ratatui::widgets::{Block, Borders, Cell, Paragraph, Row, Table, TableState}; use ratatui::Frame; use tracing::trace; use tui_input::{Input, InputRequest}; use crate::state::{clear_selected_project_file, write_selected_project_file}; -use crate::Projects; +use crate::{Project, Projects}; #[derive(Debug, Clone, PartialEq, Eq)] @@ -39,8 +39,8 @@ struct State { mode: Mode, should_exit: bool, project_table: TableState, - filtered_projects: Vec, - selected_project: Option, + filtered_projects: Vec, + selected_project: Option, } impl State { @@ -88,11 +88,11 @@ pub fn run(projects: Projects) -> Result<()> { if let Some(selected_project) = state.selected_project { // hacky stderr abuse eprintln!( - "{}cd:{}", + "{}-cd:{}", env!("CARGO_BIN_NAME"), - selected_project.display() + selected_project.path().display() ); - write_selected_project_file(selected_project)? + write_selected_project_file(&selected_project)? } Ok(()) @@ -152,7 +152,7 @@ fn handle_messages(state: &mut State, rx: &mut mpsc::Receiver) -> Resul if let Some(selected) = state.project_table.selected() { if let Some(project_path) = state.filtered_projects.get(selected) { state.should_exit = true; - state.selected_project = Some(PathBuf::from(project_path)); + state.selected_project = Some(project_path.clone()); }; } } @@ -213,26 +213,28 @@ fn draw_list(state: &mut State, frame: &mut Frame, area: Rect) { .list .iter() .filter_map(|project| { - let path_str = project.to_str().expect("invalid path string"); - let search_value = state.search.value(); if search_value.is_empty() { - return Some(path_str.to_string()); + return Some(project.clone()); } - let indices = fuzzy_search(search_value, path_str)?; - trace!(?search_value, ?path_str, ?indices); - Some(path_str.to_string()) + let indices = fuzzy_search(search_value, &project.name())?; + trace!(?search_value, ?project, ?indices); + Some(project.clone()) }) .collect(); - let rows = state - .filtered_projects - .iter() - .map(|path| Row::new([Cell::new(path.as_str())])); + let rows = state.filtered_projects.iter().map(|project| { + let name = project.name(); + let path_style = Style::new().fg(Color::DarkGray); + let path_span = Span::styled(project.path().display().to_string(), path_style); + let name_span = Span::from(name); - let widths = [Constraint::Min(20)]; + Row::new([Cell::new(name_span), Cell::new(path_span)]) + }); + + let widths = [Constraint::Min(20), Constraint::Fill(1)]; let table_highlight = Style::default().fg(Color::Blue);