1use crate::cli;
2use crate::config::{load_config, resolve_session};
3use crate::protocol::{Request, Response};
4use futures::future::join_all;
5
6pub async fn execute(
7 cli_session: Option<&str>,
8 only: Option<&str>,
9 config_path: Option<&str>,
10) -> i32 {
11 let (path, config) = match load_config(config_path) {
12 Ok(c) => c,
13 Err(e) => {
14 eprintln!("error: {}", e);
15 return 1;
16 }
17 };
18
19 let session = resolve_session(cli_session, config.session.as_deref());
20
21 let only_set: Option<Vec<&str>> = only.map(|s| s.split(',').collect());
22
23 let groups = match config.startup_order() {
24 Ok(g) => g,
25 Err(e) => {
26 eprintln!("error: {}", e);
27 return 1;
28 }
29 };
30
31 for group in &groups {
32 let names: Vec<&String> = group
33 .iter()
34 .filter(|name| {
35 only_set
36 .as_ref()
37 .is_none_or(|only| only.contains(&name.as_str()))
38 })
39 .collect();
40
41 let start_futures: Vec<_> = names
43 .iter()
44 .map(|name| {
45 let def = &config.processes[*name];
46
47 let resolved_cwd = def.cwd.as_ref().map(|c| {
48 let p = std::path::Path::new(c);
49 if p.is_relative() {
50 path.parent()
51 .unwrap_or(std::path::Path::new("."))
52 .join(p)
53 .to_string_lossy()
54 .to_string()
55 } else {
56 c.clone()
57 }
58 });
59
60 let env = if def.env.is_empty() {
61 None
62 } else {
63 Some(def.env.clone())
64 };
65
66 let req = Request::Run {
67 command: def.cmd.clone(),
68 name: Some((*name).clone()),
69 cwd: resolved_cwd,
70 env,
71 };
72 let name = (*name).clone();
73 async move {
74 let result = cli::request(session, &req, true).await;
75 (name, result)
76 }
77 })
78 .collect();
79
80 let results = join_all(start_futures).await;
81
82 for (name, result) in &results {
83 match result {
84 Ok(Response::RunOk { name, id, pid, .. }) => {
85 println!("started {} (id: {}, pid: {})", name, id, pid);
86 }
87 Ok(Response::Error { code, message }) => {
88 eprintln!("error starting {}: {}", name, message);
89 return *code;
90 }
91 _ => return 1,
92 }
93 }
94
95 let ready_futures: Vec<_> = names
97 .iter()
98 .filter_map(|name| {
99 let def = &config.processes[*name];
100 def.ready.as_ref().map(|ready| {
101 let req = Request::Wait {
102 target: (*name).clone(),
103 until: Some(ready.clone()),
104 regex: false,
105 exit: false,
106 timeout_secs: Some(30),
107 };
108 let name = (*name).clone();
109 async move {
110 let result = cli::request(session, &req, false).await;
111 (name, result)
112 }
113 })
114 })
115 .collect();
116
117 let ready_results = join_all(ready_futures).await;
118
119 for (name, result) in &ready_results {
120 match result {
121 Ok(Response::WaitMatch { .. }) => println!("{} is ready", name),
122 Ok(Response::WaitTimeout) => {
123 eprintln!("warning: {} did not become ready within 30s", name);
124 }
125 Ok(Response::Error { message, .. }) => {
126 eprintln!("error waiting for {}: {}", name, message);
127 return 1;
128 }
129 _ => {}
130 }
131 }
132 }
133
134 println!("all processes started");
135 0
136}