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