glory_cli/service/
serve.rs1use std::sync::Arc;
2
3use crate::{
4 config::Project,
5 ext::{anyhow::Result, append_str_to_filename, determine_pdb_filename, fs},
6 logger::GRAY,
7 signal::{Interrupt, ReloadSignal, ServerRestart},
8};
9use camino::Utf8PathBuf;
10use tokio::{
11 process::{Child, Command},
12 select,
13 task::JoinHandle,
14};
15
16pub async fn spawn(proj: &Arc<Project>) -> JoinHandle<Result<()>> {
17 let mut int = Interrupt::subscribe_shutdown();
18 let proj = proj.clone();
19 let mut change = ServerRestart::subscribe();
20 tokio::spawn(async move {
21 let mut server = ServerProcess::start_new(&proj).await?;
22 loop {
23 select! {
24 res = change.recv() => {
25 if let Ok(()) = res {
26 server.restart().await?;
27 ReloadSignal::send_full();
28 }
29 },
30 _ = int.recv() => {
31 server.kill().await;
32 return Ok(())
33 },
34 }
35 }
36 })
37}
38
39struct ServerProcess {
40 process: Option<Child>,
41 envs: Vec<(&'static str, String)>,
42 binary: Utf8PathBuf,
43}
44
45impl ServerProcess {
46 fn new(proj: &Project) -> Self {
47 Self {
48 process: None,
49 envs: proj.to_envs(),
50 binary: proj.bin.exe_file.clone(),
51 }
52 }
53
54 async fn start_new(proj: &Project) -> Result<Self> {
55 let mut me = Self::new(proj);
56 me.start().await?;
57 Ok(me)
58 }
59
60 async fn kill(&mut self) {
61 if let Some(proc) = self.process.as_mut() {
62 if let Err(e) = proc.kill().await {
63 log::error!("Serve error killing server process: {e}");
64 } else {
65 log::trace!("Serve stopped");
66 }
67 self.process = None;
68 }
69 }
70
71 async fn restart(&mut self) -> Result<()> {
72 self.kill().await;
73 self.start().await?;
74 log::trace!("Serve restarted");
75 Ok(())
76 }
77
78 async fn start(&mut self) -> Result<()> {
79 let bin = &self.binary;
80 let child = if bin.exists() {
81 let bin_path = if cfg!(target_os = "windows") {
83 let new_bin_path = append_str_to_filename(bin, ".serving")?;
86 log::debug!(
87 "Copying server binary {} to {}",
88 GRAY.paint(bin.as_str()),
89 GRAY.paint(new_bin_path.as_str())
90 );
91 fs::copy(bin, &new_bin_path).await?;
92 if let Some(pdb) = determine_pdb_filename(bin) {
94 let new_pdb_path = append_str_to_filename(&pdb, ".serving")?;
95 log::debug!(
96 "Copying server binary debug info {} to {}",
97 GRAY.paint(pdb.as_str()),
98 GRAY.paint(new_pdb_path.as_str())
99 );
100 fs::copy(&pdb, &new_pdb_path).await?;
101 }
102 new_bin_path
103 } else {
104 bin.clone()
105 };
106
107 log::debug!("Serve running {}", GRAY.paint(bin_path.as_str()));
108 let cmd = Some(Command::new(bin_path).envs(self.envs.clone()).spawn()?);
109 let port = self
110 .envs
111 .iter()
112 .find_map(|(k, v)| if k == &"GLORY_SITE_ADDR" { Some(v.to_string()) } else { None })
113 .unwrap_or_default();
114 log::info!("Serving at http://{port}");
115 cmd
116 } else {
117 log::debug!("Serve no exe found {}", GRAY.paint(bin.as_str()));
118 None
119 };
120 self.process = child;
121 Ok(())
122 }
123}