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