project-manager/src/main.rs

232 lines
5.7 KiB
Rust

use skim::prelude::*;
use std::{
collections::BTreeMap,
fs::File,
io::{BufRead, BufReader, Cursor, Result, Write},
path::PathBuf,
};
use tmux_interface::{
commands::tmux::StdIO, AttachSession, HasSession, KillSession, ListClients, NewSession,
SwitchClient, Tmux,
};
fn separate(line: Result<String>) -> Option<(String, String)> {
let line_ = line.unwrap();
let (s1, s2) = line_.split_once(" ")?;
Some((s1.to_owned(), s2.to_owned()))
}
fn set_projects(path: &PathBuf, projects: &BTreeMap<String, String>) {
let mut project_lines = projects
.iter()
.map(|(project_name, project_path)| {
let mut project_line: String = project_name.to_owned();
project_line.push_str(" ");
project_line.push_str(project_path);
return project_line;
})
.collect::<Vec<_>>()
.join("\n");
project_lines.push('\n');
let mut file = File::create(path).expect("Projects list could not be created.");
file
.write(project_lines.as_bytes())
.expect("Projects list could not be written to.");
}
fn get_projects(path: &PathBuf) -> BTreeMap<String, String> {
let reader = BufReader::new(File::open(path).expect("Projects list could not be opened."));
reader.lines().filter_map(separate).collect()
}
#[derive(Debug)]
enum Mode {
Find,
Add,
Delete,
Base,
}
fn add_project(
projects: &mut BTreeMap<String, String>,
project_name: Option<&String>,
) -> Option<String> {
let project_path = std::env::current_dir().expect("Current directory is not available.");
let project_path_string = project_path
.clone()
.into_os_string()
.into_string()
.expect("Current directory is not UTF-8 encoded.");
let project_name_string: String = match project_name {
Some(name) => name.to_string(),
None => match project_path.file_name() {
Some(name) => name
.to_str()
.expect("Current directory name cannot be stringified.")
.to_string(),
None => "unnamed".to_string(),
},
};
projects.insert(project_name_string.clone(), project_path_string);
Some(project_name_string)
}
fn delete_project(projects: &mut BTreeMap<String, String>, project_path: &String) {
dbg!(&project_path);
dbg!(&projects);
let project_name = projects
.iter()
.filter(|(_k, v)| v == &project_path)
.last()
.map(|(k, _v)| k.clone())
.unwrap_or("".to_string());
dbg!(&project_name);
projects.remove(&project_name);
}
fn find_projects(
projects: &BTreeMap<String, String>,
project_name: Option<&String>,
) -> Option<String> {
let project_names = projects.clone().into_keys().collect::<Vec<_>>().join("\n");
let options = SkimOptionsBuilder::default()
.height(Some("100%"))
.multi(false)
.query(project_name.map(|string| string.as_str()))
.select1(true)
.build()
.unwrap();
let items = SkimItemReader::default().of_bufread(Cursor::new(project_names));
let result = Skim::run_with(&options, Some(items)).expect("No skim result.");
match result.is_abort {
true => None,
false => Some(result.selected_items.first()?.output().to_string()),
}
}
fn focus_project(projects: &BTreeMap<String, String>, project_name: Option<String>) {
let session_name = match project_name {
Some(name) => name,
None => "base".to_string(),
};
let has_session = Tmux::with_command(HasSession::new().target_session(&session_name))
.status()
.unwrap()
.success();
match has_session {
false => Tmux::with_command(
NewSession::new()
.session_name(&session_name)
.start_directory(&projects[&session_name])
.detached(),
)
.status()
.unwrap()
.success(),
true => true,
};
match std::env::var("TMUX") {
Ok(_) => Tmux::with_command(SwitchClient::new().target_session(&session_name))
.status()
.unwrap()
.success(),
Err(_) => Tmux::with_command(AttachSession::new().target_session(&session_name))
.status()
.unwrap()
.success(),
};
}
fn main() {
let args: Vec<String> = std::env::args().collect();
let mode: Mode = match args.get(1) {
Some(flag) => match flag.as_str() {
"-a" => Mode::Add,
"-d" => Mode::Delete,
"-b" => Mode::Base,
_ => Mode::Find,
},
_ => Mode::Find,
};
let project_file_path = PathBuf::from("/home/lukew/Documents/projects");
let mut projects = get_projects(&project_file_path);
let old_session_name = String::from_utf8(
Tmux::with_command(
ListClients::new()
.target_session(std::env::var("TMUX_PANE").unwrap_or("".to_string()))
.format("#S"),
)
.output()
.unwrap()
.stdout(),
)
.unwrap()
.strip_suffix("\n")
.unwrap()
.to_string();
let old_session_path = String::from_utf8(
Tmux::with_command(
ListClients::new()
.target_session(std::env::var("TMUX_PANE").unwrap_or("".to_string()))
.format("#{pane_start_path}"),
)
.output()
.unwrap()
.stdout(),
)
.unwrap()
.strip_suffix("\n")
.unwrap()
.to_string();
let project_name = match mode {
Mode::Add => add_project(&mut projects, args.get(2)),
Mode::Delete => {
delete_project(&mut projects, &old_session_path);
None
}
Mode::Base => None,
Mode::Find => find_projects(&projects, args.get(1)),
};
focus_project(&projects, project_name);
set_projects(&project_file_path, &projects);
match mode {
Mode::Delete => Tmux::with_command(KillSession::new().target_session(old_session_name))
.stderr(Some(StdIO::Null))
.status()
.unwrap()
.success(),
_ => true,
};
match true {
true => Tmux::with_command(KillSession::new().target_session("PROJECTS"))
.stderr(Some(StdIO::Null))
.status()
.unwrap()
.success(),
false => false,
};
}