1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
use std::{fmt::Display, str::FromStr};
use clap::{Args, Subcommand};
#[derive(Debug, clap::Parser)]
#[clap(
name = "sesh",
version = "0.1.8",
author = "Will Hopkins <willothyh@gmail.com>"
)]
#[group(required = false, multiple = true)]
/// A terminal session manager for unix systems. Run persistent, named tasks that you can
/// detach from and attach to at any time - both on your local machine, and over SSH.
pub struct Cli {
#[command(subcommand)]
pub command: Option<Command>,
#[command(flatten)]
pub args: CliArgs,
}
#[derive(Debug, Clone, Args)]
pub struct CliArgs {
pub program: Option<String>,
pub args: Vec<String>,
#[arg(short, long)]
pub name: Option<String>,
#[arg(short, long)]
pub detached: bool,
}
#[derive(Debug, Subcommand)]
pub enum Command {
#[command(alias = "r", verbatim_doc_comment)]
/// Resume the last used session [alias: r]
///
/// Specify --create / -c to create a new session if one does not exist
Resume {
/// Create a new session if one does not exist
#[arg(short, long)]
create: bool,
},
/// Start a new session, optionally specifying a name [alias: s]
///
/// If no program is specified, the default shell will be used.
/// If no name is specified, the name will be [program name]-[n-1] where n is the number of sessions
/// with that program name.
/// If --detached / -d is present, the session will not be attached to the client on creation
/// and will run in the background.
#[command(alias = "s", verbatim_doc_comment)]
Start {
#[arg(short, long)]
name: Option<String>,
program: Option<String>,
args: Vec<String>,
#[arg(short, long)]
detached: bool,
},
#[command(alias = "a", verbatim_doc_comment)]
/// Attach to a session [alias: a]
///
/// Select a session by index or name.
/// If --create / -c is present, a new session will be created if one does not exist.
/// If the session was selected by name and the session was not present, the new session
/// created by --create will have the specified name.
Attach {
/// Id or name of session
session: SessionSelector,
/// Create a new session if one does not exist
#[arg(short, long)]
create: bool,
},
/// Fuzzy select a session to attach to [alias: f]
///
/// Opens a fuzzy selection window provided by the dialoguer crate.
/// Type to fuzzy find files, or use the Up/Down arrows to navigate.
/// Press Enter to confirm your selection, or Escape to cancel.
#[command(alias = "f", verbatim_doc_comment)]
Select,
/// Detach from a session [alias: d]
///
/// If no session is specified, detaches from the current session (if it exists).
/// Otherwise, detaches the specified session from its owning client.
#[command(alias = "d", verbatim_doc_comment)]
Detach {
/// Id or name of session
session: Option<SessionSelector>,
},
#[command(alias = "k", verbatim_doc_comment)]
/// Kill a session [alias: k]
///
/// Kills a session and the process it owns.
/// Select a session by name or index.
Kill {
/// Id or name of session
session: SessionSelector,
},
/// List sessions [alias: ls]
///
/// Prints a compact list of session names and indexes.
/// With the --info / -i option, prints a nicely formatted table with info about each session.
#[command(alias = "ls", verbatim_doc_comment)]
#[group(required = false, multiple = true)]
List {
/// Print detailed info about sessions
#[arg(short, long)]
info: bool,
/// Print session info as JSON, to be processed by another tool
#[arg(short, long)]
json: bool,
},
/// Shutdown the server (kill all sessions)
Shutdown,
}
#[derive(Debug, Clone)]
pub enum SessionSelector {
Id(usize),
Name(String),
}
impl SessionSelector {
pub fn name(self) -> Option<String> {
match self {
SessionSelector::Id(_) => None,
SessionSelector::Name(name) => Some(name),
}
}
}
impl Display for SessionSelector {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SessionSelector::Id(id) => write!(f, "{}", id),
SessionSelector::Name(name) => write!(f, "{}", name),
}
}
}
impl FromStr for SessionSelector {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Ok(id) = s.parse::<usize>() {
Ok(SessionSelector::Id(id))
} else {
Ok(SessionSelector::Name(s.to_owned()))
}
}
}