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
//! Definition of accepted types and their behavior.
//!
//! Here is a list of the available complex types and their FFI equivalents.
//!
//! If a type deriving the ffi shim contains references to types that are not listed below, they
//! will by default be considered `Foreign` types. It will be assumed that `Foreign` types
//! derive the ffi shim themselves, and will be manipulated as such. See the `Foreign` behavior for
//! more details.
//!
//! |Complex types|FFI equivalent|
//! |---|---|
//! |`String`|`*mut u8`|
//! |`Option<T>`|`*mut T`|
//! |`Vec<T>`|`ffishim::library::FFIVec<T>`|
//! |`Result<T, E>`|`ffishim::library::FFIResult<T>`|
//! |`chrono::Duration`|`libc::c_long`|
//!
//! And below a list of all the scalar types handled (see the `Scalars` behavior for more details.)
//!
//! |Scalar types|FFI equivalent|
//! |---|---|
//! |`bool`|`libc::c_char`|
//! |`char`|`libc::c_uint`|
//! |`f32`|`libc::c_float`|
//! |`f64`|`libc::c_double`|
//! |`u8`|`libc::c_char`|
//! |`u16`|`libc::c_ushort`|
//! |`u32`|`libc::c_uint`|
//! |`u64`|`libc::c_ulong`|
//! |`usize`|`libc::size_t`|
//! |`i8`|`libc::c_schar`|
//! |`i16`|`libc::c_short`|
//! |`i32`|`libc::c_int`|
//! |`i64`|`libc::c_long`|
//! |`isize`|`libc::ssize_t`|
//!
//! If you want to add a type behavior, just:
//!
//!  - Create a new struct in a sub-module
//!  - Implement the `Behavior` trait
//!  - Add the struct in the private `BEHAVIORS` vector

use ::once_cell::sync::Lazy;

/// The behavior of a `Type` as needed to generate the ffi shim.
pub trait Behavior: Sync + Send {
    /// Returns `true` if this behavior is fit to handle the given `sty`.
    fn is(&self, sty: &::syn::Type) -> bool;

    /// Transforms the given `Type` into its shim equivalent. For example:
    ///  - `String` -> `*mut libc::c_char`
    ///  - `u32` -> `libc::c_uint`
    ///  - ...
    fn fold(&self, sty: ::syn::Type) -> ::syn::Type;

    /// Returns an `Expr`ession that mutates the shim type into the original one.
    fn try_into(&self, sty: &::syn::Type, name: ::syn::Expr) -> ::syn::Expr;

    /// Returns an `Expr`ession that mutates the original type into its shim equivalent.
    fn from(&self, sty: &::syn::Type, name: ::syn::Expr) -> ::syn::Expr;

    /// Returns an `Expr`ession that frees an expression of the given type.
    fn free(&self, sty: &::syn::Type, name: ::syn::Expr) -> ::std::option::Option<::syn::Expr>;
}

/// Switch over a given `Type` and return the associated `Behavior`.
///
/// This is an open-ended, c-style switch: if two different type behaviors' `is` method return
/// `true`, the first one in the list will win. You can order the type behaviors in the
/// `BEHAVIORS` vector.
pub fn switch<'a, 'b>(sty: &'a ::syn::Type) -> &'b Box<dyn Behavior> {
    // TODO: give more context about which type we do not find the behavior of
    BEHAVIORS.iter().find(|tyb| tyb.is(sty)).expect("cannot find behavior for given type")
}

/// List of available, instantiated `Behavior`s.
static BEHAVIORS: Lazy<Vec<Box<dyn Behavior>>> = Lazy::new(|| {
    vec![
        // End-types
        Box::new(Scalars),
        Box::new(Bool),
        Box::new(Char),
        Box::new(Duration),
        Box::new(String),

        // Parameterized types
        Box::new(Option),
        Box::new(Result),
        Box::new(Vector),
        Box::new(Reference),

        // Foreign/custom types implementing an ffi shim
        Box::new(Foreign),
    ]
});

// End-types
mod scalars;
pub use scalars::Behavior as Scalars;
mod bool;
pub use self::bool::Behavior as Bool;
mod char;
pub use self::char::Behavior as Char;
mod duration;
pub use duration::Behavior as Duration;
mod string;
pub use string::Behavior as String;

// Parameterized types
mod option;
pub use option::Behavior as Option;
mod result;
pub use result::Behavior as Result;
mod vec;
pub use vec::Behavior as Vector;
mod reference;
pub use reference::Behavior as Reference;

// Foreign/custom types implementing an ffi shim
mod foreign;
pub use foreign::Behavior as Foreign;