Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: CI

on:
push:
branches: [ main, master ]
pull_request:
branches: [ main, master ]

jobs:
build:
name: Build and Verify
runs-on: ubuntu-latest

steps:
- name: Checkout Repository
uses: actions/checkout@v4

- name: Setup .NET SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'

- name: Restore Dependencies
run: dotnet restore src/Nalix.Protogen.slnx

- name: Build Solution
run: dotnet build src/Nalix.Protogen.slnx -c Release --no-restore
77 changes: 77 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
name: CD Release

on:
push:
tags:
- 'v*'

permissions:
contents: write

jobs:
build-binaries:
name: Build Binary for ${{ matrix.rid }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
- os: windows-latest
rid: win-x64
binary_name: nalix-protogen.exe
- os: ubuntu-latest
rid: linux-x64
binary_name: nalix-protogen
- os: macos-latest
rid: osx-x64
binary_name: nalix-protogen

steps:
- name: Checkout Repository
uses: actions/checkout@v4

- name: Setup .NET SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'

- name: Publish Single File Binary
run: |
dotnet publish src/Nalix.Protogen.Cli/Nalix.Protogen.Cli.csproj -c Release -r ${{ matrix.rid }} -p:PublishSingleFile=true -p:PublishTrimmed=false --self-contained true -o ./dist/${{ matrix.rid }}

- name: Zip Binary (Windows)
if: matrix.os == 'windows-latest'
run: |
Compress-Archive -Path ./dist/${{ matrix.rid }}/* -DestinationPath ./nalix-protogen-${{ matrix.rid }}.zip

- name: Zip Binary (Unix)
if: matrix.os != 'windows-latest'
run: |
chmod +x ./dist/${{ matrix.rid }}/${{ matrix.binary_name }}
zip -j nalix-protogen-${{ matrix.rid }}.zip ./dist/${{ matrix.rid }}/${{ matrix.binary_name }}

- name: Upload Binary Artifact
uses: actions/upload-artifact@v4
with:
name: nalix-protogen-${{ matrix.rid }}
path: nalix-protogen-${{ matrix.rid }}.zip

create-release:
name: Create GitHub Release
needs: build-binaries
runs-on: ubuntu-latest
steps:
- name: Download Artifacts
uses: actions/download-artifact@v4
with:
path: ./artifacts
merge-multiple: true

- name: Display Downloaded Files
run: ls -R ./artifacts

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
files: ./artifacts/nalix-protogen-*.zip
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
235 changes: 48 additions & 187 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,219 +1,80 @@
# Nalix Protocol Toolkit
# Nalix Protogen

A Roslyn-based code generator that produces wire-compatible C and Rust bindings from C# packet definitions. Guarantees identical byte layout across all platforms.
[![GitHub repository](https://img.shields.io/badge/GitHub-ppn--systems%2Fprotogen-blue?logo=github)](https://github.com/ppn-systems/protogen)
[![.NET 10.0](https://img.shields.io/badge/.NET-10.0-blueviolet?logo=dotnet&logoColor=white)](https://dotnet.microsoft.com)
[![License](https://img.shields.io/badge/License-Apache_2.0-orange?logo=apache)](LICENSE)

## Installation
A high-performance, Roslyn-powered code generation utility that compiles C# packet definitions into wire-compatible C and Rust bindings. Ensures identical byte layout, alignment, and serialization order across platforms.

Download the latest release or build from source:
---

```bash
dotnet publish src/Nalix.Protocol.Toolkit.Cli -c Release -o ./bin
```

The output is a self-contained `nalix-protogen` executable.
## 🗺️ Documentation Index

## Usage

```bash
nalix-protogen --project <path-to-csproj> --c-out <dir> [--rust-out <dir>] [options]
nalix-protogen --assembly <path-to-dll> --c-out <dir> [--rust-out <dir>] [options]
```
To keep this project maintainable as we support additional target languages, detailed specifications are split into dedicated guides:
* **📐 [Protogen Serialization & Type Specification](docs/SPECIFICATION.md):** Primitive and complex type mappings (C/Rust), string formats, C# definition attributes (`[Packet]`, `[SerializeOrder]`), and generated code outputs.

### Options
---

| Flag | Description |
|------|-------------|
| `-p, --project` | Path to the `.csproj` file containing packet definitions |
| `-a, --assembly` | Path to the compiled `.dll` containing packet definitions |
| `--c-out` | Output directory for C files (`packets.h`, `packets.c`, `packet_test.c`) |
| `--rust-out` | Output directory for Rust files (`packets.rs`, `packet_tests.rs`) |
| `-v, --verbose` | Show detailed output |
## ✨ Key Features

### Examples

```bash
# Generate both C and Rust bindings
nalix-protogen -p ../Nalix.Codec/Nalix.Codec.csproj --c-out ./gen/c --rust-out ./gen/rs

# Generate C only
nalix-protogen -p ../Nalix.Codec/Nalix.Codec.csproj --c-out ./gen/c

# Generate from compiled DLL
nalix-protogen -a ../Nalix.Codec/bin/Release/net10.0/Nalix.Codec.dll --c-out ./gen/c --rust-out ./gen/rs
```
* **Roslyn-Powered Parser:** Uses `MSBuildWorkspace` to parse `.csproj` source trees or reflection to read compiled assemblies (`.dll`).
* **Multi-Language Bindings:** Generates fully-typed native C structs (with MSVC pack pragmas) and safe Rust structs.
* **Automatic Size Verification:** Builds round-trip test suites (`packet_test.c` and `packet_tests.rs`) to prove serialization/deserialization logic is identical across target languages.

## Supported C# Types
---

### Primitives
## 🛠️ Installation

| C# Type | C Type | Rust Type | Size |
|---------|--------|-----------|------|
| `byte` | `uint8_t` | `u8` | 1 |
| `sbyte` | `int8_t` | `i8` | 1 |
| `bool` | `uint8_t` | `u8` | 1 |
| `short` | `int16_t` | `i16` | 2 |
| `ushort` | `uint16_t` | `u16` | 2 |
| `int` | `int32_t` | `i32` | 4 |
| `uint` | `uint32_t` | `u32` | 4 |
| `long` | `int64_t` | `i64` | 8 |
| `ulong` | `uint64_t` | `u64` | 8 |
| `float` | `float` | `f32` | 4 |
| `double` | `double` | `f64` | 8 |
Build the self-contained compiler binary from the repository root:

### Special Types

| C# Type | C Type | Rust Type | Size |
|---------|--------|-----------|------|
| `Bytes32` | `Bytes32` (struct with `uint8_t data[32]`) | `[u8; 32]` | 32 |
| `PacketHeader` | `PacketHeader` (packed struct) | `PacketHeader` | 10 |
| `string` | `NalixString` (length-prefixed UTF-8) | `Vec<u8>` | variable |

**String wire format:** `[int32_t length][utf8 bytes]` — null = length -1, empty = length 0. Matches the Nalix codec `StringFormatter`.

### Enums

Enums are mapped by their underlying integer type. Supported underlying types: `byte`, `sbyte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`.

```csharp
public enum OpMode : byte { Read = 0, Write = 1 }
// C: uint8_t
// Rust: u8
```bash
dotnet publish src/Nalix.Protogen.Cli -c Release -o ./bin
```

## Unsupported Types

The following C# types have **no native equivalent** in C or Rust and are not supported:

| C# Type | Reason |
|---------|--------|
| `char` | C `char` is 1 byte, C# `char` is 2 bytes (UTF-16). Incompatible. |
| `decimal` | No equivalent in C or Rust. |
| `DateTime` | No equivalent. Use `long` (ticks) or `ulong` (unix timestamp). |
| `Guid` | No equivalent. Use `Bytes16` or two `ulong` fields. |
| `object` / reference types | Not serializable to wire format. |
| Arrays / collections | Variable-length. Use fixed-size fields. |
| `Nullable<T>` | Not serializable. Use a sentinel value. |

## Packet Definition Rules

### Attributes
The output compiles to a self-contained `protogen` executable in the `./bin` directory.

| Attribute | Purpose |
|-----------|---------|
| `[Packet]` | Marks a class as a packet. Must inherit `PacketBase<TSelf>`. |
| `[SerializeOrder(n)]` | Sets the serialization order for payload fields. Lower = first. |
| `[SerializeHeader(n)]` | Sets the serialization order for header fields. Header fields are serialized before payload. |
| `[SerializeIgnore]` | Excludes a field from serialization. |
---

### Example
## 💻 CLI Usage

```csharp
[Packet]
public sealed class Handshake : PacketBase<Handshake>
{
[SerializeHeader(0)]
public PacketHeader _header;

[SerializeOrder(0)]
public byte Stage;

[SerializeOrder(1)]
public ushort Reason;

[SerializeOrder(2)]
public ulong SessionToken;

[SerializeOrder(3)]
public Bytes32 PublicKey;

[SerializeOrder(4)]
public Bytes32 Nonce;

[SerializeOrder(5)]
public Bytes32 Proof;

[SerializeOrder(6)]
public Bytes32 TranscriptHash;
}
```

Generates:

**C** (`packets.h`):
```c
typedef struct {
PacketHeader _header;
uint8_t Stage;
uint16_t Reason;
uint64_t SessionToken;
Bytes32 PublicKey;
Bytes32 Nonce;
Bytes32 Proof;
Bytes32 TranscriptHash;
} Handshake; // 149 bytes
```

**Rust** (`packets.rs`):
```rust
#[repr(C)]
pub struct Handshake {
pub _header: PacketHeader,
pub stage: u8,
pub reason: u16,
pub session_token: u64,
pub public_key: [u8; 32],
pub nonce: [u8; 32],
pub proof: [u8; 32],
pub transcript_hash: [u8; 32],
} // 149 bytes
```bash
# General CLI Command Syntax
protogen --project <path-to-csproj> --c-out <dir> [--rust-out <dir>] [options]
protogen --assembly <path-to-dll> --c-out <dir> [--rust-out <dir>] [options]
```

## Generated Output
### Options & Flags

### C Files
| Flag | Long Flag | Description |
| :--- | :--- | :--- |
| `-p` | `--project` | Path to the C# `.csproj` containing packet definitions. |
| `-a` | `--assembly` | Path to the compiled C# `.dll` containing packet definitions. |
| | `--c-out` | Output directory for C files (`packets.h`, `packets.c`, `packet_test.c`). |
| | `--rust-out` | Output directory for Rust files (`packets.rs`, `packets_tests.rs`). |
| `-v` | `--verbose` | Enable verbose logging for MSBuild warnings. |

| File | Description |
|------|-------------|
| `include/packets.h` | Struct definitions, function declarations, size macros |
| `src/packets.c` | Serialize/deserialize/equals implementations |
| `tests/packet_test.c` | Round-trip test for each packet |
### Practical Examples

Compile the test:
```bash
gcc -Iinclude -o packet_test tests/packet_test.c src/packets.c && ./packet_test
```

### Rust Files
# Generate C and Rust bindings from a csproj source
protogen -p ../Nalix.Codec/Nalix.Codec.csproj --c-out ./gen/c --rust-out ./gen/rs

| File | Description |
|------|-------------|
| `src/packets.rs` | Struct definitions with serialize/deserialize/equals methods |
| `tests/packet_tests.rs` | Round-trip test for each packet |

Run the test:
```bash
cargo test
# Generate C-only bindings from a compiled DLL assembly
protogen -a ../Nalix.Codec/bin/Release/net10.0/Nalix.Codec.dll --c-out ./gen/c
```

## Wire Format
---

All multi-byte values are encoded in **little-endian** byte order. `Bytes32` and `PacketHeader` fields are copied as raw bytes (no endian conversion needed).
## 📐 Project Architecture

Serialization layout:
```
[Header fields in SerializeHeader order] [Payload fields in SerializeOrder order]
```

## Architecture

```
Nalix.Protocol.Toolkit.Abstractions # Interfaces and data models
Nalix.Protocol.Toolkit.Roslyn # Roslyn analyzer + code generators
Nalix.Protocol.Toolkit.Cli # CLI entry point
```plaintext
Nalix.Protogen.Abstractions # Shared models, interfaces, and options contracts.
Nalix.Protogen.Roslyn # MSBuild and Roslyn workspace packet code parser.
Nalix.Protogen.Cli # Console Application entry point (protogen).
```

The toolkit uses Roslyn (`MSBuildWorkspace`) to analyze C# source, extract packet definitions via attributes, and generate wire-compatible C and Rust code. `PacketHeader` is hardcoded due to its `[StructLayout(Explicit)]` layout.
---

## License
## ⚖️ License

Apache License 2.0
Apache License 2.0. See [LICENSE](LICENSE) for details.
Loading
Loading