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