Files
raven/src/java.rs

234 lines
5.6 KiB
Rust

use std::fmt;
use std::path::{Path, PathBuf};
use anyhow::Context;
use bytesize::ByteSize;
pub const JAVA_VM: &str = "java";
pub const JAVA_COMPILER: &str = "javac";
pub const JAVA_EXT_SOURCE: &str = "java";
pub const JAVA_EXT_CLASS: &str = "class";
// TODO: Builder-pattern JVM wrapper.
#[derive(Debug, Default, Clone)]
pub struct JVMBuilder {
assertions: bool,
monitor: bool,
ram_min: Option<ByteSize>,
ram_max: Option<ByteSize>,
}
impl JVMBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn assertions(&mut self, assertions: bool) -> &mut Self {
self.assertions = assertions;
self
}
pub fn ram_min(&mut self, ram_min: ByteSize) -> &mut Self {
self.ram_min = Some(ram_min);
self
}
pub fn ram_max(&mut self, ram_max: ByteSize) -> &mut Self {
self.ram_max = Some(ram_max);
self
}
/// Monitor stdout and stderr in raven's process.
pub fn monitor(&mut self, monitor: bool) -> &mut Self {
self.monitor = monitor;
self
}
pub fn build(&self) -> JVM {
let mut flags = vec![];
if self.assertions {
flags.push(VMFlag::EnableAssert);
}
if let Option::Some(size) = self.ram_min {
flags.push(VMFlag::HeapMin { size });
}
if let Option::Some(size) = self.ram_max {
flags.push(VMFlag::HeapMax { size });
}
JVM {
monitor: self.monitor,
flags,
}
}
}
pub struct JVM {
monitor: bool,
flags: Vec<VMFlag>,
}
impl JVM {
pub fn run<P: AsRef<Path>>(
self,
entry_point: P,
) -> anyhow::Result<(Option<String>, Option<String>)> {
let mut cmd = vec![JAVA_VM.to_string()];
cmd.extend(
self.flags
.clone()
.into_iter()
.flat_map(|f| Into::<Vec<String>>::into(f)),
);
for part in &cmd {
println!("{}", part);
}
cmd.push("-cp".to_string());
cmd.push(entry_point.as_ref().display().to_string());
// Jave needs a class path and a file name to run.
cmd.push("Test".to_string());
println!("{:?}", cmd.as_slice());
let result = crate::io::run_process(cmd.as_slice());
if self.monitor
&& let Result::Ok((stdout, stderr)) = &result
{
if let Option::Some(stdout) = stdout
&& stdout.len() > 0
{
print!("{stdout}");
}
if let Option::Some(stderr) = stderr
&& stderr.len() > 0
{
eprintln!("{stderr}");
}
}
result
}
}
#[derive(Debug, Clone)]
pub enum VMFlag {
EnableAssert,
HeapMax { size: ByteSize },
HeapMin { size: ByteSize },
Version,
}
impl Into<Vec<String>> for VMFlag {
fn into(self) -> Vec<String> {
vec![self.to_string()]
}
}
// Currently being kept around because it's fine for the current branches,
// and is currently serving a to_string() method for the Java version fetch.
impl fmt::Display for VMFlag {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"-{}",
match self {
Self::EnableAssert => String::from("ea"),
Self::HeapMax { size } => format!("Xmx{}", size.as_u64()),
Self::HeapMin { size } => format!("Xms{}", size.as_u64()),
Self::Version => String::from("-version"), // --version
}
)
}
}
#[derive(Debug, Default, Clone)]
pub struct CompilerBuilder {
class_path: Option<PathBuf>,
destination: Option<PathBuf>,
}
impl CompilerBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn class_path<S: AsRef<Path>>(&mut self, class_path: S) -> &mut Self {
self.class_path = Some(class_path.as_ref().to_path_buf());
self
}
pub fn destination<S: AsRef<Path>>(&mut self, destination: S) -> &mut Self {
self.destination = Some(destination.as_ref().to_path_buf());
self
}
pub fn build(&self) -> Compiler {
let mut flags = vec![];
if let Option::Some(path) = self.destination.to_owned() {
flags.push(CompilerFlag::Destination { path });
}
if let Option::Some(path) = self.class_path.to_owned() {
flags.push(CompilerFlag::Classpath { path });
}
Compiler { flags }
}
}
#[derive(Debug, Clone)]
pub struct Compiler {
flags: Vec<CompilerFlag>,
}
impl Compiler {
pub fn compile<P: AsRef<Path>>(
self,
path: P,
) -> anyhow::Result<(Option<String>, Option<String>)> {
let mut cmd: Vec<String> = vec![JAVA_COMPILER.to_string()];
cmd.extend(
self.flags
.clone()
.into_iter()
.flat_map(|f| Into::<Vec<String>>::into(f)),
);
cmd.extend(crate::fs::expand_files(path)?.into_iter().filter_map(|f| {
Some(
f.to_str()
.context("Failed to cast PathBuf to &str")
.ok()?
.to_string(),
)
}));
crate::io::run_process(cmd.as_slice())
}
}
#[derive(Debug, Clone)]
pub enum CompilerFlag {
Destination { path: PathBuf },
Classpath { path: PathBuf },
}
impl Into<Vec<String>> for CompilerFlag {
fn into(self) -> Vec<String> {
match self {
Self::Classpath { path } => vec!["-cp".to_string(), path.display().to_string()],
Self::Destination { path } => vec!["-d".to_string(), path.display().to_string()],
}
}
}