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
//! Convenience patterns like [strings](crate::pattern::string), [results](crate::pattern::result::Result) and [options](crate::pattern::option::Option).
//!
//! Patterns are optional types and constructs you can use. Most patterns are automatically applied
//! once you use their corresponding type.
//!
//! Backends which support a pattern will then generate _additional_ language-specific helpers
//! and bindings for it. In any case, regardless whether a pattern is supported by a backend or not,
//! fallback bindings will be available.
//!
//! ## Pattern Usage
//!
//! For example, instead of accepting a `*const u8` (or similar) and returning `0` on success
//!
//! ```
//! # use interoptopus::ffi;
//! #
//! #[ffi]
//! pub fn write_file(file: *const u8) -> i8 {
//! let file = unsafe { /* ... */ };
//! 0
//! }
//! ```
//!
//! you would instead accept an [`ffi::String`](crate::pattern::string::String) and return an [`ffi::Result`](crate::pattern::result::Result):
//!
//! ```
//! # use interoptopus::ffi;
//! #
//! # #[ffi]
//! # pub enum MyError {
//! # Bad
//! # }
//! #
//! #[ffi]
//! pub fn write_file(file: ffi::String) -> ffi::Result<(), MyError> {
//! let file = file.as_str();
//! ffi::Ok(())
//! }
//! ```
//! That way you won't have to write `unsafe` code, _and_ you get more idiomatic code in most
//! backends. For example, in C# you might end up with a simple `WriteFile("foo.txt")` call
//! that automatically converts the used `string` to UTF-8, and in turn converts a failed result
//! to a CLR exception.
//!
//! ## Services
//!
//! Services turn a Rust `impl` blocks into a class-like constructs in target languages (e.g., a
//! C# class with methods, constructor and `IDisposable`). To create a service:
//!
//! - Annotate the struct with `#[ffi(service)]`.
//! - Annotate the `impl` block with `#[ffi]`.
//! - Provide at least one constructor returning `ffi::Result<Self, E>`.
//!
//! ```rust
//! # use interoptopus::ffi;
//! #
//! # #[ffi]
//! # pub enum MyError { General }
//! #
//! #[ffi(service)]
//! pub struct Counter { count: u32 }
//!
//! #[ffi]
//! impl Counter {
//! pub fn create() -> ffi::Result<Self, MyError> {
//! ffi::Ok(Self { count: 0 })
//! }
//!
//! pub fn increment(&mut self) {
//! self.count += 1;
//! }
//! }
//! ```
//!
//! The `#[ffi]` macro on the `impl` block generates standalone FFI functions for each public
//! method, plus a destructor. A backend then groups these into a class. Methods marked with
//! `#[ffi::skip]` or non-`pub` methods are excluded.
//!
//! For async services, additionally derive [`AsyncRuntime`](crate::pattern::asynk::AsyncRuntime)
//! and include a runtime field (see the [`rt`](crate::rt) module).
//!
//! ## Backend Support
//!
//! Patterns are exclusively **designed _on top of_ existing, C-compatible functions and types**.
//! That means a backend will handle a pattern in one of three ways:
//!
//! - The pattern is **supported** and the backend will generate the raw, underlying type and / or
//! a language-specific abstraction that safely and conveniently handles it. Examples
//! include converting a [`String`](crate::pattern::string::String) to a C# `string`, or a
//! service to a Python `class`.
//!
//! - The pattern is not supported and will be **omitted, if the pattern was merely an aggregate** of
//! existing items. For example, a service in C will not be emitted as a class, but all its
//! constituent types and functions are still available as raw bindings.
//!
//! - The pattern is not supported and will be **replaced with a fallback type**. Examples include
//! the [`CStrPtr`](crate::pattern::cstr::CStrPtr) which will become a regular `*const char` in C.
//!
//! ## Composition
//!
//! Due to a lack of expressiveness in other languages, patterns usually compose without issues in Rust, but
//! not in all backends. For example, something like `ffi::Slice<ffi::Result<ffi::Option<ffi::String>, Error>>` is supported in
//! Rust without issues, but its UX might suffer in Python.