android_build/
java_run.rs

1//! Builder for customizing and invoking a `java` command.
2
3use std::ffi::{OsStr, OsString};
4use std::path::PathBuf;
5use std::process::{Command, ExitStatus};
6use crate::env_paths::{self, PathExt};
7
8/// A builder for a `java` command that can be invoked.
9///
10/// If you need to customize the `java` command beyond what is provided here,
11/// you can use the [`JavaRun::command()`] method to get a [`Command`]
12/// that can be further customized with additional arguments.
13///
14/// Documentation on `java` options are based on
15/// <https://dev.java/learn/jvm/tools/core/java/>.
16#[derive(Clone, Debug, Default)]
17pub struct JavaRun {
18    /// Override the default `JAVA_HOME` path.
19    /// Otherwise, the default path is found using the `JAVA_HOME` env var.
20    java_home: Option<PathBuf>,
21
22    /// Specify where to find user class files and annotation processors.
23    /// If not provided, the current directory will be used.
24    class_paths: Vec<OsString>,
25
26    /// Specify which main class to run.
27    main_class: Option<OsString>,
28
29    /// Specify a JAR file to run instead of a main class.
30    jar_file: Option<OsString>,
31
32    /// Arguments to be passed to the main class being run by `java`.
33    args: Vec<OsString>,
34
35    /// If `true`, enable preview language features.
36    enable_preview_features: bool,
37}
38
39impl JavaRun {
40    /// Creates a new `JavaRun` instance with default values,
41    /// which can be further customized using the builder methods.
42    pub fn new() -> Self {
43        Default::default()
44    }
45
46    /// Executes the `java` command based on this `JavaRun` instance.
47    pub fn run(&self) -> std::io::Result<ExitStatus> {
48        self.command()?.status()
49    }
50
51    /// Returns a [`Command`] based on this `JavaRun` instance
52    /// that can be inspected or customized before being executed.
53    pub fn command(&self) -> std::io::Result<Command> {
54        let jh_clone = self.java_home.clone();
55        let java_home = jh_clone
56            .and_then(PathExt::path_if_exists)
57            .or_else(env_paths::java_home)
58            .ok_or_else(|| std::io::Error::other(
59                "JAVA_HOME not provided, and could not be auto-discovered."
60            ))?;
61
62        let mut cmd = Command::new(java_home.join("bin").join("java"));
63
64        if self.enable_preview_features {
65            cmd.arg("--enable-preview");
66        }
67        if !self.class_paths.is_empty() {
68            cmd.arg("-cp").arg(self.class_paths.join(OsStr::new(";")));
69        }
70        match (self.main_class.as_ref(), self.jar_file.as_ref()) {
71            (Some(main_class), None) => { cmd.arg(main_class); }
72            (None, Some(jar_file)) => { cmd.arg("-jar").arg(jar_file); }
73            (Some(_), Some(_)) => {
74                return Err(std::io::Error::other(
75                    "Cannot provide both a main class AND a JAR file."
76                ));
77            },
78            _ => { }
79        }
80        
81
82        self.args.iter().for_each(|f| { cmd.arg(f); });
83
84        Ok(cmd)
85    }
86
87    ///////////////////////////////////////////////////////////////////////////
88    //////////////////////// Builder methods below ////////////////////////////
89    ///////////////////////////////////////////////////////////////////////////
90
91    /// Override the default `JAVA_HOME` path.
92    ///
93    /// If not set, the default path is found using the `JAVA_HOME` env var.
94    pub fn java_home<P: AsRef<OsStr>>(&mut self, java_home: P) -> &mut Self {
95        self.java_home = Some(java_home.as_ref().into());
96        self
97    }
98
99    /// Specify where to find user class files.
100    ///
101    /// If no class paths are provided, the current directory will be used.
102    pub fn class_path<S: AsRef<OsStr>>(&mut self, class_path: S) -> &mut Self {
103        self.class_paths.push(class_path.as_ref().into());
104        self
105    }
106
107    /// Enable or disable preview language features.
108    pub fn enable_preview_features(&mut self, enable_preview_features: bool) -> &mut Self {
109        self.enable_preview_features = enable_preview_features;
110        self
111    }
112    
113    /// Specify the main class to launch when running the `java` command.
114    ///
115    /// Note that this and the `jar_file` are mutually exclusive;
116    /// only one can be chosen at a time.
117    pub fn main_class<S: AsRef<OsStr>>(&mut self, class: S) -> &mut Self {
118        self.main_class = Some(class.as_ref().into());
119        self
120    }
121
122    /// Specify the JAR file to run with the `java` command.
123    ///
124    /// Note that this and the `main_class` are mutually exclusive;
125    /// only one can be chosen at a time.
126    pub fn jar_file<P: AsRef<OsStr>>(&mut self, jar_file: P) -> &mut Self {
127        self.jar_file = Some(jar_file.as_ref().into());
128        self
129    }
130
131    /// Add an argument to be passed to the main class being run by `java`.
132    pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Self {
133        self.args.push(arg.as_ref().into());
134        self
135    }
136
137    /// Adds multiple arguments to be passed to the main class being run by `java`.
138    pub fn args<I>(&mut self, args: I) -> &mut Self
139    where
140        I: IntoIterator,
141        I::Item: AsRef<OsStr>,
142    {
143        self.args.extend(args.into_iter().map(|a| a.as_ref().into()));
144        self
145    }
146}