# C# Binding Generation Guide

This document explains how the C# bindings are automatically generated using `csbindgen`.

## Overview

The C# bindings are automatically generated from Rust FFI definitions using `csbindgen` during the build process. This
ensures that the C# API always stays in sync with the Rust FFI layer.

## How It Works

### Build Process

1. When you run `cargo build --features csharp`:
    - The `build.rs` script is executed before compilation
    - `csbindgen` parses `src/ffi.rs`
    - Safe C# P/Invoke wrappers are generated
    - Generated file is written to `csharp/src/AamNative.cs`

### Generated File Structure

The generated `AamNative.cs` contains:

```csharp
// <auto-generated>
// This code is generated by csbindgen.
// DON'T CHANGE THIS DIRECTLY.
// </auto-generated>

namespace AamRs
{
    internal static unsafe partial class AamNative
    {
        const string __DllName = "aam_rs";
        
        // Auto-generated P/Invoke methods
        [DllImport(__DllName, ...)]
        internal static extern void aam_free(AamlHandle* handle);
        
        // More methods...
    }
}
```

## Modifying Bindings

### Step 1: Update FFI in Rust

Edit `src/ffi.rs` and add or modify the C FFI functions:

```rust
/// Description of what this function does
#[unsafe(no_mangle)]
pub extern "C" fn aam_my_function(
    handle: *mut AamlHandle,
    param: *const c_char,
) -> i32 {
    // Implementation
}
```

Key requirements:

- Functions must be marked with `#[unsafe(no_mangle)]`
- Use C-compatible types: `*mut`, `*const`, `c_char`, `i32`, etc.
- Include doc comments (they will be preserved in generated C# XML docs)

### Step 2: Regenerate Bindings

```bash
cargo build --release --features ffi,csharp
```

This will regenerate `csharp/src/AamNative.cs`.

### Step 3: Verify Changes

```bash
git diff csharp/src/AamNative.cs
```

Check that the changes match your FFI modifications.

### Step 4: Create High-Level Wrapper

In `csharp/src/AamDocument.cs`, create a safe wrapper:

```csharp
public void MyFunction(string param)
{
    CheckResult(AamNative.aam_my_function(Handle, param));
}
```

### Step 5: Add Tests

In `csharp/tests/AamDocumentTests.cs`:

```csharp
[Fact]
public void MyFunction_DoesExpectedThing()
{
    try
    {
        using var doc = AamDocument.Parse("...");
        doc.MyFunction("param");
        // Assert results
    }
    catch (DllNotFoundException)
    {
        // Skip if native library not available
    }
}
```

## Configuration

The `csbindgen` configuration is in `build.rs`:

```rust
Builder::default ()
.input_extern_file("src/ffi.rs")           // Input Rust FFI file
.csharp_dll_name("aam_rs")                 // DLL name for P/Invoke
.csharp_class_name("AamNative")            // Generated class name
.csharp_namespace("AamRs")                 // C# namespace
.csharp_use_function_pointer(false)        // Use safe delegates
.generate_csharp_file("...")               // Output path
.expect("Failed to generate C# bindings");
```

### Customizing Generation

To filter specific functions, use:

```rust
.input_extern_file("src/ffi.rs")
.filter_items( | item| {
// Only generate functions starting with "aam_"
item.starts_with("aam_")
})
```

## Troubleshooting

### Generation Fails

**Error**: `Failed to generate C# bindings`

**Solution**:

1. Verify `src/ffi.rs` is valid Rust
2. Check all functions have `#[unsafe(no_mangle)]`
3. Ensure C types are used: `c_char`, not `String`

```bash
cargo check --features ffi
```

### Incorrect Bindings Generated

**Issue**: Generated signatures don't match expectations

**Solution**:

1. Verify the Rust FFI function signature
2. Delete `csharp/src/AamNative.cs` and regenerate
3. Check `csbindgen` documentation for type conversions

### Missing Functions

**Issue**: Expected functions aren't in the generated file

**Solution**:

1. Verify functions are in `src/ffi.rs`
2. Ensure functions have `#[unsafe(no_mangle)]`
3. Check `pub extern "C"` visibility
4. Rebuild: `cargo clean && cargo build --features csharp`

## Best Practices

1. **Keep FFI layer focused**: Only expose what's necessary
2. **Use descriptive names**: FFI functions should be clear (e.g., `aam_parse`)
3. **Document thoroughly**: Add doc comments to FFI functions
4. **Test generation**: Rebuild frequently to catch issues early
5. **Review changes**: Always review generated bindings before committing

## Example: Adding a New Operation

Let's say we want to add a "validate" operation:

### 1. Add to FFI (`src/ffi.rs`)

```rust
/// Validates an AAM document against a schema.
///
/// Returns 0 on success, -1 on validation error.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn aam_validate(
   handle: *mut AamHandle,
    schema_name: *const c_char,
) -> i32 {
    if handle.is_null() || schema_name.is_null() {
        return -1;
    }
    let handle = unsafe { &mut *handle };

    let schema = match unsafe { CStr::from_ptr(schema_name) }.to_str() {
        Ok(s) => s,
        Err(e) => {
            handle.set_error(e);
            return -1;
        }
    };

    // Implementation...
    0
}
```

### 2. Regenerate

```bash
cargo build --release --features ffi,csharp
```

### 3. Verify Generated C#

```csharp
[DllImport(__DllName, StringMarshalling = StringMarshalling.Utf8, ...)]
internal static extern int aam_validate(AamlHandle* handle, string schemaName);
```

### 4. Create Wrapper

```csharp
public void Validate(string schemaName)
{
    CheckResult(AamNative.aam_validate(Handle, schemaName));
}
```

### 5. Add Test

```csharp
[Fact]
public void Validate_SucceedsWithValidSchema()
{
    try
    {
        using var doc = AamDocument.Parse("host = localhost");
        doc.Validate("ServerSchema");
        // Success if no exception
    }
    catch (DllNotFoundException)
    {
        // Skip in CI
    }
}
```

## CI/CD Integration

The C# bindings are automatically regenerated during:

1. **Local development**: `cargo build --features csharp`
2. **Continuous Integration**: `validate-csharp` job in `release.yml`
3. **Release**: Building the NuGet package

The generated file is committed to git as it's part of the distribution.

## References

- [csbindgen Documentation](https://github.com/Cysharp/csbindgen)
- [Rust FFI Guide](https://doc.rust-lang.org/nomicon/ffi.html)
- [P/Invoke Reference](https://docs.microsoft.com/en-us/dotnet/standard/native-interop/pinvoke)
- [C# Unsafe Code](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/unsafe)

