Option and Result

This chapter will cover the EzffiOption and EzffiResult wrappers. They're the C-types for the already-known Rust-types Option<R> and Result<T, E>, and are included in the ezffi/ezffi.h header.

ezffi ships with macros that allow the user to extract T from an Option and a Result. Don't be stupid: check if the Result is Ok and the Option is Some before unwrapping. Their definitions are:

#define EZFFI_OPTION_UNWRAP(Type, opt_ptr) ({ Type __ez_v; ezffi_option_unwrap((opt_ptr), &__ez_v); __ez_v; })
#define EZFFI_RESULT_UNWRAP(Type, res_ptr) ({ Type __ez_v; ezffi_result_unwrap((res_ptr), &__ez_v); __ez_v; })

and their usage is:

T value = EZFFI_OPTION_UNWRAP(T, opt_ptr);
T value = EZFFI_RESULT_UNWRAP(T, res_ptr);

I also provide the following functions:

// Helpers that allow the user to easily check if the wrappers
// contain any value.
bool ezffi_option_is_some(const struct EzffiOption *o);
bool ezffi_result_is_ok(const struct EzffiResult *o);

// In the case of `Result` I have this function to extract the code.
int32_t ezffi_result_error_code(const struct EzffiResult *o);

// You can also get a pointer to the error message. NULL if there
// is no error. This lives as long as EzffiResult lives.
const char *ezffi_result_error_message(const struct EzffiResult *o);

// These functions can be used to extract the generic type, but I
// recommend the macros, they're easier to read and use these
// functions internally.
// 
// This functions also free the structs allocated data, so you don't need
// to call the free function after using them or the macros
void ezffi_option_unwrap(const struct EzffiOption *o, void *out);
void ezffi_result_unwrap(const struct EzffiResult *o, void *out);

// Call this function to free the allocated memory in case you didn't 
// unwrap and want to ignore the returned value
void ezffi_option_free(const struct EzffiOption *o);
void ezffi_result_free(const struct EzffiResult *o);

To be able to use the EzffiResult struct, the Error type your Rust Result contains must implement From<Error> for EzffiError, From<EzffiError> for Error, and Clone. The first lets ezffi set the C-side code and message; the second lets it reconstruct an Err(Error) when C passes a Result back into Rust; Clone covers the by-ref shapes and the pre-computation of code / message at construction time. You can use as many Error types as you want but always ensure each of them has a different error code. An example would be:

#[derive(Clone)]
pub enum Error {
    IOError,
    SqlxError,
    ReqwestError,
}

impl From<Error> for EzffiError {
    fn from(e: Error) -> EzffiError {
        EzffiError::new(e.code(), e.to_string())
    }
}

impl From<EzffiError> for Error {
    fn from(e: EzffiError) -> Error {
        match e.code() {
            1 => Error::IOError,
            2 => Error::SqlxError,
            _ => Error::ReqwestError,
        }
    }
}