# 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)