diff --git a/src/cli/internal.rs b/src/cli/internal.rs new file mode 100644 index 0000000000000000000000000000000000000000..1fc9b741cd127fe5c2a92d4f524922a06e0506bf --- /dev/null +++ b/src/cli/internal.rs @@ -0,0 +1,131 @@ +use macros_rs::{crashln, string}; +use pmc::{config, file, helpers, log, process::Runner}; +use regex::Regex; + +pub struct Internal<'i> { + pub id: usize, + pub runner: Runner, + pub kind: String, + pub server_name: &'i String, +} + +impl<'i> Internal<'i> { + pub fn create(mut self, script: &String, name: &Option, watch: &Option) { + let config = config::read(); + let name = match name { + Some(name) => string!(name), + None => string!(script.split_whitespace().next().unwrap_or_default()), + }; + + if matches!(&**self.server_name, "internal" | "local") { + let pattern = Regex::new(r"(?m)^[a-zA-Z0-9]+(/[a-zA-Z0-9]+)*(\.js|\.ts)?$").unwrap(); + + if pattern.is_match(script) { + let script = format!("{} {script}", config.runner.node); + self.runner.start(&name, &script, file::cwd(), watch).save(); + } else { + self.runner.start(&name, script, file::cwd(), watch).save(); + } + } else { + let Some(servers) = config::servers().servers else { + crashln!("{} Failed to read servers", *helpers::FAIL) + }; + + if let Some(server) = servers.get(self.server_name) { + match Runner::connect(self.server_name.clone(), server.get(), false) { + Some(mut remote) => remote.start(&name, script, file::cwd(), watch), + None => crashln!("{} Failed to connect (name={}, address={})", *helpers::FAIL, self.server_name, server.address), + }; + } else { + crashln!("{} Server '{}' does not exist", *helpers::FAIL, self.server_name,) + }; + } + + println!("{} Creating {}process with ({name})", *helpers::SUCCESS, self.kind); + println!("{} {}created ({name}) ✓", *helpers::SUCCESS, self.kind); + } + + pub fn restart(self, name: &Option, watch: &Option) { + println!("{} Applying {}action restartProcess on ({})", *helpers::SUCCESS, self.kind, self.id); + + if matches!(&**self.server_name, "internal" | "local") { + let mut item = self.runner.get(self.id); + + match watch { + Some(path) => item.watch(path), + None => item.disable_watch(), + } + + name.as_ref().map(|n| item.rename(n.trim().replace("\n", ""))); + item.restart(); + + log!("process started (id={})", self.id); + } else { + let Some(servers) = config::servers().servers else { + crashln!("{} Failed to read servers", *helpers::FAIL) + }; + + if let Some(server) = servers.get(self.server_name) { + match Runner::connect(self.server_name.clone(), server.get(), false) { + Some(remote) => { + let mut item = remote.get(self.id); + + name.as_ref().map(|n| item.rename(n.trim().replace("\n", ""))); + item.restart(); + } + None => crashln!("{} Failed to connect (name={}, address={})", *helpers::FAIL, self.server_name, server.address), + } + } else { + crashln!("{} Server '{}' does not exist", *helpers::FAIL, self.server_name) + }; + } + + println!("{} restarted {}({}) ✓", *helpers::SUCCESS, self.kind, self.id); + } + + pub fn stop(mut self) { + println!("{} Applying {}action stopProcess on ({})", *helpers::SUCCESS, self.kind, self.id); + + if !matches!(&**self.server_name, "internal" | "local") { + let Some(servers) = config::servers().servers else { + crashln!("{} Failed to read servers", *helpers::FAIL) + }; + + if let Some(server) = servers.get(self.server_name) { + self.runner = match Runner::connect(self.server_name.clone(), server.get(), false) { + Some(remote) => remote, + None => crashln!("{} Failed to connect (name={}, address={})", *helpers::FAIL, self.server_name, server.address), + }; + } else { + crashln!("{} Server '{}' does not exist", *helpers::FAIL, self.server_name) + }; + } + + self.runner.get(self.id).stop(); + println!("{} stopped {}({}) ✓", *helpers::SUCCESS, self.kind, self.id); + log!("process stopped {}(id={})", self.kind, self.id); + } + + pub fn remove(mut self) { + println!("{} Applying {}action removeProcess on ({})", *helpers::SUCCESS, self.kind, self.id); + + if !matches!(&**self.server_name, "internal" | "local") { + let Some(servers) = config::servers().servers else { + crashln!("{} Failed to read servers", *helpers::FAIL) + }; + + if let Some(server) = servers.get(self.server_name) { + self.runner = match Runner::connect(self.server_name.clone(), server.get(), false) { + Some(remote) => remote, + None => crashln!("{} Failed to remove (name={}, address={})", *helpers::FAIL, self.server_name, server.address), + }; + } else { + crashln!("{} Server '{}' does not exist", *helpers::FAIL, self.server_name) + }; + } + + self.runner.remove(self.id); + println!("{} removed {}({}) ✓", *helpers::SUCCESS, self.kind, self.id); + log!("process removed (id={})", self.id); + } +} diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 5232fb321a7032f92e74c3848e577d102207594b..fa6b59e03e0dfa99cc602b7cc35bc186eb745998 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -1,9 +1,10 @@ +pub(crate) mod internal; pub(crate) mod server; use colored::Colorize; +use internal::Internal; use macros_rs::{crashln, string, ternary}; use psutil::process::{MemoryInfo, Process}; -use regex::Regex; use serde::Serialize; use serde_json::json; use std::env; @@ -31,6 +32,12 @@ pub enum Args { Script(String), } +#[derive(Clone, Debug)] +pub enum Item { + Id(usize), + Name(String), +} + fn format(server_name: &String) -> (String, String) { let kind = ternary!(matches!(&**server_name, "internal" | "local"), "", "remote ").to_string(); return (kind, server_name.to_string()); @@ -46,141 +53,47 @@ pub fn get_version(short: bool) -> String { }; } -pub fn start(name: &Option, args: &Option, watch: &Option, server_name: &String) { - let mut runner = Runner::new(); - let config = config::read(); +pub fn start(name: &Option, args: &Args, watch: &Option, server_name: &String) { + let runner = Runner::new(); let (kind, list_name) = format(server_name); match args { - Some(Args::Id(id)) => { - let runner: Runner = Runner::new(); - println!("{} Applying {kind}action restartProcess on ({id})", *helpers::SUCCESS); - - if matches!(&**server_name, "internal" | "local") { - let mut item = runner.get(*id); - - match watch { - Some(path) => item.watch(path), - None => item.disable_watch(), - } - - name.as_ref().map(|n| item.rename(n.trim().replace("\n", ""))); - item.restart(); - - log!("process started (id={id})"); - } else { - let Some(servers) = config::servers().servers else { - crashln!("{} Failed to read servers", *helpers::FAIL) - }; - - if let Some(server) = servers.get(server_name) { - match Runner::connect(server_name.clone(), server.get(), false) { - Some(remote) => { - let mut item = remote.get(*id); - - name.as_ref().map(|n| item.rename(n.trim().replace("\n", ""))); - item.restart(); - } - None => crashln!("{} Failed to connect (name={server_name}, address={})", *helpers::FAIL, server.address), - } - } else { - crashln!("{} Server '{server_name}' does not exist", *helpers::FAIL) - }; - } - - println!("{} restarted {kind}({id}) ✓", *helpers::SUCCESS); - list(&string!("default"), &list_name); - } - Some(Args::Script(script)) => { - let name = match name { - Some(name) => string!(name), - None => string!(script.split_whitespace().next().unwrap_or_default()), - }; - if matches!(&**server_name, "internal" | "local") { - let pattern = Regex::new(r"(?m)^[a-zA-Z0-9]+(/[a-zA-Z0-9]+)*(\.js|\.ts)?$").unwrap(); - - if pattern.is_match(script) { - let script = format!("{} {script}", config.runner.node); - runner.start(&name, &script, file::cwd(), watch).save(); - } else { - runner.start(&name, script, file::cwd(), watch).save(); - } - - log!("process created (name={name})"); - } else { - let Some(servers) = config::servers().servers else { - crashln!("{} Failed to read servers", *helpers::FAIL) - }; - - if let Some(server) = servers.get(server_name) { - match Runner::connect(server_name.clone(), server.get(), false) { - Some(mut remote) => remote.start(&name, script, file::cwd(), watch), - None => crashln!("{} Failed to connect (name={server_name}, address={})", *helpers::FAIL, server.address), - }; - } else { - crashln!("{} Server '{server_name}' does not exist", *helpers::FAIL) - }; - } - - println!("{} Creating {kind}process with ({name})", *helpers::SUCCESS); - - println!("{} {kind}created ({name}) ✓", *helpers::SUCCESS); - list(&string!("default"), &list_name); - } - None => {} + Args::Id(id) => Internal { id: *id, runner, server_name, kind }.restart(name, watch), + Args::Script(script) => match runner.find(&script) { + Some(id) => Internal { id, runner, server_name, kind }.restart(name, watch), + None => Internal { id: 0, runner, server_name, kind }.create(script, name, watch), + }, } + + list(&string!("default"), &list_name); } -pub fn stop(id: &usize, server_name: &String) { - let mut runner: Runner = Runner::new(); +pub fn stop(item: &Item, server_name: &String) { + let runner: Runner = Runner::new(); let (kind, list_name) = format(server_name); - println!("{} Applying {kind}action stopProcess on ({id})", *helpers::SUCCESS); - - if !matches!(&**server_name, "internal" | "local") { - let Some(servers) = config::servers().servers else { - crashln!("{} Failed to read servers", *helpers::FAIL) - }; - if let Some(server) = servers.get(server_name) { - runner = match Runner::connect(server_name.clone(), server.get(), false) { - Some(remote) => remote, - None => crashln!("{} Failed to connect (name={server_name}, address={})", *helpers::FAIL, server.address), - }; - } else { - crashln!("{} Server '{server_name}' does not exist", *helpers::FAIL) - }; + match item { + Item::Id(id) => Internal { id: *id, runner, server_name, kind }.stop(), + Item::Name(name) => match runner.find(&name) { + Some(id) => Internal { id, runner, server_name, kind }.stop(), + None => crashln!("{} Process ({name}) not found", *helpers::FAIL), + }, } - runner.get(*id).stop(); - println!("{} stopped {kind}({id}) ✓", *helpers::SUCCESS); - log!("process stopped {kind}(id={id})"); - list(&string!("default"), &list_name); } -pub fn remove(id: &usize, server_name: &String) { - let mut runner: Runner = Runner::new(); +pub fn remove(item: &Item, server_name: &String) { + let runner: Runner = Runner::new(); let (kind, _) = format(server_name); - println!("{} Applying {kind}action removeProcess on ({id})", *helpers::SUCCESS); - if !matches!(&**server_name, "internal" | "local") { - let Some(servers) = config::servers().servers else { - crashln!("{} Failed to read servers", *helpers::FAIL) - }; - - if let Some(server) = servers.get(server_name) { - runner = match Runner::connect(server_name.clone(), server.get(), false) { - Some(remote) => remote, - None => crashln!("{} Failed to remove (name={server_name}, address={})", *helpers::FAIL, server.address), - }; - } else { - crashln!("{} Server '{server_name}' does not exist", *helpers::FAIL) - }; + match item { + Item::Id(id) => Internal { id: *id, runner, server_name, kind }.remove(), + Item::Name(name) => match runner.find(&name) { + Some(id) => Internal { id, runner, server_name, kind }.remove(), + None => crashln!("{} Process ({name}) not found", *helpers::FAIL), + }, } - - runner.remove(*id); - println!("{} removed {kind}({id}) ✓", *helpers::SUCCESS); - log!("process removed (id={id})"); } pub fn info(id: &usize, format: &String, server_name: &String) { diff --git a/src/main.rs b/src/main.rs index f7d2ef21633a25cbccabded46f1addd00c4cba46..b48f573b1275ffd7b84b4379ed2bb0d5540ceb2d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,12 +3,17 @@ mod daemon; mod globals; mod webui; -use crate::{cli::Args, globals::defaults}; use clap::{Parser, Subcommand}; use clap_verbosity_flag::{LogLevel, Verbosity}; use macros_rs::{str, string, then}; use update_informer::{registry, Check}; +use crate::{ + cli::{Args, Item}, + globals::defaults, +}; + +// migrate to helpers fn validate_id_script(s: &str) -> Result { if let Ok(id) = s.parse::() { Ok(Args::Id(id)) @@ -17,6 +22,15 @@ fn validate_id_script(s: &str) -> Result { } } +// migrate to helpers +fn validate_item(s: &str) -> Result { + if let Ok(id) = s.parse::() { + Ok(Item::Id(id)) + } else { + Ok(Item::Name(s.to_owned())) + } +} + #[derive(Copy, Clone, Debug, Default)] struct NoneLevel; impl LogLevel for NoneLevel { @@ -95,7 +109,7 @@ enum Commands { #[arg(long)] name: Option, #[clap(value_parser = validate_id_script)] - args: Option, + args: Args, /// Watch to reload path #[arg(long)] watch: Option, @@ -107,7 +121,8 @@ enum Commands { /// Stop/Kill a process #[command(visible_alias = "kill")] Stop { - id: usize, + #[clap(value_parser = validate_item)] + item: Item, /// Server #[arg(short, long)] server: Option, @@ -116,7 +131,8 @@ enum Commands { /// Stop then remove a process #[command(visible_alias = "rm")] Remove { - id: usize, + #[clap(value_parser = validate_item)] + item: Item, /// Server #[arg(short, long)] server: Option, @@ -194,8 +210,8 @@ fn main() { match &cli.command { Commands::Start { name, args, watch, server } => cli::start(name, args, watch, &defaults(server)), - Commands::Stop { id, server } => cli::stop(id, &defaults(server)), - Commands::Remove { id, server } => cli::remove(id, &defaults(server)), + Commands::Stop { item, server } => cli::stop(item, &defaults(server)), + Commands::Remove { item, server } => cli::remove(item, &defaults(server)), Commands::Env { id, server } => cli::env(id, &defaults(server)), Commands::Details { id, format, server } => cli::info(id, format, &defaults(server)), Commands::List { format, server } => cli::list(format, &defaults(server)), diff --git a/src/process/mod.rs b/src/process/mod.rs index f6fe68a6c15a23ab62ffd925b15ceb9213c1b88c..7743222aa5bf1c1b59d85e2d6795abfffe8c4d3b 100644 --- a/src/process/mod.rs +++ b/src/process/mod.rs @@ -297,16 +297,12 @@ impl Runner { env: unix::env(), }); - println!("{:?}", process.pid); - process.running = true; process.children = vec![]; process.started = Utc::now(); process.crash.crashed = false; process.env = env::vars().collect(); - println!("{:?}", process); - then!(dead, process.restarts += 1); then!(dead, process.crash.value += 1); then!(!dead, process.crash.value = 0); @@ -339,17 +335,27 @@ impl Runner { } pub fn items(&self) -> BTreeMap { self.list.clone() } + pub fn items_mut(&mut self) -> &mut BTreeMap { &mut self.list } pub fn save(&self) { then!(self.remote.is_none(), dump::write(&self)) } + pub fn count(&mut self) -> usize { self.list().count() } + pub fn is_empty(&self) -> bool { self.list.is_empty() } + pub fn exists(&self, id: usize) -> bool { self.list.contains_key(&id) } + pub fn info(&self, id: usize) -> Option<&Process> { self.list.get(&id) } + pub fn list<'l>(&'l mut self) -> impl Iterator { self.list.iter_mut().map(|(k, v)| (k, v)) } + pub fn process(&mut self, id: usize) -> &mut Process { self.list.get_mut(&id).unwrap_or_else(|| crashln!("{} Process ({id}) not found", *helpers::FAIL)) } + pub fn pid(&self, id: usize) -> i64 { self.list.get(&id).unwrap_or_else(|| crashln!("{} Process ({id}) not found", *helpers::FAIL)).pid } + pub fn find(&self, name: &str) -> Option { self.list.iter().find(|(_, p)| p.name == name).map(|(id, _)| *id) } + pub fn get(self, id: usize) -> ProcessWrapper { ProcessWrapper { id,