1use std::{error::Error, process::Command, sync::mpsc};
2
3#[derive(Debug)]
4pub enum OsedaRunError {
5 BuildError(String),
6 ServeError(String),
7}
8
9impl std::error::Error for OsedaRunError {}
10impl std::fmt::Display for OsedaRunError {
11 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
12 match self {
13 Self::BuildError(msg) => write!(f, "Oseda Build Error: {}", msg),
14 Self::ServeError(msg) => write!(f, "Oseda Serve Error: {}", msg),
15 }
16 }
17}
18
19pub fn run() -> Result<(), OsedaRunError> {
20 match Command::new("npx").arg("vite").arg("build").status() {
22 Ok(status) => {
23 if !status.success() {
24 println!("Error: `npx vite build` exited with a failure.");
25 println!("Please ensure that npx and vite are installed properly.");
26 return Err(OsedaRunError::BuildError(
27 "could not 'npx vite build'".to_string(),
28 ));
29 }
30 }
31 Err(e) => {
32 println!("Error: failed to execute `npx vite build`: {e}");
33 println!("Please ensure that `npx` and `vite` are installed and in your PATH.");
34 return Err(OsedaRunError::BuildError(
35 "could not 'npx vite build'".to_string(),
36 ));
37 }
38 }
39
40 let mut child = Command::new("serve").arg("dist").spawn().map_err(|e| {
41 println!("Error starting `serve dist`: {e}");
42 OsedaRunError::ServeError("failed to start serve".into())
43 })?;
44 let (tx, rx) = mpsc::channel();
48 ctrlc::set_handler(move || {
49 println!("\nSIGINT received. Attempting graceful shutdown...");
50 let _ = tx.send(());
51 })
52 .expect("Error setting Ctrl+C handler");
53
54 rx.recv().unwrap();
56
57 if let Err(e) = child.kill() {
59 println!("Failed to kill `serve`: {e}");
60 } else {
61 println!("`serve` process terminated.");
62 }
63
64 let _ = child.wait();
65
66 Ok(())
67}