diff --git a/src/cli.rs b/src/cli.rs
new file mode 100644
index 0000000..31aa6ae
--- /dev/null
+++ b/src/cli.rs
@@ -0,0 +1,132 @@
+use std::path::{self, Path, PathBuf};
+
+use anyhow::{ensure, Result};
+use clap::Subcommand;
+
+use crate::state::write_projects_file;
+use crate::{dirs, Projects};
+
+
+#[derive(Debug, Clone, clap::ValueEnum)]
+pub enum ShellType {
+ Nu,
+}
+
+impl ShellType {
+ pub fn integration(&self) -> String {
+ let template = match self {
+ Self::Nu => include_str!("./integrations/nushell.nu"),
+ };
+
+ let file_path = dirs::selected_project_file().to_str().unwrap();
+ let current_exe = std::env::current_exe().unwrap();
+ let current_exe = current_exe.to_str().unwrap();
+
+ template
+ .replace("{%SELECTED_PROJECT_FILE%}", file_path)
+ .replace("{%PROJ_EXE%}", current_exe)
+ }
+}
+
+
+#[derive(Debug, Subcommand)]
+pub enum Command {
+ /// Add a project
+ Add { path: PathBuf },
+
+ /// Remove a project
+ Remove { path: PathBuf },
+
+ /// List existing projects
+ List,
+
+ /// Initialize shell integration
+ Init { shell: ShellType },
+}
+
+impl Command {
+ pub fn execute(&self, mut projects: Projects) -> Result<()> {
+ match self {
+ Command::Add { path } => {
+ add_project(&mut projects, path)?;
+ }
+ Command::Remove { path } => {
+ remove_project(&mut projects, path)?;
+ }
+ Command::List => {
+ list_projects(&projects)?;
+ }
+ Command::Init { shell } => {
+ print_shell_integration(shell);
+ }
+ }
+
+ Ok(())
+ }
+}
+
+
+fn print_shell_integration(shell: &ShellType) {
+ println!("{}", shell.integration());
+}
+
+
+fn add_project
(projects: &mut Projects, path: P) -> Result<()>
+where
+ P: AsRef,
+{
+ let path = path::absolute(path)?;
+
+ ensure!(path.is_dir(), "Project path does not exists");
+ ensure!(
+ !projects.list.contains(&path.to_path_buf()),
+ "Project path already registered"
+ );
+
+ projects.list.push(path);
+ write_projects_file(projects)?;
+ println!("Added {}", projects.list.last().unwrap().display());
+
+ Ok(())
+}
+
+
+fn remove_project(projects: &mut Projects, path: P) -> Result<()>
+where
+ P: AsRef,
+{
+ let path = path::absolute(path)?;
+
+ ensure!(
+ projects.list.contains(&path.to_path_buf()),
+ "Project path not in registry"
+ );
+
+ let idx =
+ projects.list.iter().enumerate().find_map(
+ |(idx, elem)| {
+ if elem == &path {
+ Some(idx)
+ } else {
+ None
+ }
+ },
+ );
+
+ if let Some(idx) = idx {
+ let proj = projects.list.remove(idx);
+ write_projects_file(projects)?;
+ println!("Removed {}", proj.display());
+ }
+
+ Ok(())
+}
+
+
+fn list_projects(projects: &Projects) -> Result<()> {
+ for project in &projects.list {
+ println!("{project:?}")
+ }
+
+ Ok(())
+}
diff --git a/src/main.rs b/src/main.rs
index 50dbaa2..9bd7e99 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,62 +1,27 @@
+mod cli;
mod dirs;
+mod state;
mod tui;
-use std::io::Write;
-use std::path::{Path, PathBuf};
-use std::{fs, path};
+use std::path::PathBuf;
-use anyhow::{ensure, Result};
-use clap::{Parser, Subcommand};
-
-
-#[derive(Debug, Clone, clap::ValueEnum)]
-enum ShellType {
- Nu,
-}
-
-impl ShellType {
- pub fn integration(&self) -> String {
- let template = match self {
- Self::Nu => include_str!("./integrations/nushell.nu"),
- };
-
- let file_path = dbg!(dirs::selected_project_file().to_str().unwrap());
- let current_exe = std::env::current_exe().unwrap();
- let current_exe = current_exe.to_str().unwrap();
-
- template
- .replace("{%SELECTED_PROJECT_FILE%}", file_path)
- .replace("{%PROJ_EXE%}", current_exe)
- }
-}
+use anyhow::Result;
+use clap::Parser;
+use cli::Command;
+use state::read_projects_file;
#[derive(Debug, Parser)]
-pub struct Args {
+struct Args {
#[command(subcommand)]
- cmd: Option,
-}
-
-#[derive(Debug, Subcommand)]
-enum Command {
- /// Add a project
- Add { path: PathBuf },
-
- /// Remove a project
- Remove { path: PathBuf },
-
- /// List existing projects
- List,
-
- /// Initialize shell integration
- Init { shell: ShellType },
+ pub cmd: Option,
}
#[derive(Debug, Default, serde::Serialize, serde::Deserialize)]
struct Projects {
- list: Vec,
+ pub list: Vec,
}
@@ -65,164 +30,10 @@ fn main() -> Result<()> {
let projects = read_projects_file()?;
- let result = match &args.cmd {
- Some(cmd) => handle_subcommands(projects, cmd),
- None => run_tui(projects),
+ match &args.cmd {
+ Some(cmd) => cmd.execute(projects)?,
+ None => tui::run(projects)?,
};
- if let Err(err) = result {
- eprintln!("{err}");
- std::process::exit(1);
- }
-
Ok(())
}
-
-
-fn run_tui(projects: Projects) -> Result<()> {
- clear_selected_project_file()?;
- let selected_project = tui::run(projects)?;
-
- if let Some(selected_project) = selected_project {
- write_selected_project_file(selected_project)?
- }
-
- Ok(())
-}
-
-
-fn handle_subcommands(mut projects: Projects, cmd: &Command) -> Result<()> {
- match cmd {
- Command::Add { path } => {
- add_project(&mut projects, path)?;
- }
- Command::Remove { path } => {
- remove_project(&mut projects, path)?;
- }
- Command::List => {
- list_projects(&projects)?;
- }
- Command::Init { shell } => {
- print_shell_integration(shell);
- }
- }
-
- Ok(())
-}
-
-
-fn print_shell_integration(shell: &ShellType) {
- println!("{}", shell.integration());
-}
-
-
-fn add_project(projects: &mut Projects, path: P) -> Result<()>
-where
- P: AsRef,
-{
- let path = path::absolute(path)?;
-
- ensure!(path.is_dir(), "Project path does not exists");
- ensure!(
- !projects.list.contains(&path.to_path_buf()),
- "Project path already registered"
- );
-
- projects.list.push(path);
- write_projects_file(projects)?;
- println!("Added {}", projects.list.last().unwrap().display());
-
- Ok(())
-}
-
-
-fn remove_project(projects: &mut Projects, path: P) -> Result<()>
-where
- P: AsRef,
-{
- let path = path::absolute(path)?;
-
- ensure!(
- projects.list.contains(&path.to_path_buf()),
- "Project path not in registry"
- );
-
- let idx =
- projects.list.iter().enumerate().find_map(
- |(idx, elem)| {
- if elem == &path {
- Some(idx)
- } else {
- None
- }
- },
- );
-
- if let Some(idx) = idx {
- let proj = projects.list.remove(idx);
- write_projects_file(projects)?;
- println!("Removed {}", proj.display());
- }
-
- Ok(())
-}
-
-
-fn list_projects(projects: &Projects) -> Result<()> {
- for project in &projects.list {
- println!("{project:?}")
- }
-
- Ok(())
-}
-
-fn clear_selected_project_file() -> Result<()> {
- let cache_file = dirs::selected_project_file();
-
- if fs::exists(cache_file)? {
- fs::remove_file(cache_file)?;
- }
-
- Ok(())
-}
-
-fn write_selected_project_file(path: PathBuf) -> Result<()> {
- let cache_file = dirs::selected_project_file();
-
- let mut file = fs::OpenOptions::new()
- .write(true)
- .create(true)
- .truncate(true)
- .open(cache_file)?;
- write!(file, "{}", path.display())?;
-
- Ok(())
-}
-
-fn write_projects_file(projects: &Projects) -> Result<()> {
- let projects_file = dirs::data_path().join("projects");
-
- let file = fs::OpenOptions::new()
- .write(true)
- .create(true)
- .truncate(true)
- .open(projects_file)?;
-
- bincode::serialize_into(file, projects)?;
-
- Ok(())
-}
-
-
-fn read_projects_file() -> Result {
- let projects_file = dirs::data_path().join("projects");
-
- if !projects_file.exists() {
- return Ok(Projects::default());
- }
-
- let file = std::fs::File::open(projects_file)?;
- let projects = bincode::deserialize_from(file)?;
-
- Ok(projects)
-}
diff --git a/src/state.rs b/src/state.rs
new file mode 100644
index 0000000..7826943
--- /dev/null
+++ b/src/state.rs
@@ -0,0 +1,59 @@
+use std::fs;
+use std::io::Write;
+use std::path::PathBuf;
+
+use anyhow::Result;
+
+use crate::{dirs, Projects};
+
+
+pub fn clear_selected_project_file() -> Result<()> {
+ let cache_file = dirs::selected_project_file();
+
+ if fs::exists(cache_file)? {
+ fs::remove_file(cache_file)?;
+ }
+
+ Ok(())
+}
+
+pub fn write_selected_project_file(path: PathBuf) -> Result<()> {
+ let cache_file = dirs::selected_project_file();
+
+ let mut file = fs::OpenOptions::new()
+ .write(true)
+ .create(true)
+ .truncate(true)
+ .open(cache_file)?;
+ write!(file, "{}", path.display())?;
+
+ Ok(())
+}
+
+pub fn write_projects_file(projects: &Projects) -> Result<()> {
+ let projects_file = dirs::data_path().join("projects");
+
+ let file = fs::OpenOptions::new()
+ .write(true)
+ .create(true)
+ .truncate(true)
+ .open(projects_file)?;
+
+ bincode::serialize_into(file, projects)?;
+
+ Ok(())
+}
+
+
+pub fn read_projects_file() -> Result {
+ let projects_file = dirs::data_path().join("projects");
+
+ if !projects_file.exists() {
+ return Ok(Projects::default());
+ }
+
+ let file = std::fs::File::open(projects_file)?;
+ let projects = bincode::deserialize_from(file)?;
+
+ Ok(projects)
+}
diff --git a/src/tui.rs b/src/tui.rs
index f2ea3ce..f2b0a50 100644
--- a/src/tui.rs
+++ b/src/tui.rs
@@ -9,6 +9,7 @@ use ratatui::style::{Color, Style};
use ratatui::widgets::{Block, Borders, Cell, Row, Table, TableState};
use ratatui::Frame;
+use crate::state::{clear_selected_project_file, write_selected_project_file};
use crate::Projects;
@@ -43,7 +44,9 @@ impl State {
}
-pub fn run(projects: Projects) -> Result