Dushistov / flapigen-rs

Tool for connecting programs or libraries written in Rust with other languages
BSD 3-Clause "New" or "Revised" License
775 stars 59 forks source link

Add support for recursive types in struct #451

Open CinchBlue opened 1 year ago

CinchBlue commented 1 year ago

If you have a lib.rs and a java_glue.rs.in, this will not work:

// lib.rs
mod java_glue;

pub enum RecursiveEnum {
    Null,
    S
}

#[derive(Debug, Clone, Default)]
pub struct Lol {
    pub s: String,
}

#[derive(Debug, Clone, Default)]
pub struct RecursiveFoo {
    pub s: String,
    pub child: Vec<RecursiveFoo>,
}

impl RecursiveFoo {
    pub fn new(s: String, child: Vec<RecursiveFoo>) -> Self {
        Self { s, child }
    }
}
// java_glue.rs.in
use crate::*;
use jni_sys::*;
foreign_class!(class Lol {
    self_type Lol;
    constructor  Lol::default() -> Lol;
});

foreign_class!(class RecursiveFoo {
    self_type RecursiveFoo;
    constructor  RecursiveFoo::new(s: String, child: Vec<RecursiveFoo>) -> RecursiveFoo;
});

You will get an error like:

error: failed to run custom build command for `lol2_lib_android2 v0.1.0 (/Users/austin/work/android.debug/my_repo/lol/lib_android2)`

Caused by:
  process didn't exit successfully: `/Users/austin/work/android.debug/lol/target/release/build/lol_lib_android2-e0e77b6c90dc0964/build-script-build` (exit status: 101)
  --- stderr
  error in android bindings: src/java_glue.rs.in
  parsing of android bindings: src/java_glue.rs.in failed
  error: Do not know conversion from Java type to such rust type 'Vec < RecursiveFoo >'
      constructor  RecursiveFoo::new(s: String, child: Vec<RecursiveFoo>) -> RecursiveFoo;
                                                       ^^^^^^^^^^^^^^^^^

  At android bindings: src/java_glue.rs.in:10:53
  thread 'main' panicked at 'explicit panic', /Users/austin/.cargo/registry/src/index.crates.io-6f17d22bba15001f/flapigen-0.6.0/src/error.rs:88:5
  note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
error: Recipe `build-android-lib2` failed with exit code 101

but this type is acceptable to Rust if you compile without the build script.

CinchBlue commented 1 year ago

it also does not work if you use Option<Box<RecursiveFoo>> instead:

// lib.rs
mod java_glue;

pub enum RecursiveEnum {
    Null,
    S
}

#[derive(Debug, Clone, Default)]
pub struct Lol {
    pub s: String,
}

#[derive(Debug, Clone, Default)]
pub struct RecursiveFoo {
    pub s: String,
    pub child: Option<Box<RecursiveFoo>>,
}

impl RecursiveFoo {
    pub fn new(s: String, child: Option<Box<RecursiveFoo>>) -> Self {
        Self { s, child }
    }
}
// java_glue.rs.in
use crate::*;
use jni_sys::*;
foreign_class!(class Lol {
    self_type Lol;
    constructor  Lol::default() -> Lol;
});

foreign_class!(class RecursiveFoo {
    self_type RecursiveFoo;
    constructor  RecursiveFoo::new(s: String, child: Option<Box<RecursiveFoo>>,) -> RecursiveFoo;
});
error: failed to run custom build command for `lol_lib_android2 v0.1.0 (/Users/austin/work/android.debug/my_repo/lol/lib_android2)`

Caused by:
  process didn't exit successfully: `/Users/austin/work/android.debug/my_repo/target/release/build/lol_lib_android2-e0e77b6c90dc0964/build-script-build` (exit status: 101)
  --- stdout
  cargo:warning=mapping types: type Box < RecursiveFoo > unknown
  cargo:warning=mapping types: type Box < RecursiveFoo > unknown

  --- stderr
  error in android bindings: src/java_glue.rs.in
  parsing of android bindings: src/java_glue.rs.in failed
  error: Do not know conversion from Java type to such rust type 'Option < Box < RecursiveFoo > >'
      constructor  RecursiveFoo::new(s: String, child: Option<Box<RecursiveFoo>>,) -> RecursiveFoo;
                                                       ^^^^^^^^^^^^^^^^^^^^^^^^^

  At android bindings: src/java_glue.rs.in:10:53
  thread 'main' panicked at 'explicit panic', /Users/austin/.cargo/registry/src/index.crates.io-6f17d22bba15001f/flapigen-0.6.0/src/error.rs:88:5
  note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
error: Recipe `build-android-lib2` failed with exit code 101
CinchBlue commented 1 year ago

I see that there is some sort of generic type map within the library -- but I don't have insight into what is happening. How are these type conversions being caught, and why do other Vec<T> work?

Dushistov commented 1 year ago

How are these type conversions being caught, and why do other Vec work?

Other works, because of usage of generic mapping T <-> Java is possible only after parsing of foreign_class!, so inside foreign_class! it is possible only use &self type and equivalent &T type.

CinchBlue commented 1 year ago

This isn't theoretically impossible right? Is there some way I can help implement this? What is needed?