Skip to content

Commit 5eef063

Browse files
committed
Add a start command for projects
1 parent 9e337bc commit 5eef063

File tree

8 files changed

+121
-55
lines changed

8 files changed

+121
-55
lines changed

src/cli/project.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ pub struct ProjectCli {
1010
pub enum ProjectCommands {
1111
#[command(alias = "ls")]
1212
List(ProjectListArgs),
13+
Start(ProjectStartArgs),
1314
}
1415

1516
#[derive(Debug, Parser)]
@@ -18,3 +19,16 @@ pub struct ProjectListArgs {
1819
#[arg(short, long, default_value_t = false)]
1920
pub minimal: bool,
2021
}
22+
23+
#[derive(Debug, Parser)]
24+
pub struct ProjectStartArgs {
25+
pub name: String,
26+
27+
/// Start the session detached
28+
#[arg(short, long, default_value_t = false)]
29+
pub detached: bool,
30+
31+
/// Always start a new session instead of attaching to an existing session
32+
#[arg(long, default_value_t = false)]
33+
pub always_new_session: bool,
34+
}

src/commands/directory.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::{
77
widgets::{heading::Heading, table::Table},
88
};
99
use std::{collections::HashMap, path::PathBuf};
10-
use tmux_interface::{NewSession, Tmux, TmuxCommand};
10+
use tmux_interface::{NewSession, Tmux};
1111

1212
pub fn directory_handler(args: DirectoryCli) {
1313
match args.action {

src/commands/project.rs

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
use crate::{
2-
cli::project::{ProjectCli, ProjectCommands, ProjectListArgs},
2+
apply_if,
3+
cli::project::{ProjectCli, ProjectCommands, ProjectListArgs, ProjectStartArgs},
4+
helpers::{self, Exit},
35
projects::parse_project_config,
6+
templates::apply_windows,
7+
tmux,
48
widgets::{heading::Heading, table::Table},
59
};
10+
use tmux_interface::{NewSession, Tmux};
611

712
pub fn project_handler(args: ProjectCli) {
813
match args.action {
914
ProjectCommands::List(args) => list_handler(args),
15+
ProjectCommands::Start(args) => start_handler(args),
1016
}
1117
}
1218

@@ -20,3 +26,45 @@ fn list_handler(args: ProjectListArgs) {
2026
}
2127
}
2228
}
29+
30+
fn start_handler(args: ProjectStartArgs) {
31+
let projects = parse_project_config();
32+
let project = projects
33+
.into_iter()
34+
.find(|proj| proj.name == args.name)
35+
.exit(1, "Project could not be found");
36+
37+
let detached = args.detached;
38+
39+
if tmux::session_exists(&project.name).unwrap_or(false) && !args.always_new_session {
40+
apply_if!(
41+
!detached,
42+
Tmux::new(),
43+
add_command,
44+
tmux::attach(&project.name)
45+
)
46+
.output()
47+
.exit(1, "Could not attach to the Tmux-session");
48+
return;
49+
}
50+
51+
let name = tmux::get_unused_name(project.name);
52+
let windows = Vec::from(project.setup);
53+
let path = helpers::absolute_path(&project.root_dir).exit(1, "The path could not be found");
54+
55+
let new_session_cmd = NewSession::new()
56+
.detached()
57+
.session_name(&name)
58+
.start_directory(path.to_string_lossy().into_owned());
59+
60+
let initial_tmux = apply_if!(
61+
!detached,
62+
Tmux::new().add_command(new_session_cmd),
63+
add_command,
64+
tmux::attach(&name)
65+
);
66+
67+
apply_windows(initial_tmux, &windows, &Some(path))
68+
.output()
69+
.exit(1, "Could not start Tmux-session");
70+
}

src/commands/template.rs

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ use crate::{
22
apply_if,
33
cli::template::{ListTemplateArgs, StartTemplateArgs, TemplateCli, TemplateCommands},
44
helpers::{absolute_path, dir_name, Exit},
5-
templates::{apply_template, parse_template_config},
6-
tmux::{attach, session_exists},
5+
templates::{apply_windows, parse_template_config},
6+
tmux,
77
widgets::{heading::Heading, table::Table},
88
};
99
use std::path::PathBuf;
@@ -51,8 +51,8 @@ fn start_handler(args: StartTemplateArgs) {
5151
.map(|p| dir_name(p))
5252
.unwrap_or(template.name.clone());
5353

54-
if session_exists(&name).unwrap_or(false) && !args.always_new_session {
55-
apply_if!(!detached, Tmux::new(), add_command, attach(name))
54+
if tmux::session_exists(&name).unwrap_or(false) && !args.always_new_session {
55+
apply_if!(!detached, Tmux::new(), add_command, tmux::attach(name))
5656
.output()
5757
.exit(1, "Could not attach to the Tmux-session");
5858
return;
@@ -64,35 +64,21 @@ fn start_handler(args: StartTemplateArgs) {
6464
!detached,
6565
Tmux::new().add_command(new_session_cmd),
6666
add_command,
67-
attach(&name)
67+
tmux::attach(&name)
6868
);
6969

70-
let tmux = apply_template(initial_tmux, &template, &resolved_path);
70+
let tmux = apply_windows(initial_tmux, &template.windows, &resolved_path);
7171

7272
tmux.output().exit(1, "Could not start Tmux-session");
7373
}
7474

75-
fn get_unused_name(name: String, used: Option<u8>) -> String {
76-
let new_name = match used {
77-
Some(counter) => format!("{}({})", name, counter),
78-
None => name.clone(),
79-
};
80-
81-
if session_exists(&new_name).unwrap_or(false) {
82-
let next_counter = used.unwrap_or(0) + 1;
83-
get_unused_name(name, Some(next_counter))
84-
} else {
85-
new_name
86-
}
87-
}
88-
8975
fn resolve_cmd_name(
9076
path: &Option<PathBuf>,
9177
name: Option<String>,
9278
template_name: String,
9379
) -> (TmuxCommand<'static>, String) {
9480
if let Some(p) = path {
95-
let session_name = get_unused_name(name.unwrap_or_else(|| dir_name(p)), None);
81+
let session_name = tmux::get_unused_name(name.unwrap_or_else(|| dir_name(p)));
9682
return (
9783
NewSession::new()
9884
.detached()
@@ -103,7 +89,7 @@ fn resolve_cmd_name(
10389
);
10490
}
10591

106-
let session_name = get_unused_name(name.unwrap_or(template_name), None);
92+
let session_name = tmux::get_unused_name(name.unwrap_or(template_name));
10793
(
10894
NewSession::new()
10995
.detached()

src/projects.rs

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ use crate::{
55
widgets::table::Table,
66
};
77
use serde::Deserialize;
8-
use std::fs;
8+
use std::{fs, path::PathBuf};
99

1010
#[derive(Debug, PartialEq, Eq)]
1111
pub struct Project {
1212
pub name: String,
13+
pub root_dir: PathBuf,
1314
pub setup: ProjectSetup,
1415
}
1516

@@ -20,20 +21,30 @@ pub enum ProjectSetup {
2021
Windows(Vec<Window>),
2122
}
2223

23-
impl From<ProjectSetup> for Table<String, String> {
24-
fn from(value: ProjectSetup) -> Self {
25-
let (template_name, windows) = match value {
24+
impl From<ProjectSetup> for Vec<Window> {
25+
fn from(val: ProjectSetup) -> Self {
26+
match val {
2627
ProjectSetup::Template(template_name) => {
2728
let all_templates = parse_template_config();
2829
let template = all_templates
2930
.into_iter()
3031
.find(|t| t.name == template_name)
3132
.unwrap_or_else(|| exit!(1, "Template {} could not be found", template_name));
3233

33-
(Some(template_name), template.windows)
34+
template.windows
3435
}
35-
ProjectSetup::Windows(windows) => (None, windows),
36+
ProjectSetup::Windows(windows) => windows,
37+
}
38+
}
39+
}
40+
41+
impl From<ProjectSetup> for Table<String, String> {
42+
fn from(value: ProjectSetup) -> Self {
43+
let template_name = match &value {
44+
ProjectSetup::Template(template_name) => Some(template_name.clone()),
45+
ProjectSetup::Windows(_) => None,
3646
};
47+
let windows: Vec<Window> = value.into();
3748

3849
let mut rows = Self::new(vec![(
3950
"Template".to_string(),
@@ -55,6 +66,7 @@ impl<'de> Deserialize<'de> for Project {
5566
#[derive(Deserialize)]
5667
struct RawProject {
5768
name: String,
69+
root_dir: PathBuf,
5870
template: Option<String>,
5971
windows: Option<Vec<Window>>,
6072
}
@@ -73,6 +85,7 @@ impl<'de> Deserialize<'de> for Project {
7385

7486
Ok(Project {
7587
name: raw.name,
88+
root_dir: raw.root_dir,
7689
setup,
7790
})
7891
}
@@ -118,6 +131,7 @@ windows:
118131
project,
119132
Project {
120133
name: "OsmApp".to_string(),
134+
root_dir: PathBuf::from("~/GitHub/osmapp"),
121135
setup: ProjectSetup::Windows(vec![
122136
Window {
123137
name: Some(" Neovim".to_string()),
@@ -145,6 +159,7 @@ template: Svelte",
145159
project,
146160
Project {
147161
name: "Dlool".to_string(),
162+
root_dir: PathBuf::from("~/SoftwareDevelopment/web/Dlool/dlool_frontend_v2/"),
148163
setup: ProjectSetup::Template("Svelte".to_string())
149164
}
150165
);

src/templates.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,12 @@ pub fn parse_template_config() -> Vec<Template> {
5353
.collect()
5454
}
5555

56-
pub fn apply_template<'a>(
56+
pub fn apply_windows<'a>(
5757
tmux: Tmux<'a>,
58-
template: &'a Template,
58+
windows: &'a [Window],
5959
dir: &'a Option<PathBuf>,
6060
) -> Tmux<'a> {
61-
let enumerated = template.windows.iter().enumerate();
61+
let enumerated = windows.iter().enumerate();
6262
enumerated.fold(tmux, |tmux, (window_idx, window)| {
6363
let cmd = build_tmux_command(window_idx, window, dir);
6464

src/tmux.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,25 @@ macro_rules! conditional_command {
2323
if $condition {
2424
$command.into()
2525
} else {
26-
TmuxCommand::new()
26+
tmux_interface::TmuxCommand::new()
2727
}
2828
};
2929
}
30+
31+
fn private_get_unused_name(name: String, used: Option<u8>) -> String {
32+
let new_name = match used {
33+
Some(counter) => format!("{}({})", name, counter),
34+
None => name.clone(),
35+
};
36+
37+
if session_exists(&new_name).unwrap_or(false) {
38+
let next_counter = used.unwrap_or(0) + 1;
39+
private_get_unused_name(name, Some(next_counter))
40+
} else {
41+
new_name
42+
}
43+
}
44+
45+
pub fn get_unused_name(name: String) -> String {
46+
private_get_unused_name(name, None)
47+
}

src/widgets/table.rs

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::{fmt, iter};
1+
use std::fmt;
22

33
#[derive(Debug, Clone, PartialEq, Eq)]
44
pub struct Table<T: fmt::Display, U: fmt::Display> {
@@ -75,9 +75,11 @@ where
7575
.zip(values.iter())
7676
.map(|(key, value)| {
7777
format!(
78-
"│ {} │ {} │",
79-
pad_str(key, ' ', key_width),
80-
pad_str(value, ' ', val_width)
78+
"│ {:<width$} │ {:<val_width$} │",
79+
key,
80+
value,
81+
width = key_width,
82+
val_width = val_width
8183
)
8284
})
8385
.collect();
@@ -94,20 +96,3 @@ where
9496
)
9597
}
9698
}
97-
98-
fn pad_str<T: Into<String>>(string: T, padder: char, len: usize) -> String {
99-
let string = string.into();
100-
let extra_len = len - string.len();
101-
let pad_str: String = iter::repeat(padder).take(extra_len).collect();
102-
format!("{}{}", string, pad_str)
103-
}
104-
105-
#[cfg(test)]
106-
mod tests {
107-
use super::*;
108-
109-
#[test]
110-
fn test_pad_str() {
111-
assert_eq!(pad_str("hi", ' ', 5), "hi ".to_string());
112-
}
113-
}

0 commit comments

Comments
 (0)