ffizz-header 0.5.0

FFI helpers to generate a C header for your library
Documentation
This crate supports generating a C header for a library crate, directly in the library itself.

Typically the crate would be annotated with `ffizz_header` macros to define the header content.
Then, the header is generated by calling [`generate`].

# Generating Headers

What follows is a simple, effective way to generate the header file, using the excellent [cargo-xtask](https://github.com/matklad/cargo-xtask/).
With this in place, simply run `cargo xtask codegen` to generate the header file.
The file can either be checked in (in which case CI should verify that it is up-to-date), or generated as part of the release / packaging process.

## Setup

In your library's top level, add a call to 

```ignore
#[cfg(debug_assertions)] // only include this in debug builds
/// Generate the header
pub fn generate_header() -> String {
    ffizz_header::generate()
}
```

Set up an xtask project as described in [the project's documentation](https://github.com/matklad/cargo-xtask/).
Add your library as a dependency of the xtask crate.
In `xtask/src/main.rs`, for a `mysupercool-lib` crate:

```ignore
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;

fn main() {
    let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
    let workspace_dir = manifest_dir.parent().unwrap();

    // assume the mysupercool-lib crate is in `lib/`..
    let lib_crate_dir = workspace_dir.join("tests").join("lib");
    let mut file = File::create(lib_crate_dir.join("mysupercoollib.h")).unwrap();
    write!(&mut file, "{}", ::mysupercool_lib::generate_header()).unwrap();
}
```

You may wish to improve on this implementation, with proper command-line parsing and error handling.

### Caveats

This method does not support producing multiple header files for a single workspace.
Rust refuses to link them, due to duplicate symbols.
If your workspace contains multiple libraries, another option is to build a binary for each one, that generates the header file for only that library.

## Defining Headers

Typically, a library exporting a header will define its topmatter and corresponding footer in `src/lib.rs`, using [`snippet`].

```
ffizz_header::snippet! {
#[ffizz(name="topmatter", order=1)]
/// ```c
/// #ifndef INFPREC_H
/// #define INFPREC_H
///
/// #include <stdint.h> // ..and any other required headesr
/// ```
}

ffizz_header::snippet! {
#[ffizz(name="bottomatter", order=10000)]
/// ```c
/// #endif /* INFPREC_H */
/// ```
}
```

The topmatter might also include forward declarations of types or macros.

The remaining declarations will be for types and exported functions, using [`item`].
It can be helpful to define a range of `order` values for each source file, to keep related declarations together in the generated header.

```
#[ffizz_header::item]
#[ffizz(order = 900)]
/// ***** infprec_t *****
///
/// An infinite-precision integer.
/// ```c
/// typedef struct infprec_t infprec_t
/// ```
pub struct InfPrec { /* .. */ }
```

```
# type infprec_t = ();
#[ffizz_header::item]
#[ffizz(order = 901)]
/// Add two infinite-precision numbers.
#[no_mangle]
pub unsafe extern "C" fn infprec_add(a: infprec_t,  b: infprec_t) -> infprec_t { todo!() }
```

### `extern "C"`

For headers intended for use in C and C++, it may be helpful to define an EXTERN_C macro:

```c
/// #ifdef __cplusplus
/// #define EXTERN_C extern "C"
/// #else
/// #define EXTERN_C
/// #endif // __cplusplus
```

this can later be used in declarations like
```c
EXTERN_C infprec_t infprec_add(infprec_t a, infprec_t b);
```