1pub mod error;
23
24use crate::error::EngineError;
25use jni::{InitArgsBuilder, JNIVersion, JavaVM};
26use std::fs;
27use std::io::Write;
28use std::ops::Deref;
29use std::path::Path;
30use std::pin::Pin;
31use tempfile::TempDir;
32
33pub struct JvmEngine {
34 jvm: Pin<Box<JavaVM>>,
35 #[allow(unused)]
37 deps: TempDir,
38}
39
40struct JarDependency<'a> {
41 contents: &'a [u8],
42 name: &'a str,
43}
44
45pub struct JvmEngineBuilder<'a> {
46 deps: Vec<JarDependency<'a>>,
47 check_jni: bool,
48 verbose_jni: bool,
49}
50
51impl<'a> JvmEngineBuilder<'a> {
52 pub fn new() -> Self {
54 Self {
55 deps: Vec::default(),
56 verbose_jni: false,
57 check_jni: false,
58 }
59 }
60
61 pub fn add_dependency(mut self, contents: &'a [u8], name: &'a str) -> Self {
64 self.deps.push(JarDependency { contents, name });
65 self
66 }
67
68 pub fn check_jni(mut self) -> Self {
70 self.check_jni = true;
71 self
72 }
73
74 pub fn verbose_jni(mut self) -> Self {
76 self.verbose_jni = true;
77 self
78 }
79
80 pub fn try_init(self) -> Result<JvmEngine, EngineError> {
87 JvmEngine::try_from_builder(self)
88 }
89}
90
91impl JvmEngine {
92 fn try_from_builder(builder: JvmEngineBuilder) -> Result<Self, EngineError> {
93 let tempdir = TempDir::new()?;
94 let classpath = builder
95 .deps
96 .into_iter()
97 .map(|dep| Self::write_jar(dep.contents, dep.name, tempdir.path()))
98 .collect::<Result<Vec<_>, EngineError>>()?;
99
100 let mut args = InitArgsBuilder::new().version(JNIVersion::V8);
101
102 if !classpath.is_empty() {
103 args = args.option(format!("-Djava.class.path={}", classpath.join(":")));
104 }
105
106 if builder.check_jni {
107 args = args.option("-Xcheck:jni");
108 }
109
110 if builder.verbose_jni {
111 args = args.option("-verbose:jni");
112 }
113
114 let jvm = JavaVM::new(args.build()?)?;
115 Ok(Self {
116 jvm: Box::pin(jvm),
117 deps: tempdir,
118 })
119 }
120
121 fn write_jar(bytes: &[u8], name: &str, dir: &Path) -> Result<String, EngineError> {
122 let path = dir.join(name);
123 let mut kernel = fs::File::create(&path)?;
124 kernel.write_all(bytes)?;
125
126 Ok(path.to_string_lossy().to_string())
127 }
128}
129
130impl<'a> Default for JvmEngineBuilder<'a> {
131 fn default() -> Self {
132 Self::new()
133 }
134}
135
136impl Deref for JvmEngine {
137 type Target = JavaVM;
138
139 fn deref(&self) -> &Self::Target {
140 &self.jvm
141 }
142}