Skip to content

Commit 089770e

Browse files
committed
id functions in flat
1 parent 60ebd4d commit 089770e

5 files changed

Lines changed: 222 additions & 196 deletions

File tree

zenoh-flat/src/jni_converter.rs

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,22 @@ pub struct Builder {
6060
/// the raw pointer via `Arc::from_raw`) instead of borrowed. Used for
6161
/// close/undeclare-style functions that invalidate their handle.
6262
consume_args: HashMap<String, HashSet<String>>,
63+
/// Return-type wrappers keyed by the last-segment name of `T` in
64+
/// `ZResult<T>`. Applies when `T` is a plain (non-`Vec`) type.
65+
return_wrappers: HashMap<String, ReturnWrapper>,
66+
/// Return-type wrappers keyed by the element type name of `Vec<T>` in
67+
/// `ZResult<Vec<T>>`.
68+
return_wrappers_vec: HashMap<String, ReturnWrapper>,
69+
}
70+
71+
/// Describes how to render a `ZResult<T>` return value into a JNI-compatible
72+
/// output value. Registered via [`Builder::return_wrapper`] /
73+
/// [`Builder::return_wrapper_vec`].
74+
#[derive(Clone)]
75+
pub(crate) struct ReturnWrapper {
76+
jni_type: syn::Type,
77+
wrap_fn: syn::Path,
78+
default_expr: syn::Expr,
6379
}
6480

6581
impl Default for Builder {
@@ -78,6 +94,8 @@ impl Default for Builder {
7894
enum_decoders: HashMap::new(),
7995
callback_decoders: HashMap::new(),
8096
consume_args: HashMap::new(),
97+
return_wrappers: HashMap::new(),
98+
return_wrappers_vec: HashMap::new(),
8199
}
82100
}
83101
}
@@ -188,6 +206,37 @@ impl Builder {
188206
self
189207
}
190208

209+
/// Register a return-type wrapper for `ZResult<T>` where `T`'s
210+
/// last-segment name equals `type_name`. `jni_type` is the generated
211+
/// `extern "C"` return type. `wrap_fn` must have signature
212+
/// `fn(&mut JNIEnv, T) -> ZResult<jni_type>`. `default_expr` is the value
213+
/// returned on error (before the exception is thrown on the JVM side).
214+
pub fn return_wrapper(
215+
mut self,
216+
type_name: impl Into<String>,
217+
jni_type: impl AsRef<str>,
218+
wrap_fn: impl AsRef<str>,
219+
default_expr: impl AsRef<str>,
220+
) -> Self {
221+
self.return_wrappers
222+
.insert(type_name.into(), parse_return_wrapper(jni_type, wrap_fn, default_expr));
223+
self
224+
}
225+
226+
/// Like [`Builder::return_wrapper`] but applies when `T` is `Vec<E>`
227+
/// with `E`'s last-segment name equal to `element_type_name`.
228+
pub fn return_wrapper_vec(
229+
mut self,
230+
element_type_name: impl Into<String>,
231+
jni_type: impl AsRef<str>,
232+
wrap_fn: impl AsRef<str>,
233+
default_expr: impl AsRef<str>,
234+
) -> Self {
235+
self.return_wrappers_vec
236+
.insert(element_type_name.into(), parse_return_wrapper(jni_type, wrap_fn, default_expr));
237+
self
238+
}
239+
191240
/// Mark a specific argument of a source function as consuming: the
192241
/// generated wrapper will take ownership of the raw pointer via
193242
/// `Arc::from_raw` (dropping the Arc at end of scope), rather than
@@ -428,6 +477,37 @@ impl JniConverter {
428477
});
429478
call_args.push(quote! { #name });
430479
}
480+
ArgKind::OptionEncoding => {
481+
let decoder = self
482+
.cfg
483+
.encoding_decoder
484+
.as_ref()
485+
.expect("encoding_decoder not configured");
486+
let id_ident = format_ident!("{}_id", name);
487+
let schema_ident = format_ident!("{}_schema", name);
488+
jni_params.push(quote! { #id_ident: jni::sys::jint });
489+
jni_params.push(quote! { #schema_ident: jni::objects::JString });
490+
prelude.push(quote! {
491+
let #name = Some(#decoder(&mut env, #id_ident, &#schema_ident)?);
492+
});
493+
call_args.push(quote! { #name });
494+
}
495+
ArgKind::OptionString => {
496+
let decoder = self
497+
.cfg
498+
.string_decoder
499+
.as_ref()
500+
.expect("string_decoder not configured");
501+
jni_params.push(quote! { #name: jni::objects::JString });
502+
prelude.push(quote! {
503+
let #name = if !#name.is_null() {
504+
Some(#decoder(&mut env, &#name)?)
505+
} else {
506+
None
507+
};
508+
});
509+
call_args.push(quote! { #name });
510+
}
431511
ArgKind::Unsupported => panic!(
432512
"unsupported parameter type `{}` for `{}` at {loc}",
433513
ty.to_token_stream(),
@@ -453,6 +533,18 @@ impl JniConverter {
453533
quote! { () },
454534
quote! { #zresult<()> },
455535
)
536+
} else if let Some(wrapper) = self.lookup_return_wrapper(&inner) {
537+
let ReturnWrapper {
538+
jni_type,
539+
wrap_fn,
540+
default_expr,
541+
} = wrapper;
542+
(
543+
quote! { #jni_type },
544+
quote! { #wrap_fn(&mut env, __result) },
545+
quote! { #default_expr },
546+
quote! { #zresult<#jni_type> },
547+
)
456548
} else {
457549
(
458550
quote! { *const #inner },
@@ -497,6 +589,23 @@ impl JniConverter {
497589
syn::parse2(tokens).expect("generated JNI wrapper must parse")
498590
}
499591

592+
fn lookup_return_wrapper(&self, ty: &syn::Type) -> Option<ReturnWrapper> {
593+
let syn::Type::Path(tp) = ty else { return None };
594+
let seg = tp.path.segments.last()?;
595+
let name = seg.ident.to_string();
596+
if name == "Vec" {
597+
let syn::PathArguments::AngleBracketed(args) = &seg.arguments else {
598+
return None;
599+
};
600+
let syn::GenericArgument::Type(elem) = args.args.first()? else {
601+
return None;
602+
};
603+
let elem_name = type_last_segment(elem)?;
604+
return self.cfg.return_wrappers_vec.get(&elem_name).cloned();
605+
}
606+
self.cfg.return_wrappers.get(&name).cloned()
607+
}
608+
500609
fn classify_arg(&self, ty: &syn::Type) -> ArgKind {
501610
match ty {
502611
syn::Type::Reference(r) if r.mutability.is_none() => {
@@ -537,6 +646,16 @@ impl JniConverter {
537646
if name == "Option" && is_option_of_vec_u8(last) {
538647
return ArgKind::OptionVecU8;
539648
}
649+
if name == "Option" {
650+
if let Some(inner) = option_inner_type_name(last) {
651+
if inner == "String" {
652+
return ArgKind::OptionString;
653+
}
654+
if inner == "Encoding" {
655+
return ArgKind::OptionEncoding;
656+
}
657+
}
658+
}
540659
if name == "Vec" && is_vec_of_u8(last) {
541660
return ArgKind::VecU8;
542661
}
@@ -564,12 +683,31 @@ enum ArgKind {
564683
VecU8,
565684
/// `Encoding` → `(jint id, JString schema)` pair via `encoding_decoder`.
566685
Encoding,
686+
/// `Option<Encoding>` → `(jint id, JString schema)` pair via `encoding_decoder`,
687+
/// wrapped in `Some(_)`. Semantic gating on payload presence is the
688+
/// callee's responsibility.
689+
OptionEncoding,
690+
/// `Option<String>` → nullable `JString`.
691+
OptionString,
567692
/// `impl Fn(T) + Send + Sync + 'static` → `(JObject callback, JObject on_close)`
568693
/// pair decoded via a callback decoder registered for `T`.
569694
Callback(syn::Path),
570695
Unsupported,
571696
}
572697

698+
fn parse_return_wrapper(
699+
jni_type: impl AsRef<str>,
700+
wrap_fn: impl AsRef<str>,
701+
default_expr: impl AsRef<str>,
702+
) -> ReturnWrapper {
703+
ReturnWrapper {
704+
jni_type: syn::parse_str(jni_type.as_ref()).expect("invalid return wrapper jni_type"),
705+
wrap_fn: syn::parse_str(wrap_fn.as_ref()).expect("invalid return wrapper wrap_fn path"),
706+
default_expr: syn::parse_str(default_expr.as_ref())
707+
.expect("invalid return wrapper default_expr"),
708+
}
709+
}
710+
573711
fn type_last_segment(ty: &syn::Type) -> Option<String> {
574712
let syn::Type::Path(tp) = ty else { return None };
575713
tp.path.segments.last().map(|s| s.ident.to_string())
@@ -593,6 +731,18 @@ fn extract_fn_single_arg_type_name(
593731
None
594732
}
595733

734+
/// Return the last-segment name of the single generic argument of an
735+
/// `Option<...>` path segment, if any (e.g. `Option<String>` → `Some("String")`).
736+
fn option_inner_type_name(seg: &syn::PathSegment) -> Option<String> {
737+
let syn::PathArguments::AngleBracketed(args) = &seg.arguments else {
738+
return None;
739+
};
740+
let syn::GenericArgument::Type(inner) = args.args.first()? else {
741+
return None;
742+
};
743+
type_last_segment(inner)
744+
}
745+
596746
/// Check whether an `Option<...>` path segment wraps exactly `Vec<u8>`.
597747
fn is_option_of_vec_u8(seg: &syn::PathSegment) -> bool {
598748
let syn::PathArguments::AngleBracketed(args) = &seg.arguments else {

zenoh-flat/src/session.rs

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use zenoh::{
2323
query::{ConsolidationMode, Query, QueryTarget, Queryable, Querier, Reply, ReplyKeyExpr, Selector},
2424
sample::Sample,
2525
qos::{CongestionControl, Priority, Reliability},
26-
session::Session,
26+
session::{Session, ZenohId},
2727
Wait,
2828
};
2929

@@ -204,27 +204,31 @@ pub fn declare_queryable(
204204
///
205205
/// Each [`Reply`] received from the network is delivered to the `callback`.
206206
/// `selector_params` is appended to the `key_expr` to form the query selector
207-
/// (pass `String::new()` for no parameters). `payload` and `encoding` are
208-
/// coupled: if a payload is given, encoding is attached; if no payload is
209-
/// given, encoding is ignored.
207+
/// (pass `None` for no parameters). `payload` and `encoding` are coupled:
208+
/// if a payload is given, encoding is attached; if no payload is given,
209+
/// encoding is ignored.
210+
///
211+
/// Parameter order matches the JNI calling convention so the wrapper can be
212+
/// generated by `zenoh_flat::jni_converter` without reordering.
213+
#[prebindgen_proc_macro::prebindgen("jni")]
210214
pub fn get(
211215
session: &Session,
212216
key_expr: KeyExpr<'static>,
213-
selector_params: String,
217+
selector_params: Option<String>,
214218
callback: impl Fn(Reply) + Send + Sync + 'static,
219+
timeout: Duration,
215220
query_target: QueryTarget,
216221
consolidation: ConsolidationMode,
222+
attachment: Option<Vec<u8>>,
223+
payload: Option<Vec<u8>>,
224+
encoding: Option<Encoding>,
217225
congestion_control: CongestionControl,
218226
priority: Priority,
219227
express: bool,
220-
timeout: Duration,
221228
reply_key_expr: ReplyKeyExpr,
222-
payload: Option<Vec<u8>>,
223-
encoding: Option<Encoding>,
224-
attachment: Option<Vec<u8>>,
225229
) -> ZResult<()> {
226230
let key_expr_string = key_expr.to_string();
227-
let selector = Selector::owned(&key_expr, selector_params);
231+
let selector = Selector::owned(&key_expr, selector_params.unwrap_or_default());
228232
let mut get_builder = session
229233
.get(selector)
230234
.callback(callback)
@@ -329,6 +333,24 @@ pub fn delete(
329333
})
330334
}
331335

336+
/// Return the Zenoh ID of the session.
337+
#[prebindgen_proc_macro::prebindgen("jni")]
338+
pub fn get_zid(session: &Session) -> ZResult<ZenohId> {
339+
Ok(session.info().zid().wait())
340+
}
341+
342+
/// Return the Zenoh IDs of the peers connected to this session.
343+
#[prebindgen_proc_macro::prebindgen("jni")]
344+
pub fn get_peers_zid(session: &Session) -> ZResult<Vec<ZenohId>> {
345+
Ok(session.info().peers_zid().wait().collect())
346+
}
347+
348+
/// Return the Zenoh IDs of the routers connected to this session.
349+
#[prebindgen_proc_macro::prebindgen("jni")]
350+
pub fn get_routers_zid(session: &Session) -> ZResult<Vec<ZenohId>> {
351+
Ok(session.info().routers_zid().wait().collect())
352+
}
353+
332354
/// Close a Zenoh session using a reference to the session.
333355
#[prebindgen_proc_macro::prebindgen("jni")]
334356
pub fn close_session(session: &Session) -> ZResult<()> {

zenoh-jni/build.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,18 @@ fn main() {
2525
.callback_decoder("Reply", "crate::sample_callback::process_kotlin_reply_callback")
2626
.consume_arg("close_session", "session")
2727
.consume_arg("undeclare_key_expr", "key_expr")
28+
.return_wrapper(
29+
"ZenohId",
30+
"jni::sys::jbyteArray",
31+
"crate::zenoh_id::zenoh_id_to_byte_array",
32+
"jni::objects::JByteArray::default().as_raw()",
33+
)
34+
.return_wrapper_vec(
35+
"ZenohId",
36+
"jni::sys::jobject",
37+
"crate::zenoh_id::zenoh_ids_to_java_list",
38+
"jni::objects::JObject::default().as_raw()",
39+
)
2840
.build();
2941

3042
source

0 commit comments

Comments
 (0)