use crate::cli;
use crate::config::{load_config, resolve_session};
use crate::protocol::{Request, Response, RestartPolicy, WatchConfig};
use futures::future::join_all;
pub async fn execute(
cli_session: Option<&str>,
only: Option<&str>,
config_path: Option<&str>,
proxy: bool,
) -> i32 {
let (path, config) = match load_config(config_path) {
Ok(c) => c,
Err(e) => {
eprintln!("error: {}", e);
return 1;
}
};
let session = resolve_session(cli_session, config.session.as_deref());
let only_set: Option<Vec<&str>> = only.map(|s| s.split(',').collect());
let groups = match config.startup_order() {
Ok(g) => g,
Err(e) => {
eprintln!("error: {}", e);
return 1;
}
};
let enable_proxy = proxy || config.proxy.unwrap_or(false);
if enable_proxy && let Some(code) = cli::enable_proxy(session, config.proxy_port).await {
return code;
}
for group in &groups {
let names: Vec<&String> = group
.iter()
.filter(|name| {
only_set
.as_ref()
.is_none_or(|only| only.contains(&name.as_str()))
})
.collect();
let start_futures: Vec<_> = names
.iter()
.map(|name| {
let def = &config.processes[*name];
let resolved_cwd = def.cwd.as_ref().map(|c| {
let p = std::path::Path::new(c);
if p.is_relative() {
path.parent()
.unwrap_or(std::path::Path::new("."))
.join(p)
.to_string_lossy()
.to_string()
} else {
c.clone()
}
});
let env = if def.env.is_empty() {
None
} else {
Some(def.env.clone())
};
let restart = def
.autorestart
.as_ref()
.map(|m| RestartPolicy::from_args(m, def.max_restarts, def.restart_delay));
let watch = def.watch.as_ref().and_then(|paths| {
WatchConfig::from_args(
paths.clone(),
def.watch_ignore.clone().unwrap_or_default(),
)
});
let req = Request::Run {
command: def.cmd.clone(),
name: Some((*name).clone()),
cwd: resolved_cwd,
env,
port: def.port,
restart,
watch,
};
let name = (*name).clone();
async move {
let result = cli::request(session, &req, true).await;
(name, result)
}
})
.collect();
let results = join_all(start_futures).await;
for (name, result) in &results {
match result {
Ok(Response::RunOk {
name, id, pid, url, ..
}) => match url {
Some(u) => println!("started {} (id: {}, pid: {}, {})", name, id, pid, u),
None => println!("started {} (id: {}, pid: {})", name, id, pid),
},
Ok(Response::Error { code, message }) => {
eprintln!("error starting {}: {}", name, message);
return code.exit_code();
}
_ => return 1,
}
}
let ready_futures: Vec<_> = names
.iter()
.filter_map(|name| {
let def = &config.processes[*name];
def.ready.as_ref().map(|ready| {
let req = Request::Wait {
target: (*name).clone(),
until: Some(ready.clone()),
regex: false,
exit: false,
timeout_secs: Some(30),
};
let name = (*name).clone();
async move {
let result = cli::request(session, &req, false).await;
(name, result)
}
})
})
.collect();
let ready_results = join_all(ready_futures).await;
for (name, result) in &ready_results {
match result {
Ok(Response::WaitMatch { .. }) => println!("{} is ready", name),
Ok(Response::WaitTimeout) => {
eprintln!("warning: {} did not become ready within 30s", name);
}
Ok(Response::Error { message, .. }) => {
eprintln!("error waiting for {}: {}", name, message);
return 1;
}
_ => {}
}
}
}
println!("all processes started");
0
}