Skip to content

Commit

Permalink
enable mdbook testing in CI
Browse files Browse the repository at this point in the history
Currently all tests are ignored.
mdbook can't easily link to crates,
see rust-lang/mdBook#706 for details.
  • Loading branch information
nikomatsakis committed Jun 26, 2023
1 parent a6e7ac7 commit 94ba7f4
Show file tree
Hide file tree
Showing 11 changed files with 57 additions and 46 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,13 @@ jobs:
with:
distribution: 'corretto'
java-version: '17'
- name: Install mdBook
run: curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.4.28/mdbook-v0.4.28-x86_64-unknown-linux-gnu.tar.gz | tar -xz
- name: Build
run: cargo build --verbose
- name: Test crates
run: cargo test --all-targets --verbose
- name: Test client crates
run: cargo test --all-targets --verbose --manifest-path=test-crates/Cargo.toml
- name: Test book
run: mdbook test book
10 changes: 5 additions & 5 deletions book/src/call_java_from_rust.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public class Widget { /* ... */ }

Using duchess, we can declare a Rust version of this class with the `java_package!` macro:

```rust
```rust,ignore
duchess::java_package! {
// First, identify the package you are mirroring,
// and the visibility level that you want.
Expand All @@ -44,7 +44,7 @@ duchess::java_package! {

This module will expand to a module hierarchy matching the Java package name:

```rust
```rust,ignore
pub mod com {
pub mod widgard {
// One struct per Java class:
Expand Down Expand Up @@ -80,7 +80,7 @@ pub mod com {

Once you've created the Java package, you can create java objects and invoke their methods. This should mostly just work as you would expect, with one twist. Invoking a Java method doesn't immediately cause it to execute. Instead, like an iterator or an async function, it returns a `JvmOp`, which is like a suspended JVM operation that is *ready* to execute. To actually cause the method to execute, you call `execute`.

```rust
```rust,ignore
// We need to use `FactoryExt` to call methods on factory:
use com::widgard::{Factory, FactoryExt};
Expand All @@ -103,7 +103,7 @@ Note that to call methods on the JVM, we first had to start it. You do that via

Because jvm-ops are lazy, you can also chain them together:

```rust
```rust,ignore
use com::widgard::{Factory, FactoryExt};
let f = Factory::new().execute();
Expand All @@ -114,7 +114,7 @@ f.consume_widget(f.produce_widget()).execute();

In fact, using the `inspect` combinator, we can go further:

```rust
```rust,ignore
use com::widgard::{Factory, FactoryExt};
duchess::Jvm::with(|jvm| {
Expand Down
1 change: 1 addition & 0 deletions book/src/derive.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Deriving Java/Rust conversions
2 changes: 1 addition & 1 deletion book/src/implementing_native_methods.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public class ClassWithNativeMethod {

you can provide an implementation for `compute` like so:

```rust
```rust,ignore
// First, reflect the class, as described in the "calling Java from Rust" tutorial:
duchess::java_package! {
package me.ferris;
Expand Down
8 changes: 4 additions & 4 deletions book/src/internals.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ How the generated code works and why.

Java objects are represented by a dummy struct:

```rust
```rust,ignore
pub struct MyObject {
_dummy: ()
}
```

which implements the `JavaObject` trait:

```rust
```rust,ignore
unsafe impl JavaObject for MyObject { }
```

Expand Down Expand Up @@ -61,7 +61,7 @@ Covers various bits of rationale.

We do not want users to have to supply a context object on every method call, so instead we take the lifetime of the returned java reference and tie it to the inputs:

```rust
```rust,ignore
// from Java, and ignoring exceptions / null for clarity:
//
// class MyObject { ReturnType some_method(); }
Expand All @@ -81,7 +81,7 @@ We have a conflict:
* Either we make every method take a jdk pointer context.
* Or... we go into a suspended mode...

```rust
```rust,ignore
MyObject::new(x, y, z)
.execute(jdk);
Expand Down
4 changes: 2 additions & 2 deletions book/src/intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Duchess is a Rust crate that makes it easy, ergonomic, and efficient to interope

Duchess permits you to reflect Java classes into Rust and easily invoke methods on Java objects. For example the following Java code...

```rust
```rust,ignore
Logger logger = new log.Logger();
logger.addEvent(
Event.builder()
Expand All @@ -23,7 +23,7 @@ logger.addEvent(

...could be executed in Rust as follows:

```rust
```rust,ignore
let logger = log::Logger::new().global().execute()?;
logger
.add_event(
Expand Down
6 changes: 3 additions & 3 deletions book/src/java_package.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ This will generate a Rust module structure containing:

For the example above we would get

```rust
```rust,ignore
pub mod my {
pub mod package {
// References to java types branch to duchess; other references
Expand Down Expand Up @@ -126,7 +126,7 @@ class C2 { }

This allows the macro to generate combined Rust modules:

```rust
```rust,ignore
pub mod foo {
pub mod bar {
pub struct C1 { .. }
Expand Down Expand Up @@ -173,7 +173,7 @@ class C2 { /* you don't have to oxidize any further details */ }

When your classes reference other packages that are not currently being oxidixed, duchess will simply generate a reference to those classes. Its your responsibility to bring them into scope.

```rust
```rust,ignore
// Bring `q` into scope from somewhere else
use some_rust_crate::q;
Expand Down
2 changes: 1 addition & 1 deletion book/src/jvm.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Multiple threads can invoke `Jvm::with`, but only one underlying JVM can ever be

When you start the JVM from your Rust code, you can set various options by using the jvm builder:

```rust
```rust,ignore
Jvm::builder()
.add_classpath("foo")
.add_classpath("bar")
Expand Down
8 changes: 4 additions & 4 deletions book/src/linking_native_functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Using the [`#[java_function]`](./java_function.md) decorator you can write Rust

If your Rust program is launching the JVM, then you can configure that JVM to link to your native method definitions through methods on the JVM builder.

```rust
```rust,ignore
use duchess::prelude::*; // 👈 You'll need this.
#[java_function(...)]
Expand All @@ -27,7 +27,7 @@ Invoking the link method for every java functon you wish to implement is tedious

To avoid this, you can create **suites** of java functions. The idea is that the `link` method accepts both individual `JavaFunction` structs but also `Vec<JavaFunction>` suites. You can then write a function in your module that returns a `Vec<JavaFunction>` with all the java functions defined locally:

```rust
```rust,ignore
use duchess::prelude::*;
#[java_function(...)]
Expand All @@ -46,7 +46,7 @@ fn java_functions() -> Vec<JavaFunction> {

You can also compose suites from other crates or modules:

```rust
```rust,ignore
fn java_functions() -> Vec<duchess::JavaFunction> {
crate_a::java_functions()
.into_iter()
Expand All @@ -57,7 +57,7 @@ fn java_functions() -> Vec<duchess::JavaFunction> {

And finally you can invoke `link()` to link them all at once:

```rust
```rust,ignore
fn main() -> duchess::GlobalResult<()> {
Jvm::builder()
.link(java_functions())
Expand Down
34 changes: 17 additions & 17 deletions book/src/methods.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

When you use duchess, you invoke methods via nice syntax like

```rust
```rust,ignore
java_list.get(0).to_string().execute()
// ^^^^^^^^^
// This is the method we are discussing here
Expand All @@ -15,7 +15,7 @@ How does this actually work (and why)?
*Part* of our setup is that define relatively ordinary looking inherent methods
on the type that defines the method, e.g.:

```rust
```rust,ignore
impl Object {
fn to_string(&self) -> impl JavaMethod<String> { /* tbd */ }
}
Expand Down Expand Up @@ -118,7 +118,7 @@ In the following sections, we are going to walk through each part of the solutio

The first step is to create a "fully qualified" notation for each Java method:

```rust
```rust,ignore
impl Object {
fn to_string(
this: impl IntoJava<Object>
Expand All @@ -139,15 +139,15 @@ where `View<J>` has the same data as `J` but defines inherent methods.
We'll create a trait `FromRef` to use for this pattern,
where `View<J>: FromRef<J>` indicates that a view `&View<J>` can be constructed from a `&J` reference:

```rust
```rust,ignore
pub trait FromRef<J> {
fn from_ref(t: &J) -> &Self;
}
```

A view struct is just a newtype on the underlying `J` type but with `#[repr(transparent)]`:

```rust
```rust,ignore
#[repr(transparent)]
pub struct View<J> {
this: J,
Expand All @@ -158,7 +158,7 @@ The `#[repr(transparent)]` attribute ensures that `J` and `View<J>` have the sam
and are treated equivalently in ABIs and the like.
Thanks to this, we can implement `FromRef` like so:

```rust
```rust,ignore
impl FromRef<J> for View<J> {
fn from_ref(t: &J) -> &Self {
// Safe because of the `#[repr(transparent)]` attribute
Expand All @@ -177,7 +177,7 @@ The *method resolution order* for a type `T` is an ordered list of its transitiv

For each class `X`, we define a *`ViewAsObj` struct* `ViewAsXObj<J, N>`:

```rust
```rust,ignore
#[repr(transparent)]
struct ViewAsXObj<J, N> {
this: J,
Expand All @@ -194,7 +194,7 @@ The class has two type parameters:

Each ViewAsObj struct includes a Deref that derefs to N:

```rust
```rust,ignore
impl<J, N> Deref for ViewAsXObj<J, N> {
type Target = N;
Expand All @@ -206,7 +206,7 @@ impl<J, N> Deref for ViewAsXObj<J, N> {

So given `interface Foo extends Bar, Baz`, the type `Foo` would deref to

```rust
```rust,ignore
ViewAsFooObj<Foo, ViewAsBarObj<Foo, ViewAsBazObj<Foo, ()>>>
// --- ----------- ----------------------------------
// X J N
Expand All @@ -220,7 +220,7 @@ and so forth.

Each op struct implements a trait `FromRef<J>`:

```rust
```rust,ignore
trait FromRef<J> {
fn from_ref(r: &J) -> &Self;
}
Expand All @@ -230,7 +230,7 @@ The `from_ref` method allows constructing an op struct from an `&J` reference.
Implementing this method requires a small bit of unsafe code,
leveraging the `repr(transparent)` attribute on each op struct:

```rust
```rust,ignore
impl<J> FromRef<J> for ObjectOp<J>
where
J: IntoJava<Foo>,
Expand All @@ -249,7 +249,7 @@ also has inherent methods for each Java method.
These are implemented by invoking the [fully qualified inherent functions]().
For example, the ViewAsObj struct for `Object` includes a `to_string` method like so:

```rust
```rust,ignore
impl<J, N> ViewAsObjectObj<J, N>
where
J: Upcast<Object>,
Expand All @@ -267,7 +267,7 @@ So we create them inside of a `const _: () = { .. }` block.
But we do need *some* way to name them.
We expose them via associated types of the `JavaView` trait:

```rust
```rust,ignore
trait JavaView {
type OfObj<J>: FromRef<J>;
type OfObjWith<J, N>: FromRef<J>
Expand All @@ -279,7 +279,7 @@ trait JavaView {
The `OfObj` associated type in particular provides the "default value" for `N` that defines the MRO.
The `OfObjWith` is used to supply an explicit `N` value. For example:

```rust
```rust,ignore
const _: () = {
struct ViewAsFooObj<J, C> { ... }
Expand All @@ -299,7 +299,7 @@ The `ViewAsObj` structs allow you to invoke methods on a java object reference l
But they do not allow you to invoke methods on some random [`JvmOp`] that happens to *return* a string.
For that, we create a very similar set of `ViewAsOp` structs:

```rust
```rust,ignore
#[repr(transparent)]
struct ViewAsXOp<J, N> {
this: J,
Expand All @@ -313,7 +313,7 @@ But the signature is slightly different; it is a `&self` method, but the `impl J
Instead, it copies the `self.this` out.
This relies on the fact that all [`JvmOp`] values are `Copy`.

```rust
```rust,ignore
impl<J, N> ViewAsObjectObj<J, N>
where
J: IntoJava<Object>,
Expand All @@ -336,7 +336,7 @@ The reason is that the `ViewAsObjectObj` traits are the output from `Deref` impl

We also have to add a `Deref` impl to each of the op structs.

```rust
```rust,ignore
struct SomeOp { }
impl JvmOp for SomeOp {
Expand Down
24 changes: 15 additions & 9 deletions book/src/to_java.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,28 +19,34 @@ e.g., `vec.to_java::<java::util::List<_>>()`.
The Rust `String` type converts to the Java string type.
One could compute the Java `hashCode` for a string as follows:

```rust
```rust,ignore
use duchess::prelude::*;
use duchess::java;
let data = format!("Hello, Duchess!");
let hashCode: i32 =
data.to_java() // Returns a `JvmOp` producing a `java::lang::String`
.hashCode() // Returns a `JvmOp` invoking `hashCode` on this string
.execute(); // Execute the jvmop
let hash_code: i32 =
data.to_java::<java::lang::String>() // Returns a `JvmOp` producing a `java::lang::String`
.hash_code() // Returns a `JvmOp` invoking `hashCode` on this string
.execute()?; // Execute the jvmop
```

### `Global<java::lang::String>`

Converting a Rust reference to a Java object, such as a `Global` reference, is an identity operation.

```rust
```rust,ignore
use duchess::prelude::*;
use duchess::java;
// Produce a Global reference from a Rust string
let data: Global<java::lang::String> =
format!("Hello, Duchess!").global().execute();
format!("Hello, Duchess!").global().execute()?;
// Invoke `to_java` on the `Global` reference
let hashCode: i32 =
data.to_java() // Returns a `JvmOp` producing a `java::lang::String`
data.to_java::<java::lang::String>() // Returns a `JvmOp` producing a `java::lang::String`
.hashCode() // Returns a `JvmOp` invoking `hashCode` on this string
.execute(); // Execute the jvmop
.execute()?; // Execute the jvmop
```

## Deriving `ToJava` for your own types
Expand Down

0 comments on commit 94ba7f4

Please sign in to comment.