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