Skip to main content

Crate inline_java

Crate inline_java 

Source
Expand description

§inline java

Embed Java directly in Rust — evaluated at program runtime (java!, java_fn!) or at compile time (ct_java!).

§Prerequisites

Java 8+ with javac and java on PATH.

§Quick start

# Cargo.toml
[dependencies]
inline_java = "0.1.0"

§java! — runtime, no parameters

Compiles and runs Java each time the surrounding Rust code executes. Expands to Result<T, inline_java::JavaError>.

use inline_java::java;

// No type annotation needed — the macro infers `i32` from `static int run()`
let x = java! {
    static int run() {
        return 42;
    }
}.unwrap();

§java_fn! — runtime, with parameters

Like java!, but run(...) may declare parameters. Expands to a Rust function value fn(P1, P2, …) -> Result<T, JavaError>. Parameters are serialised by Rust and piped to the Java process over stdin.

use inline_java::java_fn;

// Single parameter — return type inferred from `static int run()`
let doubled = java_fn! {
    static int run(int n) {
        return n * 2;
    }
}(21).unwrap();

// Multiple parameters
let msg: String = java_fn! {
    static String run(String greeting, String target) {
        return greeting + ", " + target + "!";
    }
}("Hello", "World").unwrap();

// Optional parameter
let result: Option<i32> = java_fn! {
    import java.util.Optional;
    static Optional<Integer> run(Optional<Integer> val) {
        return val.map(x -> x * 2);
    }
}(Some(21)).unwrap();

§ct_java! — compile time

Runs Java during rustc macro expansion and splices the result as a Rust literal at the call site. No parameters are allowed (values must be compile-time constants).

use inline_java::ct_java;

const PI: f64 = ct_java! {
    static double run() {
        return Math.PI;
    }
};

// Arrays work too — result is a Rust array literal baked into the binary
const PRIMES: [i32; 5] = ct_java! {
    static int[] run() {
        return new int[]{2, 3, 5, 7, 11};
    }
};

§Supported parameter types (java_fn!)

Declare parameters in the Java run(...) signature; Rust receives them with the mapped types below.

Java parameter typeRust parameter type
bytei8
shorti16
inti32
longi64
floatf32
doublef64
booleanbool
charchar
String&str
T[] / List<BoxedT>&[T]
Optional<BoxedT>Option<T>

§Supported return types

Java return typeRust return type
bytei8
shorti16
inti32
longi64
floatf32
doublef64
booleanbool
charchar
StringString
T[] / List<BoxedT>Vec<T>
Optional<BoxedT>Option<T>

Types can be nested arbitrarily: Optional<List<Integer>>Option<Vec<i32>>, List<String[]>Vec<Vec<String>>, etc.

§Options

The following optional key = "value" pairs may appear before the Java body, separated by commas:

  • javac = "<args>" — extra arguments for javac (shell-quoted).
  • java = "<args>" — extra arguments for java (shell-quoted).
use inline_java::java;

let result: String = java! {
    javac = "-cp ./my.jar",
    java  = "-cp ./my.jar",
    import com.example.MyClass;
    static String run() {
        return new MyClass().greet();
    }
}.unwrap();

§Cache directory

Compiled .class files are cached so that unchanged Java code is not recompiled on every run. The cache root is resolved in this order:

PriorityLocation
1INLINE_JAVA_CACHE_DIR environment variable (if set and non-empty)
2Platform cache directory — ~/.cache/inline_java on Linux, ~/Library/Caches/inline_java on macOS, %LOCALAPPDATA%\inline_java on Windows
3<system temp>/inline_java (fallback if the platform cache dir is unavailable)

Each compiled class gets its own subdirectory named <ClassName>_<hash>/, where the hash covers the Java source, the expanded javac flags, the current working directory, and the raw java flags. This means changing any of those inputs automatically triggers a fresh compilation.

§Using project Java source files

Use import or package directives together with javac = "-sourcepath <path>" (or -classpath) to call into your own Java code:

use inline_java::java;

// import style
let s: String = java! {
    javac = "-sourcepath .",
    import com.example.demo.*;
    static String run() {
        return new HelloWorld().greet();
    }
}.unwrap();

// package style — the generated class becomes part of the named package
let s: String = java! {
    javac = "-sourcepath .",
    package com.example.demo;
    static String run() {
        return new HelloWorld().greet();
    }
}.unwrap();

§Refactoring use case

inline_java is particularly well-suited for incremental Java → Rust migrations. The typical workflow is:

  1. Keep the original Java logic intact.
  2. Write the replacement in Rust.
  3. Use java_fn! to call the original Java with the same inputs and assert that both implementations produce identical outputs.
use inline_java::java_fn;

fn my_rust_impl(n: i32) -> i32 {
    // … new Rust code …
    n * 2
}

fn parity_with_java() {
    let java_impl = java_fn! {
        static int run(int n) {
            // original Java logic, verbatim
            return n * 2;
        }
    };

    for n in [0, 1, -1, 42, i32::MAX / 2] {
        let expected = java_impl(n).unwrap();
        assert_eq!(my_rust_impl(n), expected, "diverged for n={n}");
    }
}

parity_with_java();

§Crate layout

CratePurpose
inline_javaPublic API — re-exports macros and core types
inline_java_macrosProc-macro implementation (java!, java_fn!, ct_java!)
inline_java_coreRuntime helpers (run_java, JavaError)
inline_java_demoDemo binary

Macros§

ct_java
Re-export the proc macros so users only need to depend on this crate. Run Java at compile time and splice its return value as a Rust literal.
java
Re-export the proc macros so users only need to depend on this crate. Compile and run zero-argument Java code at program runtime.
java_fn
Re-export the proc macros so users only need to depend on this crate. Return a typed Rust function that compiles and runs Java at program runtime.

Enums§

JavaError
Re-export the core error type and runtime helpers. All errors that java! and java_fn! can return at runtime (and that ct_java! maps to compile_error! diagnostics at build time).

Functions§

expand_java_args
Re-export the core error type and runtime helpers. Shell-expand raw (expanding env vars and ~), then split into individual arguments (respecting quotes). Returns an empty vec if raw is empty.
run_java
Re-export the core error type and runtime helpers. Compile (if needed) and run a generated Java class, returning raw stdout bytes.