arc_web/commands/
develop.rs1use std::path::PathBuf;
2use std::process::ExitStatus;
3use std::{fs, io};
4use tokio::io::{AsyncBufReadExt, BufReader, Lines};
5use tokio::process::{ChildStderr, ChildStdout, Command};
6use tokio::task::JoinHandle;
7use tokio::try_join;
8use tracing::{debug, error, info};
9
10pub async fn run_development() -> io::Result<()> {
13 info!("Starting development mode");
14
15 let cargo_watch_task: JoinHandle<io::Result<()>> = tokio::spawn(run_cargo_watch());
16 let bundle_task: JoinHandle<io::Result<()>> = tokio::spawn(run_vite_bundle());
17
18 match try_join!(cargo_watch_task, bundle_task) {
19 Ok(_) => {
20 info!("Development environment running successfully");
21 Ok(())
22 }
23 Err(e) => {
24 error!("Development environment error: {:?}", e);
25 Err(io::Error::other("Failed to run development tasks"))
26 }
27 }
28}
29
30async fn run_cargo_watch() -> io::Result<()> {
31 let mut cargo_watch_process = Command::new("cargo")
32 .arg("watch")
33 .arg("-x")
34 .arg("run serve")
35 .arg("-i")
36 .arg("database/*")
37 .arg("-i")
38 .arg("dist/*")
39 .arg("-i")
40 .arg("node_modules/*")
41 .stdout(std::process::Stdio::piped())
42 .stderr(std::process::Stdio::piped())
43 .spawn()
44 .expect("Failed to start Cargo Watch");
45
46 let stdout = cargo_watch_process
47 .stdout
48 .take()
49 .expect("Failed to capture stdout");
50 let stderr = cargo_watch_process
51 .stderr
52 .take()
53 .expect("Failed to capture stderr");
54
55 let stdout_task = tokio::spawn(async move {
56 let mut reader: Lines<BufReader<ChildStdout>> = BufReader::new(stdout).lines();
57 while let Ok(Some(line)) = reader.next_line().await {
58 debug!("[cargo-watch] {}", line);
59 }
60 });
61
62 let stderr_task = tokio::spawn(async move {
63 let mut reader: Lines<BufReader<ChildStderr>> = BufReader::new(stderr).lines();
64 while let Ok(Some(line)) = reader.next_line().await {
65 info!("[cargo-watch] {}", line);
66 }
67 });
68
69 let status = cargo_watch_process
70 .wait()
71 .await
72 .expect("Cargo Watch process wasn't running");
73
74 stdout_task.await.expect("Failed to handle stdout");
75 stderr_task.await.expect("Failed to handle stderr");
76
77 if !status.success() {
78 return Err(io::Error::other(format!(
79 "Cargo Watch process exited with status: {:?}",
80 status
81 )));
82 }
83
84 Ok(())
85}
86
87async fn run_vite_bundle() -> io::Result<()> {
88 if !fs::exists(PathBuf::from("node_modules")).unwrap() {
89 let mut npm_install_process = Command::new("npm")
90 .arg("install")
91 .stdout(std::process::Stdio::piped())
92 .stderr(std::process::Stdio::piped())
93 .spawn()
94 .expect("Failed to install nodejs dependencies!");
95
96 let status: ExitStatus = npm_install_process
97 .wait()
98 .await
99 .expect("Npm Install wasn't running");
100
101 if !status.success() {
102 return Err(io::Error::other(format!(
103 "Npm Install process exited with status: {:?}",
104 status
105 )));
106 }
107 }
108
109 let mut vite_process = Command::new("npx")
110 .arg("vite")
111 .arg("build")
112 .arg("--watch")
113 .stdout(std::process::Stdio::piped())
114 .stderr(std::process::Stdio::piped())
115 .spawn()
116 .expect("Failed to start Vite dev server");
117
118 let stdout = vite_process
119 .stdout
120 .take()
121 .expect("Failed to capture stdout");
122 let stderr = vite_process
123 .stderr
124 .take()
125 .expect("Failed to capture stderr");
126
127 let stdout_task = tokio::spawn(async move {
128 let mut reader: Lines<BufReader<ChildStdout>> = BufReader::new(stdout).lines();
129 while let Ok(Some(line)) = reader.next_line().await {
130 debug!("[vite] {}", line);
131 }
132 });
133
134 let stderr_task: JoinHandle<()> = tokio::spawn(async move {
135 let mut reader: Lines<BufReader<ChildStderr>> = BufReader::new(stderr).lines();
136 while let Ok(Some(line)) = reader.next_line().await {
137 info!("[vite] {}", line);
138 }
139 });
140
141 let status: ExitStatus = vite_process
142 .wait()
143 .await
144 .expect("Vite process wasn't running");
145
146 stdout_task.await.expect("Failed to handle stdout");
147 stderr_task.await.expect("Failed to handle stderr");
148
149 if !status.success() {
150 return Err(io::Error::other(format!(
151 "Vite process exited with status: {:?}",
152 status
153 )));
154 }
155
156 Ok(())
157}