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, ram_max: Option, class_path: PathBuf, } impl JVMBuilder { pub fn new>(class_path: P) -> Self { Self { class_path: class_path.as_ref().to_path_buf(), ..Default::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![VMFlag::Classpath { path: self.class_path.to_owned(), }]; 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, } impl JVM { pub fn run>( self, entry_point: P, ) -> anyhow::Result<(Option, Option)> { let mut cmd = vec![JAVA_VM.to_string()]; cmd.extend( self.flags .clone() .into_iter() .flat_map(|f| Into::>::into(f)), ); cmd.push(entry_point.as_ref().to_path_buf().display().to_string()); 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 { Classpath { path: PathBuf }, EnableAssert, HeapMax { size: ByteSize }, HeapMin { size: ByteSize }, Version, } impl Into> for VMFlag { fn into(self) -> Vec { self.to_string() .split_ascii_whitespace() .map(|s| s.to_string()) .collect() } } // 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::Classpath { path } => format!("classpath {}", path.display()), 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, destination: Option, } impl CompilerBuilder { pub fn new() -> Self { Self::default() } pub fn class_path>(&mut self, class_path: S) -> &mut Self { self.class_path = Some(class_path.as_ref().to_path_buf()); self } pub fn destination>(&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, } impl Compiler { pub fn compile>( self, path: P, ) -> anyhow::Result<(Option, Option)> { let mut cmd: Vec = vec![JAVA_COMPILER.to_string()]; cmd.extend( self.flags .clone() .into_iter() .flat_map(|f| Into::>::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 { Classpath { path: PathBuf }, Destination { path: PathBuf }, } impl Into> for CompilerFlag { fn into(self) -> Vec { match self { Self::Classpath { path } => vec!["-classpath".to_string(), path.display().to_string()], Self::Destination { path } => vec!["-d".to_string(), path.display().to_string()], } } }