1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
//! `robusta_jni` is a library that provides a procedural macro to make easier to write JNI-compatible code in Rust.
//!
//! It can perform automatic conversion of Rust-y input and output types.
//!
//! ```toml
//! [dependencies]
//! robusta_jni = "0.1"
//! ```
//!
//! # Getting started
//! The [`#[bridge]`](bridge) attribute is `robusta_jni`'s entry point. It must be applied to a module.
//! `robusta_jni` will then generate proper function definitions and trait implementations depending on declared methods.
//!
//! # Declaring classes
//! Rust counterparts of Java classes are declared as Rust `struct`s, with a `#[package(my.package.name)]` attribute.
//! When using the default package, just omit the package name inside parentheses.
//!
//! Structs without the package attribute will be ignored by `robusta_jni`.
//!
//! In order to use the features of `robusta_jni`, declared structs should also implement the [`Signature`] trait.
//! This can be done manually or with autoderive.
//!
//! Example:
//! ```rust
//! use robusta_jni::bridge;
//! use robusta_jni::convert::Signature;
//!
//! #[bridge]
//! mod jni {
//!     # use robusta_jni::convert::Signature;
//!     #[package()] // default package
//!     struct A;
//!
//!     impl Signature for A {
//!         const SIG_TYPE: &'static str = "LA;";
//!     }
//!
//!     #[derive(Signature)]
//!     #[package(my.awesome.package)]
//!     struct B;
//! }
//! ```
//!
//! # Adding native methods
//! JNI bindings are generated for every method implemented for `package`-annotated structs.
//! Each method can optionally specify a `#[call_type]` attribute that will determine how conversions between Rust and Java types are performed.
//! For more information about conversions and `#[call_type]`, check out the [convert](convert) module.
//!
//! In general, **all input and output types must implement proper conversion traits**
//! (input types must implement `(Try)FromJavaValue` and output types must implement `(Try)IntoJavaValue`)
//!
//! Native methods can optionally accept a [`JNIEnv`] parameter as first parameter (after `self` if present).
//!
//! Methods are declared as standard Rust functions with public visibility and "jni" ABI, and are matched by name with Java methods.
//! No special handling is needed.
//!
//! Example:
//!
//! ```rust
//! # use robusta_jni::bridge;
//! #
//! # #[bridge]
//! # mod jni {
//!     # use robusta_jni::convert::{Signature, TryFromJavaValue, JavaValue};
//!     # use robusta_jni::jni::JNIEnv;
//!     # use jni::objects::JObject;
//!     # #[derive(Signature)]
//!     # #[package()]
//!     # struct A;
//!     #
//!     # impl<'env: 'borrow, 'borrow> TryFromJavaValue<'env, 'borrow> for A {
//!     #    type Source = JObject<'env>;
//!     #
//!     #    fn try_from(s: Self::Source,env: &'borrow JNIEnv<'env>) -> ::robusta_jni::jni::errors::Result<Self> {
//!     #         Ok(A)
//!     #     }
//!     # }
//!     #
//! impl A {
//!     pub extern "jni" fn op(self, _env: &JNIEnv, flag: bool) -> i32 {
//!         //                       ^^^^^ optional
//!         if flag {
//!             1
//!         } else {
//!             0
//!         }
//!     }
//!
//!     // here the `env` parameter is omitted
//!     pub extern "jni" fn special(mut input1: Vec<i32>, input2: i32) -> Vec<String> {
//!         input1.push(input2);
//!         input1.iter().map(ToString::to_string).collect()
//!     }
//!
//! }
//! # }
//! ```
//!
//! # Adding Java methods
//! You can also declare Java methods and `robusta` will generate binding glue to convert types and call methods on the Java side.
//! Again, **all input and output types must implement proper conversion traits**: in this case it's the reverse from the Java to Rust case
//! (input types must implement `(Try)IntoJavaValue` and output types must implement `(Try)FromJavaValue`).
//!
//! Methods are declared as standard Rust functions with public visibility, a "java" ABI and an empty body, and are matched by name with Java methods.
//! Both static and non-static methods must accept a [`JNIEnv`] parameter as first parameter (after self if present).
//!
//! Constructors can be declared via a `#[constructor]` attribute on static methods, and are matched by their type signature.
//!
//! When using `#[call_type(safe)]` or omitting `call_type` attribute, the output type **must** be [`jni::errors::Result<T>`](jni::errors::Result)
//! with `T` being the actual method return type. Otherwise when using `#[call_type(unchecked)]` `T` is sufficient.
//!
//! **When using `#[call_type(unchecked)]` if a Java exception is thrown while calling a method a panic is raised.**
//!
//! ## Static methods
//!
//! Example:
//! ```rust
//! # use robusta_jni::bridge;
//! # use robusta_jni::convert::{Signature, TryFromJavaValue};
//! #
//! # #[bridge]
//! # mod jni {
//!     # use robusta_jni::convert::{Signature, TryFromJavaValue, JavaValue};
//!     # use robusta_jni::jni::JNIEnv;
//!     # use jni::objects::JObject;
//!     # #[derive(Signature)]
//!     # #[package()]
//!     # struct A;
//!     #
//!     # impl<'env: 'borrow, 'borrow> TryFromJavaValue<'env, 'borrow> for A {
//!     #    type Source = JObject<'env>;
//!     #
//!     #    fn try_from(s: Self::Source,env: &'borrow JNIEnv<'env>) -> ::robusta_jni::jni::errors::Result<Self> {
//!     #         Ok(A)
//!     #     }
//!     # }
//!     #
//! impl A {
//!     pub extern "java" fn staticJavaMethod(
//!         env: &JNIEnv,
//!         i: i32,
//!         u: i32,
//!     ) -> ::robusta_jni::jni::errors::Result<i32> {}
//! }
//! # }
//! ```
//!
//! ## Non-static methods
//!
//! Example:
//! ```rust
//! # use robusta_jni::bridge;
//! # use robusta_jni::convert::{Signature, TryFromJavaValue};
//! #
//! # #[bridge]
//! # mod jni {
//!     # use robusta_jni::convert::{Signature, TryFromJavaValue, JavaValue, TryIntoJavaValue};
//!     # use robusta_jni::jni::JNIEnv;
//!     # use jni::objects::JObject;
//!     # #[derive(Signature)]
//!     # #[package()]
//!     # struct A;
//!     #
//!     # impl<'env: 'borrow, 'borrow> TryFromJavaValue<'env, 'borrow> for A {
//!     #    type Source = JObject<'env>;
//!     #
//!     #    fn try_from(s: Self::Source,env: &'borrow JNIEnv<'env>) -> ::robusta_jni::jni::errors::Result<Self> {
//!     #         Ok(A)
//!     #     }
//!     # }
//!     #
//!     # impl<'env> TryIntoJavaValue<'env> for &A {
//!     #   type Target = JObject<'env>;
//!     #
//!     #   fn try_into(self, env: &JNIEnv<'env>) -> ::robusta_jni::jni::errors::Result<Self::Target> {
//!     #         env.new_object("A", "()V", &[])
//!     #   }
//!     # }
//!     #
//! impl A {
//!     pub extern "java" fn selfMethod(
//!         &self,
//!         env: &JNIEnv,
//!         i: i32,
//!         u: i32,
//!     ) -> ::robusta_jni::jni::errors::Result<i32> {}
//! }
//! # }
//! ```
//!
//! ## Constructors
//!
//! Example:
//! ```rust
//! # use robusta_jni::bridge;
//! # use robusta_jni::convert::{Signature, TryFromJavaValue};
//! #
//! # #[bridge]
//! # mod jni {
//!     # use robusta_jni::convert::{Signature, TryFromJavaValue, JavaValue};
//!     # use robusta_jni::jni::JNIEnv;
//!     # use jni::objects::JObject;
//!     # #[derive(Signature)]
//!     # #[package()]
//!     # struct A;
//!     #
//!     # impl<'env: 'borrow, 'borrow> TryFromJavaValue<'env, 'borrow> for A {
//!     #    type Source = JObject<'env>;
//!     #
//!     #    fn try_from(s: Self::Source,env: &'borrow JNIEnv<'env>) -> ::robusta_jni::jni::errors::Result<Self> {
//!     #         Ok(A)
//!     #     }
//!     # }
//!     #
//! impl A {
//!     #[constructor]  //   vvv ---- this method name can be anything because it's a constructor
//!     pub extern "java" fn new(
//!         env: &JNIEnv
//!     ) -> ::robusta_jni::jni::errors::Result<Self> {}
//! }
//! # }
//! ```
//!
//! # Conversion details and special lifetimes
//! The procedural macro handles two special lifetimes specially: `'env` and `'borrow`.
//!
//! When declaring structs with lifetimes you may be asked to name one of the lifetimes as `'env` in order to
//! disambiguate code generation for the attribute macro.
//! In the generated code, this lifetime would correspond to the one used to convert your type to `*IntoJavaValue`, like:
//! ```ignore
//! <A<'env> as TryIntoJavaValue<'env>>
//! ```
//! This lifetime is always used as the lifetime parameter of `JNIEnv` instances.
//!
//! When using `*FromJavaValue` derive macros your structs will be required to have both `'env` and `'borrow`,
//! with the same bounds as in the trait definition. For more information, see the relevant traits documentation.
//!
//! ## Library-provided conversions
//!
//! | **Rust**                                                                           | **Java**                          |
//! |------------------------------------------------------------------------------------|-----------------------------------|
//! | i32                                                                                | int                               |
//! | bool                                                                               | boolean                           |
//! | char                                                                               | char                              |
//! | i8                                                                                 | byte                              |
//! | f32                                                                                | float                             |
//! | f64                                                                                | double                            |
//! | i64                                                                                | long                              |
//! | i16                                                                                | short                             |
//! | Vec\<T\>†                                                                          | ArrayList\<T\>                    |
//! | [jni::JObject<'env>](jni::objects::JObject)                                      ‡ | *(any Java object as input type)* |
//! | [jni::jobject](jni::sys::jobject)                                                    | *(any Java object as output)*     |
//!
//! † Type parameter `T` must implement proper conversion types
//!
//! ‡ The special `'env` lifetime **must** be used
//!
//! ## Limitations
//!
//! Currently there are some limitations in the conversion mechanism:
//!  * Boxed types are supported only through the opaque `JObject`/`jobject` types
//!  * Automatic type conversion is limited to the table outlined above, though easily extendable if needed.
//!
//! [`Signature`]: convert::Signature
//! [`JNIEnv`]: jni::JNIEnv
//!

pub use robusta_codegen::bridge;

pub mod convert;

pub use jni;

pub use static_assertions::assert_type_eq_all;