1use std::{
2 process::Command,
3 sync::{
4 atomic::{AtomicBool, Ordering},
5 Arc,
6 },
7 time::Duration,
8};
9
10#[derive(Debug)]
12pub enum OsedaRunError {
13 BuildError(String),
14 ServeError(String),
15}
16
17impl std::error::Error for OsedaRunError {}
18impl std::fmt::Display for OsedaRunError {
19 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20 match self {
21 Self::BuildError(msg) => write!(f, "Oseda Build Error: {}", msg),
22 Self::ServeError(msg) => write!(f, "Oseda Serve Error: {}", msg),
23 }
24 }
25}
26
27pub fn run() -> Result<(), OsedaRunError> {
38 run_with_shutdown(Arc::new(AtomicBool::new(false)))
40}
41
42pub fn run_with_shutdown(shutdown_flag: Arc<AtomicBool>) -> Result<(), OsedaRunError> {
43 match Command::new("npx").arg("vite").arg("build").status() {
45 Ok(status) => {
46 if !status.success() {
47 println!("Error: `npx vite build` exited with a failure.");
48 println!("Please ensure that npx and vite are installed properly.");
49 return Err(OsedaRunError::BuildError(
50 "could not 'npx vite build'".to_string(),
51 ));
52 }
53 }
54 Err(e) => {
55 println!("Error: failed to execute `npx vite build`: {e}");
56 println!("Please ensure that `npx` and `vite` are installed and in your PATH.");
57 return Err(OsedaRunError::BuildError(
58 "could not 'npx vite build'".to_string(),
59 ));
60 }
61 }
62
63 let mut child = Command::new("npx")
64 .arg("serve")
65 .arg("dist")
66 .spawn()
67 .map_err(|e| {
68 println!("Error starting `serve dist`: {e}");
69 OsedaRunError::ServeError("failed to start serve".into())
70 })?;
71 let ctrlc_flag = shutdown_flag.clone();
76 ctrlc::set_handler(move || {
77 println!("\nSIGINT received. Attempting graceful shutdown...");
78 ctrlc_flag.store(true, Ordering::SeqCst);
79 })
80 .map_err(|e| {
81 println!("Error setting ctrl+c handler: {e}");
82 OsedaRunError::ServeError("failed to set handler".into())
83 })?;
84
85 while !shutdown_flag.load(Ordering::SeqCst) {
87 std::thread::sleep(Duration::from_millis(100));
88 }
89
90 if let Err(e) = child.kill() {
92 println!("Failed to kill `serve`: {e}");
93 } else {
94 println!("`serve` process terminated.");
95 }
96
97 let _ = child.wait();
98
99 Ok(())
100}