Most of the refactor. Need to switch machines.

This commit is contained in:
Olivia Brooks
2026-02-15 09:36:04 -05:00
parent dda863e512
commit e41d4bcd76
61 changed files with 3390 additions and 618 deletions

21
crates/java/Cargo.toml Normal file
View File

@@ -0,0 +1,21 @@
# May want to find a better name, more reflective of the JDK part
# than the entire Java language.
[package]
name = "java"
version = "0.1.0"
edition.workspace = true
license.workspace = true
description = "Tools for interfacing with the Java Development Kit"
repository.workspace = true
publish.workspace = true
test.workspace = true
[dependencies]
bytesize.workspace = true
derive_more.workspace = true
fs.workspace = true
io.workspace = true
semver.workspace = true

View File

@@ -0,0 +1,80 @@
use std::path::{Path, PathBuf};
use crate::JAVA_BIN_COMPILER;
use crate::Result;
#[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) -> Result<(Option<String>, Option<String>)> {
let mut cmd: Vec<String> = vec![JAVA_BIN_COMPILER.to_string()];
cmd.extend(
self.flags
.clone()
.into_iter()
.flat_map(|f| Into::<Vec<String>>::into(f)),
);
cmd.extend(
fs::expand_files(path)?
.into_iter()
.filter_map(|f| Some(f.to_str()?.to_string())),
);
Ok(io::run_process(cmd.as_slice())?)
}
}
#[derive(Debug, Clone)]
pub enum CompilerFlag {
Classpath { path: PathBuf },
Destination { path: PathBuf },
}
impl Into<Vec<String>> for CompilerFlag {
fn into(self) -> Vec<String> {
match self {
Self::Classpath { path } => vec!["-classpath".to_string(), path.display().to_string()],
Self::Destination { path } => vec!["-d".to_string(), path.display().to_string()],
}
}
}

19
crates/java/src/error.rs Normal file
View File

@@ -0,0 +1,19 @@
use derive_more::{Display, From};
pub type Result<T> = core::result::Result<T, Error>;
#[derive(Debug, From, Display)]
pub enum Error {
EmptyStdout,
#[from]
Io(io::Error),
NthOutOfBounds,
#[from]
Semver(semver::Error),
#[from]
StdIo(std::io::Error),
}

49
crates/java/src/lib.rs Normal file
View File

@@ -0,0 +1,49 @@
pub mod compiler;
pub mod error;
pub mod runtime;
use std::str::FromStr;
pub use error::{Error, Result};
use runtime::VMFlag;
pub const JAVA_BIN_VM: &str = "java";
pub const JAVA_BIN_COMPILER: &str = "javac";
pub const JAVA_EXT_SOURCE: &str = "java";
pub const JAVA_EXT_CLASS: &str = "class";
pub const F_JAVA_VERSION: &str = ".java-version";
/// Uses the java binary to parse its stdout for version information.
///
/// This is non-caching.
pub fn get_javac_ver() -> Result<semver::Version> {
// TODO: Consider making this pull the version info from javac instead?
/*
* $ java --version
* openjdk 21.0.9 2025-10-21
* OpenJDK Runtime Environment (build 21.0.9+10)
* OpenJDK 64-Bit Server VM (build 21.0.9+10, mixed mode, sharing)
*/
Ok(semver::Version::from_str(
get_java_version_info()?
.lines()
.nth(0)
.ok_or(Error::EmptyStdout)?
.split_ascii_whitespace()
.nth(1)
.ok_or(Error::NthOutOfBounds)?,
)?)
}
/// Calls the java binary, returning the complete stdout version information.
fn get_java_version_info() -> Result<String> {
Ok(
io::run_process(&[JAVA_BIN_VM, VMFlag::Version.to_string().as_str()])?
.0
.ok_or(Error::EmptyStdout)?,
)
}

144
crates/java/src/runtime.rs Normal file
View File

@@ -0,0 +1,144 @@
use std::fmt;
use std::path::{Path, PathBuf};
use crate::JAVA_BIN_VM;
use crate::Result;
use bytesize::ByteSize;
#[derive(Debug, Default, Clone)]
pub struct JVMBuilder {
assertions: bool,
monitor: bool,
ram_min: Option<ByteSize>,
ram_max: Option<ByteSize>,
class_path: PathBuf,
}
impl JVMBuilder {
pub fn new<P: AsRef<Path>>(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<VMFlag>,
}
impl JVM {
pub fn run<P: AsRef<Path>>(self, entry_point: P) -> Result<(Option<String>, Option<String>)> {
let mut cmd = vec![JAVA_BIN_VM.to_string()];
cmd.extend(
self.flags
.clone()
.into_iter()
.flat_map(|f| Into::<Vec<String>>::into(f)),
);
cmd.push(entry_point.as_ref().to_path_buf().display().to_string());
let result = io::run_process(cmd.as_slice())?;
if self.monitor {
let (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}");
}
}
Ok(result)
}
}
#[derive(Debug, Clone)]
pub enum VMFlag {
Classpath { path: PathBuf },
EnableAssert,
HeapMax { size: ByteSize },
HeapMin { size: ByteSize },
Version,
}
impl Into<Vec<String>> for VMFlag {
fn into(self) -> Vec<String> {
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
}
)
}
}