Expand description
OCaml-gen
This crate provides automatic generation of OCaml bindings. Refer to the rustdoc for more information.
Example
Here’s an example of generating some bindings. Create a main.rs
as:
use ocaml_gen::prelude::*;
// Some Rust code you have:
#[derive(ocaml::FromValue, ocaml::IntoValue, ocaml_gen::CustomType)]
pub struct SomeType {
pub a: u8,
}
#[ocaml_gen::func]
#[ocaml::func]
fn create_some_type() -> SomeType {
SomeType { a: 42 }
}
fn main() {
// initialize your environment
let env = &mut Env::default();
// choose where you want to write the bindings
let w = &mut std::io::stdout();
// you can declare modules
decl_module!(w, env, "Types", {
// and types
decl_type!(w, env, SomeType => "t");
});
decl_module!(w, env, "Functions", {
// and functions
decl_func!(w, env, create_some_type => "create");
});
}
Note that the underlying function imported by decl_func!
is actually caml_of_numeral_to_ocaml
, which is created by the annotated macro ocaml_gen::func
.
So either your function is in scope, or you import everything (e.g. use path::*
), or you import the derived function directly (e.g. use path::caml_of_numeral_to_ocaml
).
The OCaml bindings generated should look like this:
module Types = struct
type nonrec t
end
module Functions = struct
val create : unit -> Types.t
end
Usage
In general, you can use this library by following these steps:
- annotate your types and functions with
ocaml-rs
andocaml_gen
macros. - create a
main.rs
file to generate your binding filebindings.ml
as shown above. - use the
ocaml-rs
crate to export the static library that OCaml will use. - add a
dune
file to your crate to build the Rust static library as well as the OCaml bindings. (protip: use(mode promote)
in the dune file to have the resulting binding file live within the folder.) - optionally enforce in CI that the promoted binding file is correct.
You can see an example of these steps in the test/ folder. (Although we don’t “promote” the file with dune for testing purposes.)
Annotations
To allow ocaml-gen to understand how to generate OCaml bindings from your types and functions, you must annotate them using ocaml-gen’s macros.
To allow generation of bindings on structs, use ocaml_gen::Struct
:
#[ocaml_gen::Struct]
struct MyType {
// ...
}
To allow generation of bindings on enums, use ocaml_gen::Enum
:
#[ocaml_gen::Enum]
enum MyType {
// ...
}
To allow generation of bindings on functions, use ocaml_gen::func
:
#[ocaml_gen::func]
#[ocaml::func] // if you use the crate ocaml-rs' macro, it must appear after
pub fn your_function(arg1: String) {
//...
}
To allow generation of bindings on custom types, use ocaml_gen::CustomType
:
#[ocaml_gen::CustomType]
struct MyCustomType {
// ...
}
Binding generations
To generate bindings, you must create a main.rs
file that uses the ocaml_gen
crate functions to layout what the bindings .ml
file will look like.
The first thing to do is to import the types and functions you want to generate bindings for, as well as the ocaml_gen
macros:
use ocaml_gen::prelude::*;
use your_crate::*;
You can then use decl_module!
to declare modules:
let env = &mut Env::default();
let w = &mut std::io::stdout();
decl_module!(w, env, "T1", {
decl_module!(w, env, "T2", {
decl_type!(w, env, SomeType);
});
});
decl_module!(w, env, "T3", {
decl_type!(w, env, SomeOtherType);
});
You can rename types and functions by simply adding an arrow:
decl_type!(w, env, SomeType => "t");
You can also declare generic types by first declaring the generic type parameters (that you must reuse for all generic types):
decl_fake_generic!(T1, 0); // danger:
decl_fake_generic!(T2, 1); // make sure you
decl_fake_generic!(T3, 2); // increment these correctly
decl_type!(w, env, TypeWithOneGenericType<T1>);
decl_type!(w, env, ThisOneHasTwo<T1, T2>);
decl_type!(w, env, ThisOneHasThreeOfThem<T1, T2, T3>);
You can also create type aliases with the decl_type_alias!
macro but it is highly experimental.
It has a number of issues:
- the alias doesn’t work outside of the module it is declared current scope (which is usually what you want)
- the alias is ignoring the instantiation of type parameters. This means that it might rename
Thing<usize>
tot1
, eventhought1
was an alias toThing<String>
(this is the main danger, see this tracking issue) - it won’t work (binding generation will throw an error) if you try to alias two instantiations of the same generic type (for example,
t1
is the alias ofThing<usize>
andt2
is the alias ofThing<String>
)
Modules
- Implementations of
crate::OCamlDesc
for types that have natural equivalents in OCaml. The OCaml description should be the corresponding data types in OCaml This should correspond to the mapping defined in the ocaml-rs book:<https://github.com/zshipko/ocaml-rs/blob/v1.0.0-beta.4/doc/src/02_type_conversion.md>
FIXME: Unsigned types like uint16, uint32 and uint64 are not implemented as OCaml does not provide types in the Stdlib for it. A custom block should be used. Using Stdint could be a solution. - To use the library, you can simply import the prelude as in:
Macros
- Creates a fake generic. This is a necessary hack, at the moment, to declare types (with the
decl_type
macro) that have generic parameters. - Declares the binding for a given function
- Creates a module
- Declares the binding for a given type
- Declares a new OCaml type that is made of other types
Structs
- The environment at some point in time during the declaration of OCaml bindings. It ensures that types cannot be declared twice, and that types that are renamed and/or relocated into module are referenced correctly.
Traits
OCamlBinding
is the trait implemented by types to generate their OCaml bindings. It is usually derived automatically via the Struct macro, or theCustomType
macro for custom types. For functions, refer to the func macro.OCamlDesc
is the trait implemented by types to facilitate generation of their OCaml bindings. It is usually derived automatically via the Struct macro, or theCustomType
macro for custom types.
Attribute Macros
- A macro to create OCaml bindings for a function that uses
#[ocaml::func]
Derive Macros
- Derives implementations for
OCamlDesc
andOCamlBinding
on a custom type For example: - The Enum derive macro. It generates implementations of
ToOCaml
andOCamlBinding
on an enum type. The type must implementocaml::IntoValue
andocaml::FromValue
For example: - The Struct derive macro. It generates implementations of
ToOCaml
andOCamlBinding
on a struct. The type must implementocaml::IntoValue
andocaml::FromValue