# Contributing to C# Bindings

This document explains how to develop and contribute to the C# bindings for `aam-rs`.

## Architecture

The C# bindings use P/Invoke to call the native Rust library. The architecture is:

1. **Rust FFI Layer** (`src/ffi.rs`): Provides the C API
2. **csbindgen** (`build.rs`): Auto-generates safe C# wrapper from Rust FFI definitions
3. **Managed Layer** (`csharp/src/*.cs`): User-friendly C# wrapper classes

## Setup

### Prerequisites

- .NET 8.0 or later (for building and running tests)
- Rust toolchain
- C# compatible IDE (Visual Studio, Rider, VS Code + C# extensions)

### Local Development

1. **Build the native library:**

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

This will:

- Build the Rust FFI library
- Auto-generate `csharp/src/AamNative.cs` using csbindgen
- Create the native binaries

2. **Prepare native libraries for local development:**

```bash
# Linux/macOS
./csharp/setup-native.sh

# Windows
csharp\setup-native.bat
```

This copies the native libraries to the expected runtime directories.

3. **Open and build the project:**

```bash
cd csharp
dotnet build
```

## Making Changes

### Adding a New FFI Function

1. **Add the function to `src/ffi.rs`:**

```rust
#[unsafe(no_mangle)]
pub extern "C" fn aam_new_feature(handle: *mut AamlHandle, param: *const c_char) -> i32 {
    // Implementation
}
```

2. **Regenerate C# bindings:**

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

The `csbindgen` build script will auto-generate the corresponding C# P/Invoke wrapper in `csharp/src/AamNative.cs`.

3. **Add the high-level wrapper to `csharp/src/AamDocument.cs`:**

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

4. **Write tests** in `csharp/tests/AamDocumentTests.cs`

### Updating csbindgen

If you need to modify the generated bindings configuration, edit `build.rs`:

```rust
Builder::default ()
.input_extern_file("src/ffi.rs")
.csharp_dll_name("aam_rs")
.csharp_class_name("AamNative")
.csharp_namespace("AamCsharp")
// Add or modify configuration here
.generate_csharp_file("csharp/src/AamNative.cs")
.expect("Failed to generate C# bindings");
```

## Testing

### Run All Tests

```bash
dotnet test
```

### Run Specific Test

```bash
dotnet test --filter "ParseAndFindObj"
```

### With Native Library Path

```bash
export LD_LIBRARY_PATH=/path/to/aam-rs/target/release:$LD_LIBRARY_PATH
dotnet test
```

### Notes

- Tests gracefully skip if the native library is not available (via `DllNotFoundException` catch)
- This allows tests to pass in CI even without native artifacts during early build stages

## Building the NuGet Package

### Locally

```bash
# First, prepare native libraries
./csharp/setup-native.sh

# Then build the package
cd csharp
dotnet pack -c Release
```

The package will be created in `csharp/artifacts/`.

### Version Management

The version is automatically managed by `release-please`. The version is specified in `csharp/aam-csharp.csproj`:

```xml

<Version>2.0.3</Version> <!-- x-release-please-version -->
```

## CI/CD

The C# bindings are built and published automatically via GitHub Actions:

1. **On every push to main** (`release.yml`):
    - Validates C# build
    - Runs C# tests
    - Part of the validation before release

2. **On release** (`release.yml`):
    - Builds native libraries for all platforms
    - Creates NuGet package
    - Publishes to nuget.org

## Debugging

### Enable Native Debugging

On Windows with Visual Studio:

1. Set the debug output to show native traces
2. Use DbgView to monitor native library calls

### Check Generated Bindings

The auto-generated file is located at:

- `csharp/src/AamNative.cs`

This is marked with `// <auto-generated>` — don't edit it manually.

## Common Issues

### Native Library Not Found

**Problem**: `DllNotFoundException` when running tests or code

**Solution**:

```bash
# Linux/macOS
export LD_LIBRARY_PATH=$PWD/../target/release:$LD_LIBRARY_PATH

# Windows
set PATH=%CD%\..\target\release;%PATH%
```

Or run the setup script:

```bash
./csharp/setup-native.sh
```

### Binding Generation Fails

**Problem**: Build fails during C# binding generation

**Solution**:

1. Ensure `src/ffi.rs` has valid Rust FFI functions
2. Check that the function is marked with `#[unsafe(no_mangle)]`
3. Verify the Rust syntax is correct by running `cargo check --features ffi`

### Test Failures

**Problem**: C# tests fail with assertion errors

**Solution**:

1. Ensure the native library is on the library path
2. Check that the Rust FFI implementation matches expectations
3. Add debug output to understand what's happening

## Best Practices

1. **Keep FFI layer minimal**: Only expose necessary functionality
2. **Use safe wrappers**: The `AamDocument` class should handle all safety concerns
3. **Test thoroughly**: Both FFI layer and managed layer need tests
4. **Document thoroughly**: Add XML docs to public C# APIs
5. **Version carefully**: Keep versions in sync across Cargo.toml and .csproj

## Documentation

- [C# README](README.md) — User-facing documentation
- [API Reference](README.md#api-reference) — Full API documentation
- [Examples](examples/) — Working examples

## Getting Help

- Check existing issues on GitHub
- Review the Rust FFI implementation in `src/ffi.rs`
- Look at examples in `csharp/examples/`
- Check the [csbindgen documentation](https://github.com/Cysharp/csbindgen)

