lighty_java/
runtime.rs

1
2use crate::errors::{JavaRuntimeError, JavaRuntimeResult};
3use std::path::{Path, PathBuf};
4use std::process::Stdio;
5use tracing::info;
6use tokio::io::AsyncReadExt;
7use tokio::process::{Child, Command};
8use tokio::sync::oneshot::Receiver;
9use tracing::debug;
10pub struct JavaRuntime(pub PathBuf);
11
12impl JavaRuntime {
13    pub fn new(path: PathBuf) -> JavaRuntime {
14        JavaRuntime(path)
15    }
16
17    pub async fn execute(&self, arguments: Vec<String>, game_dir: &Path) -> JavaRuntimeResult<Child> {
18        if !self.0.exists() {
19            return Err(JavaRuntimeError::NotFound {
20                path: self.0.display().to_string(),
21            });
22        }
23        let mut command = Command::new(&self.0);
24
25
26        //DEBUG TEST
27        debug!("Executing Java runtime: {}", self.0.display());
28        info!("Executing Java runtime: {}", self.0.display());
29        info!("Arguments: {:?}", &arguments);
30
31
32
33
34        command.current_dir(game_dir);
35
36        command.args(arguments);
37        println!("Java runtime: {}", self.0.display());
38
39        command.stderr(Stdio::piped()).stdout(Stdio::piped());
40
41        let child = command.spawn()?;
42        Ok(child)
43    }
44
45    pub async fn handle_io<D: Send + Sync>(
46        &self,
47        running_task: &mut Child,
48        on_stdout: fn(&D, &[u8]) -> JavaRuntimeResult<()>,
49        on_stderr: fn(&D, &[u8]) -> JavaRuntimeResult<()>,
50        terminator: Receiver<()>,
51        data: &D,
52    ) -> JavaRuntimeResult<()> {
53        let mut stdout = running_task.stdout.take()
54            .ok_or(JavaRuntimeError::IoCaptureFailure)?;
55        let mut stderr = running_task.stderr.take()
56            .ok_or(JavaRuntimeError::IoCaptureFailure)?;
57
58        let mut stdout_buf = vec![0; 1024];
59        let mut stderr_buf = vec![0; 1024];
60
61        tokio::pin!(terminator);
62
63        loop {
64            tokio::select! {
65                read_len = stdout.read(&mut stdout_buf) => {
66                    let _ = on_stdout(&data, &stdout_buf[..read_len?]);
67                },
68                read_len = stderr.read(&mut stderr_buf) => {
69                    let _ = on_stderr(&data, &stderr_buf[..read_len?]);
70                },
71                _ = &mut terminator => {
72                    running_task.kill().await?;
73                    break;
74                },
75                exit_status = running_task.wait() => {
76                    let code = exit_status?.code().unwrap_or(7900); // 7900 = unwrap failed error code
77
78                    debug!("Process exited with code: {}", code);
79                    if code != 0 && code != -1073740791 { // -1073740791 = happens when the process is killed forcefully, we don't want to error in this case
80                        return Err(JavaRuntimeError::NonZeroExit { code });
81                    }
82                    break;
83                },
84            }
85        }
86        Ok(())
87    }
88}