diff --git a/.gitignore b/.gitignore index f51d6b8..9e79245 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,3 @@ -toad-jni.so - # macOS .DS_Store diff --git a/build.sbt b/build.sbt index a44aa33..5afc79b 100644 --- a/build.sbt +++ b/build.sbt @@ -5,7 +5,7 @@ import sbt.nio.file.FileTreeView Global / onChangedBuildSource := ReloadOnSourceChanges val scala3Version = "3.2.2" -val cargoBuild = taskKey[Unit]("cd ./toad-java-glue-rs; cargo build") +val cargoBuild = taskKey[Unit]("cd ./glue; cargo build") val ejectHeaders = taskKey[Unit]("Generate C headers for FFI") val fullBuild = taskKey[Unit]("cargoBuild > ejectHeaders") val glob = settingKey[Map[String, Glob]]("globs") @@ -14,7 +14,7 @@ val path = settingKey[Map[String, String]]("paths") fork := true javaOptions += "--enable-preview" -javacOptions ++= Seq("--enable-preview", "--release", "20", "-Xlint:preview") +javacOptions ++= Seq("--enable-preview", "--release", "20") lazy val root = project .in(file(".")) @@ -25,11 +25,11 @@ lazy val root = project libraryDependencies += "org.scalameta" %% "munit" % "0.7.29" % Test, glob := Map( "java.sources" -> baseDirectory.value.toGlob / "src" / "main" / "java" / ** / "*.java", - "glue.sources" -> baseDirectory.value.toGlob / "toad-java-glue-rs" / "src" / ** / "*.rs" + "glue.sources" -> baseDirectory.value.toGlob / "glue" / "src" / ** / "*.rs" ), path := Map( - "glue.base" -> (baseDirectory.value / "toad-java-glue-rs").toString, - "glue.target" -> (baseDirectory.value / "toad-java-glue-rs" / "target" / "debug").toString, + "glue.base" -> (baseDirectory.value / "glue").toString, + "glue.target" -> (baseDirectory.value / "target" / "glue" / "debug").toString, "java.classTarget" -> (baseDirectory.value / "target" / "scala-3.2.2" / "classes").toString ), ejectHeaders := { @@ -52,7 +52,7 @@ lazy val root = project }, cargoBuild := { val cmd = - Seq("sh", "-c", "cd toad-java-glue-rs; cargo rustc -- -Awarnings") + Seq("sh", "-c", "cd glue; cargo rustc -- -Awarnings") println(cmd !!) }, fullBuild := { diff --git a/glue/.cargo/config.toml b/glue/.cargo/config.toml new file mode 100644 index 0000000..28b4d1f --- /dev/null +++ b/glue/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +target-dir = "../target/native" diff --git a/toad-java-glue-rs/Cargo.lock b/glue/Cargo.lock similarity index 98% rename from toad-java-glue-rs/Cargo.lock rename to glue/Cargo.lock index a0aa473..97dd692 100644 --- a/toad-java-glue-rs/Cargo.lock +++ b/glue/Cargo.lock @@ -505,6 +505,8 @@ checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" [[package]] name = "toad" version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6e3e3216e542ea3b2da0d2200e498b016907b68acb288b47a487cb9fd681b42" dependencies = [ "embedded-time", "log", @@ -576,7 +578,9 @@ dependencies = [ [[package]] name = "toad-jni" -version = "0.1.0" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8211cccf8f1dce2768a85b6f42b1d232542ecb616584bc9915da1cfc50c3d3d1" dependencies = [ "jni", "toad-array 0.5.0", diff --git a/toad-java-glue-rs/Cargo.toml b/glue/Cargo.toml similarity index 71% rename from toad-java-glue-rs/Cargo.toml rename to glue/Cargo.toml index 4e82b82..82f5a9c 100644 --- a/toad-java-glue-rs/Cargo.toml +++ b/glue/Cargo.toml @@ -9,7 +9,7 @@ crate_type = ["cdylib"] [dependencies] jni = "*" -toad = {path = "../../toad/toad"} -toad-jni = {path = "../../toad/toad-jni"} -toad-msg = "*" +toad = "0.17.2" +toad-jni = "0.4.0" +toad-msg = "0.18.1" tinyvec = {version = "1.5", default_features = false, features = ["rustc_1_55"]} diff --git a/toad-java-glue-rs/README.md b/glue/README.md similarity index 100% rename from toad-java-glue-rs/README.md rename to glue/README.md diff --git a/toad-java-glue-rs/rust-toolchain.toml b/glue/rust-toolchain.toml similarity index 100% rename from toad-java-glue-rs/rust-toolchain.toml rename to glue/rust-toolchain.toml diff --git a/toad-java-glue-rs/rustfmt.toml b/glue/rustfmt.toml similarity index 100% rename from toad-java-glue-rs/rustfmt.toml rename to glue/rustfmt.toml diff --git a/toad-java-glue-rs/src/lib.rs b/glue/src/lib.rs similarity index 81% rename from toad-java-glue-rs/src/lib.rs rename to glue/src/lib.rs index 1d509ec..8050291 100644 --- a/toad-java-glue-rs/src/lib.rs +++ b/glue/src/lib.rs @@ -18,6 +18,7 @@ macro_rules! package { (ext $start:ident.$($thing:ident).+) => {concat!(stringify!($start), $("/", stringify!($thing)),+)}; } +pub mod uint; pub mod message_code; pub mod message_opt_ref; pub mod message_opt_value_ref; @@ -39,7 +40,10 @@ pub unsafe extern "system" fn Java_dev_toad_Runtime_init<'local>(mut env: JNIEnv #[cfg(test)] mod tests { use jni::{InitArgsBuilder, JavaVM}; + use toad::retry::Strategy; + use toad::time::Millis; + use crate::retry_strategy::RetryStrategy; use crate::runtime_config::RuntimeConfig; #[test] @@ -64,5 +68,13 @@ mod tests { let r = RuntimeConfig::new(e); assert_eq!(r.to_toad(e), Default::default()); + + let r = Strategy::Exponential { init_min: Millis::new(0), + init_max: Millis::new(100) }; + assert_eq!(RetryStrategy::from_toad(e, r).to_toad(e), r); + + let r = Strategy::Delay { min: Millis::new(0), + max: Millis::new(100) }; + assert_eq!(RetryStrategy::from_toad(e, r).to_toad(e), r); } } diff --git a/toad-java-glue-rs/src/message_code.rs b/glue/src/message_code.rs similarity index 100% rename from toad-java-glue-rs/src/message_code.rs rename to glue/src/message_code.rs diff --git a/toad-java-glue-rs/src/message_opt_ref.rs b/glue/src/message_opt_ref.rs similarity index 100% rename from toad-java-glue-rs/src/message_opt_ref.rs rename to glue/src/message_opt_ref.rs diff --git a/toad-java-glue-rs/src/message_opt_value_ref.rs b/glue/src/message_opt_value_ref.rs similarity index 100% rename from toad-java-glue-rs/src/message_opt_value_ref.rs rename to glue/src/message_opt_value_ref.rs diff --git a/toad-java-glue-rs/src/message_ref.rs b/glue/src/message_ref.rs similarity index 100% rename from toad-java-glue-rs/src/message_ref.rs rename to glue/src/message_ref.rs diff --git a/toad-java-glue-rs/src/message_type.rs b/glue/src/message_type.rs similarity index 100% rename from toad-java-glue-rs/src/message_type.rs rename to glue/src/message_type.rs diff --git a/glue/src/retry_strategy.rs b/glue/src/retry_strategy.rs new file mode 100644 index 0000000..393ae4c --- /dev/null +++ b/glue/src/retry_strategy.rs @@ -0,0 +1,87 @@ +use jni::objects::{GlobalRef, JObject}; +use jni::JNIEnv; +use toad::retry::Strategy; +use toad::time::Millis; +use toad_jni::cls::java; +use toad_jni::convert::Object; +use toad_jni::Sig; + +pub struct RetryStrategy(GlobalRef); + +impl RetryStrategy { + pub const PATH: &'static str = package!(dev.toad.RetryStrategy); + + pub const EXPONENTIAL: &'static str = package!(dev.toad.RetryStrategyExponential); + pub const EXPONENTIAL_CTOR: Sig = Sig::new().arg(Sig::class(java::time::Duration::PATH)) + .arg(Sig::class(java::time::Duration::PATH)) + .returning(Sig::VOID); + + pub const DELAY: &'static str = package!(dev.toad.RetryStrategyDelay); + pub const DELAY_CTOR: Sig = Sig::new().arg(Sig::class(java::time::Duration::PATH)) + .arg(Sig::class(java::time::Duration::PATH)) + .returning(Sig::VOID); + + pub fn exp<'a>(&self, e: &mut JNIEnv<'a>) -> Self { + let o = e.new_object(Self::PATH, Sig::new().returning(Sig::VOID), &[]) + .unwrap(); + let g = e.new_global_ref(o).unwrap(); + Self(g) + } + + pub fn millis_field<'a>(&self, e: &mut JNIEnv<'a>, key: &str) -> Millis { + let o = e.get_field(&self.0, key, Sig::class(java::time::Duration::PATH)) + .unwrap() + .l() + .unwrap(); + let g = e.new_global_ref(o).unwrap(); + let d = java::time::Duration::from_java(g); + Millis::new(d.to_millis(e) as u64) + } + + pub fn to_toad<'a>(self, e: &mut JNIEnv<'a>) -> Strategy { + if e.is_instance_of(&self.0, Self::EXPONENTIAL).unwrap() { + Strategy::Exponential { init_min: self.millis_field(e, "initMin"), + init_max: self.millis_field(e, "initMax") } + } else { + Strategy::Delay { min: self.millis_field(e, "min"), + max: self.millis_field(e, "max") } + } + } + + pub fn from_toad<'a>(e: &mut JNIEnv<'a>, s: Strategy) -> Self { + let g = match s { + | Strategy::Delay { min, max } => { + let (min, max) = (java::time::Duration::of_millis(e, min.0 as i64), + java::time::Duration::of_millis(e, max.0 as i64)); + let (min, max) = (min.to_java(), max.to_java()); + let o = e.new_object(Self::DELAY, + Self::DELAY_CTOR, + &[min.as_obj().into(), max.as_obj().into()]) + .unwrap(); + e.new_global_ref(o).unwrap() + }, + | Strategy::Exponential { init_min, init_max } => { + let (init_min, init_max) = (java::time::Duration::of_millis(e, init_min.0 as i64), + java::time::Duration::of_millis(e, init_max.0 as i64)); + let (init_min, init_max) = (init_min.to_java(), init_max.to_java()); + let o = e.new_object(Self::EXPONENTIAL, + Self::EXPONENTIAL_CTOR, + &[init_min.as_obj().into(), init_max.as_obj().into()]) + .unwrap(); + e.new_global_ref(o).unwrap() + }, + }; + + Self(g) + } +} + +impl Object for RetryStrategy { + fn from_java(jobj: GlobalRef) -> Self { + Self(jobj) + } + + fn to_java(self) -> GlobalRef { + self.0 + } +} diff --git a/toad-java-glue-rs/src/runtime_config.rs b/glue/src/runtime_config.rs similarity index 100% rename from toad-java-glue-rs/src/runtime_config.rs rename to glue/src/runtime_config.rs diff --git a/glue/src/uint.rs b/glue/src/uint.rs new file mode 100644 index 0000000..c56040a --- /dev/null +++ b/glue/src/uint.rs @@ -0,0 +1,58 @@ +use jni::{JNIEnv, objects::{JObject, JByteArray}}; +use toad_jni::Sig; + +pub mod path { + pub const U64: &'static str = package!(dev.toad.ffi.u64); + pub const U32: &'static str = package!(dev.toad.ffi.u32); + pub const U16: &'static str = package!(dev.toad.ffi.u16); + pub const U8: &'static str = package!(dev.toad.ffi.u8); +} + +pub fn u64<'a>(e: &mut JNIEnv<'a>, o: JObject<'a>) -> u64 { + let bi = e.call_method(o, "bigintValue", Sig::new().returning(Sig::class("java.math.BigInteger")), &[]).unwrap().l().unwrap(); + let barr: JByteArray<'a> = e.call_method(bi, "toByteArray", Sig::new().returning(Sig::array_of(Sig::BYTE)), &[]).unwrap().l().unwrap().try_into().unwrap(); + + let mut bytes = [0i8; 8]; + + // BigInteger is a growable two's complement integer + // + // the "growable" comes from its backing structure being a simple + // int array `int[]`, where bytes are added as needed to afford capacity. + // + // two's-complement means the most significant bit (the first bit of the first byte) + // indicates the sign of the integer, where 0 is positive and 1 is negative. + // + // The rest of the bits are unchanged, meaning the range is from `-(2^(n - 1))` + // to `2^(n - 1) - 1`. + // + // For example, a two's complement i8 would be able to represent `-128` (`0b11111111`), + // `0` (`0b00000000`) to `127` (`0b01111111`). for positive integers, the representation is + // the same as unsigned integers, meaning we simply need to make sure we don't accidentally + // interpret the first bit as part of the integer. + // + // Here we assume whoever is responsible for BigInteger made sure that it's positive, + // so converting the big-endian two's complement int + e.get_byte_array_region(&barr, 0, &mut bytes).unwrap(); + + // https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/math/BigInteger.html#toByteArray() + // + // BigInt.toByteArray actually returns the raw byte representation of the integer, NOT + // two's complement `byte`s as the type signature would lead you to believe. + // + // To interpret these bytes as i8 is incorrect. + let bytes = bytes.map(|i| i8::to_be_bytes(i)[0]); + + u64::from_be_bytes(bytes) +} + +pub fn u32<'a>(e: &mut JNIEnv<'a>, o: JObject<'a>) -> u32 { + e.call_method(o, "longValue", Sig::new().returning(Sig::LONG), &[]).unwrap().j().unwrap() as u32 +} + +pub fn u16<'a>(e: &mut JNIEnv<'a>, o: JObject<'a>) -> u16 { + e.call_method(o, "intValue", Sig::new().returning(Sig::INT), &[]).unwrap().i().unwrap() as u16 +} + +pub fn u8<'a>(e: &mut JNIEnv<'a>, o: JObject<'a>) -> u8 { + e.call_method(o, "shortValue", Sig::new().returning(Sig::SHORT), &[]).unwrap().s().unwrap() as u8 +} diff --git a/src/main/java/dev.toad/RetryStrategy.java b/src/main/java/dev.toad/RetryStrategy.java index bb73265..03437ff 100644 --- a/src/main/java/dev.toad/RetryStrategy.java +++ b/src/main/java/dev.toad/RetryStrategy.java @@ -1,53 +1,3 @@ package dev.toad; -import java.time.Duration; -import java.util.Optional; - -public abstract sealed class RetryStrategy { - - @Override - public boolean equals(Object other) { - return switch (this) { - case Exponential self -> switch (other) { - case Exponential e -> e.initMin == self.initMin && - e.initMax == self.initMax; - default -> false; - }; - case Linear self -> switch (other) { - case Linear e -> e.min == self.min && e.max == self.max; - default -> false; - }; - default -> false; - }; - } - - public final class Exponential extends RetryStrategy { - - public final Duration initMin; - public final Duration initMax; - - private static native Exponential fromRust(byte[] mem); - - private native byte[] toRust(); - - public Exponential(Duration initMin, Duration initMax) { - this.initMin = initMin; - this.initMax = initMax; - } - } - - public final class Linear extends RetryStrategy { - - public final Duration min; - public final Duration max; - - private static native Linear fromRust(byte[] mem); - - private native byte[] toRust(); - - public Linear(Duration min, Duration max) { - this.min = min; - this.max = max; - } - } -} +public abstract sealed class RetryStrategy permits RetryStrategyDelay, RetryStrategyExponential { } diff --git a/src/main/java/dev.toad/RetryStrategyDelay.java b/src/main/java/dev.toad/RetryStrategyDelay.java new file mode 100644 index 0000000..ef2087a --- /dev/null +++ b/src/main/java/dev.toad/RetryStrategyDelay.java @@ -0,0 +1,27 @@ +package dev.toad; + +import java.time.Duration; +import java.util.Optional; + +public final class RetryStrategyDelay extends RetryStrategy { + + public final Duration min; + public final Duration max; + + private static native RetryStrategyDelay fromRust(byte[] mem); + + private native byte[] toRust(); + + public RetryStrategyDelay(Duration min, Duration max) { + this.min = min; + this.max = max; + } + + @Override + public boolean equals(Object other) { + return switch (other) { + case RetryStrategyDelay e -> e.min == this.min && e.max == this.max; + default -> false; + }; + } +} diff --git a/src/main/java/dev.toad/RetryStrategyExponential.java b/src/main/java/dev.toad/RetryStrategyExponential.java new file mode 100644 index 0000000..7b979e3 --- /dev/null +++ b/src/main/java/dev.toad/RetryStrategyExponential.java @@ -0,0 +1,27 @@ +package dev.toad; + +import java.time.Duration; +import java.util.Optional; + + public final class RetryStrategyExponential extends RetryStrategy { + + public final Duration initMin; + public final Duration initMax; + + private static native RetryStrategyExponential fromRust(byte[] mem); + + private native byte[] toRust(); + + public RetryStrategyExponential(Duration initMin, Duration initMax) { + this.initMin = initMin; + this.initMax = initMax; + } + + @Override + public boolean equals(Object other) { + return switch (other) { + case RetryStrategyExponential e -> e.initMin == this.initMin && e.initMax == this.initMax; + default -> false; + }; + } + } diff --git a/src/main/java/dev.toad/RuntimeOptions.java b/src/main/java/dev.toad/RuntimeOptions.java index 3f1da9f..c40f63f 100644 --- a/src/main/java/dev.toad/RuntimeOptions.java +++ b/src/main/java/dev.toad/RuntimeOptions.java @@ -1,5 +1,6 @@ package dev.toad; +import dev.toad.ffi.*; import java.time.Duration; import java.util.Optional; import java.util.function.Function; @@ -44,13 +45,13 @@ public final class RuntimeOptions implements Cloneable { public final class Net implements Cloneable { - private short port; - private short concurrency; + private u16 port; + private u8 concurrency; private Msg msg; public Net() { - this.port = 5683; - this.concurrency = 1; + this.port = new u16(5683); + this.concurrency = new u8((short)1); this.msg = new Msg(); } @@ -77,12 +78,12 @@ public final class RuntimeOptions implements Cloneable { return self; } - public short port() { - return this.port; + public int port() { + return this.port.intValue(); } public short concurrency() { - return this.concurrency; + return this.concurrency.shortValue(); } public Msg msg() { @@ -91,14 +92,14 @@ public final class RuntimeOptions implements Cloneable { public Net withPort(short port) { return this.with(self -> { - self.port = port; + self.port = new u16(port); return self; }); } public Net withConcurrency(short conc) { return this.with(self -> { - self.concurrency = conc; + self.concurrency = new u8(conc); return self; }); } @@ -113,8 +114,8 @@ public final class RuntimeOptions implements Cloneable { public final class Msg implements Cloneable { - private Optional tokenSeed = Optional.empty(); - private Optional probingRateBytesPerSecond = Optional.empty(); + private Optional tokenSeed = Optional.empty(); + private Optional probingRateBytesPerSecond = Optional.empty(); private Optional multicastResponseLeisure = Optional.empty(); private Con con; private Non non; @@ -152,11 +153,11 @@ public final class RuntimeOptions implements Cloneable { } public Optional tokenSeed() { - return this.tokenSeed; + return this.tokenSeed.map(u16 -> u16.intValue()); } public Optional probingRateBytesPerSecond() { - return this.probingRateBytesPerSecond; + return this.probingRateBytesPerSecond.map(u16 -> u16.intValue()); } public Optional multicastResponseLeisure() { @@ -173,14 +174,14 @@ public final class RuntimeOptions implements Cloneable { public Msg withTokenSeed(int tokenSeed) { return this.with(self -> { - self.tokenSeed = Optional.of(tokenSeed); + self.tokenSeed = Optional.of(new u16(tokenSeed)); return self; }); } public Msg withProbingRateBytesBerSecond(int bps) { return this.with(m -> { - m.probingRateBytesPerSecond = Optional.of(bps); + m.probingRateBytesPerSecond = Optional.of(new u16(bps)); return m; }); } @@ -210,7 +211,7 @@ public final class RuntimeOptions implements Cloneable { private Optional ackedRetryStrategy = Optional.empty(); private Optional unackedRetryStrategy = Optional.empty(); - private Optional maxAttempts = Optional.empty(); + private Optional maxAttempts = Optional.empty(); public Con() {} @@ -237,7 +238,7 @@ public final class RuntimeOptions implements Cloneable { } public Optional maxAttempts() { - return this.maxAttempts; + return this.maxAttempts.map(u16 -> u16.intValue()); } public Con withAckedRetryStrategy(RetryStrategy r) { @@ -256,7 +257,7 @@ public final class RuntimeOptions implements Cloneable { public Con withMaxAttempts(int a) { return this.with(s -> { - s.maxAttempts = Optional.of(a); + s.maxAttempts = Optional.of(new u16(a)); return s; }); } @@ -270,7 +271,7 @@ public final class RuntimeOptions implements Cloneable { public final class Non implements Cloneable { private Optional retryStrategy = Optional.empty(); - private Optional maxAttempts = Optional.empty(); + private Optional maxAttempts = Optional.empty(); public Non() {} @@ -292,7 +293,7 @@ public final class RuntimeOptions implements Cloneable { } public Optional maxAttempts() { - return this.maxAttempts; + return this.maxAttempts.map(u16 -> u16.intValue()); } public Non withRetryStrategy(RetryStrategy r) { @@ -304,7 +305,7 @@ public final class RuntimeOptions implements Cloneable { public Non withMaxAttempts(int a) { return this.with(s -> { - s.maxAttempts = Optional.of(a); + s.maxAttempts = Optional.of(new u16(a)); return s; }); } diff --git a/src/main/java/dev.toad/ffi/u16.java b/src/main/java/dev.toad/ffi/u16.java new file mode 100644 index 0000000..f0c4c19 --- /dev/null +++ b/src/main/java/dev.toad/ffi/u16.java @@ -0,0 +1,15 @@ +package dev.toad.ffi; + +public final class u16 { + public static final int MAX = (int)(Math.pow(2, 16) - 1); + private final int l; + + public u16(int l) { + uint.assertWithinRange(this.MAX, l); + this.l = l; + } + + public int intValue() { + return this.l; + } +} diff --git a/src/main/java/dev.toad/ffi/u32.java b/src/main/java/dev.toad/ffi/u32.java new file mode 100644 index 0000000..1f9b70d --- /dev/null +++ b/src/main/java/dev.toad/ffi/u32.java @@ -0,0 +1,15 @@ +package dev.toad.ffi; + +public final class u32 { + public static final long MAX = (long)(Math.pow(2, 32) - 1); + private final long l; + + public u32(long l) { + uint.assertWithinRange(this.MAX, l); + this.l = l; + } + + public long longValue() { + return this.l; + } +} diff --git a/src/main/java/dev.toad/ffi/u64.java b/src/main/java/dev.toad/ffi/u64.java new file mode 100644 index 0000000..050c3d5 --- /dev/null +++ b/src/main/java/dev.toad/ffi/u64.java @@ -0,0 +1,17 @@ +package dev.toad.ffi; + +public final class u64 { + public static final double MAX = Math.pow(2, 64) - 1; + private final double l; + + public u64(double l) { + uint.assertWithinRange(this.MAX, l); + uint.assertNatural(l); + + this.l = l; + } + + public double doubleValue() { + return this.l; + } +} diff --git a/src/main/java/dev.toad/ffi/u8.java b/src/main/java/dev.toad/ffi/u8.java new file mode 100644 index 0000000..c7649bb --- /dev/null +++ b/src/main/java/dev.toad/ffi/u8.java @@ -0,0 +1,15 @@ +package dev.toad.ffi; + +public final class u8 { + public static final short MAX = (short)(Math.pow(2, 8) - 1); + private final short l; + + public u8(short l) { + uint.assertWithinRange(this.MAX, l); + this.l = l; + } + + public short shortValue() { + return this.l; + } +} diff --git a/src/main/java/dev.toad/ffi/uint.java b/src/main/java/dev.toad/ffi/uint.java new file mode 100644 index 0000000..eb63dc7 --- /dev/null +++ b/src/main/java/dev.toad/ffi/uint.java @@ -0,0 +1,15 @@ +package dev.toad.ffi; + +public class uint { + public static void assertWithinRange(double max, double n) { + if (n < 0 || n > max) { + throw new IllegalArgumentException(String.format("% must be between 0 and %", n, max)); + } + } + + public static void assertNatural(double n) { + if (n % 1 > 0.0) { + throw new IllegalArgumentException(String.format("% must be a whole integer", n)); + } + } +} diff --git a/toad-java-glue-rs/src/retry_strategy.rs b/toad-java-glue-rs/src/retry_strategy.rs deleted file mode 100644 index 05ec932..0000000 --- a/toad-java-glue-rs/src/retry_strategy.rs +++ /dev/null @@ -1,52 +0,0 @@ -use jni::objects::{GlobalRef, JObject}; -use jni::JNIEnv; -use toad::retry::Strategy; -use toad::time::Millis; -use toad_jni::cls::java; -use toad_jni::convert::Object; -use toad_jni::Sig; - -pub struct RetryStrategy(GlobalRef); - -impl RetryStrategy { - pub const PATH: &'static str = package!(dev.toad.RetryStrategy); - pub const EXPONENTIAL: &'static str = package!(dev.toad.RetryStrategy.Exponential); - pub const LINEAR: &'static str = package!(dev.toad.RetryStrategy.Linear); - - pub fn exp<'a>(&self, e: &mut JNIEnv<'a>) -> Self { - let o = e.new_object(Self::PATH, Sig::new().returning(Sig::VOID), &[]) - .unwrap(); - let g = e.new_global_ref(o).unwrap(); - Self(g) - } - - pub fn millis_field<'a>(&self, e: &mut JNIEnv<'a>, key: &str) -> Millis { - let o = e.get_field(&self.0, "initMax", Sig::class(java::time::Duration::PATH)) - .unwrap() - .l() - .unwrap(); - let g = e.new_global_ref(o).unwrap(); - let d = java::time::Duration::from_java(g); - Millis::new(d.to_millis(e) as u64) - } - - pub fn to_toad<'a>(self, e: &mut JNIEnv<'a>) -> Strategy { - if e.is_instance_of(&self.0, Self::EXPONENTIAL).unwrap() { - Strategy::Exponential { init_min: self.millis_field(e, "initMin"), - init_max: self.millis_field(e, "initMax") } - } else { - Strategy::Delay { min: self.millis_field(e, "min"), - max: self.millis_field(e, "max") } - } - } -} - -impl Object for RetryStrategy { - fn from_java(jobj: GlobalRef) -> Self { - Self(jobj) - } - - fn to_java(self) -> GlobalRef { - self.0 - } -}