.NET 10 bindings and code-generation tooling for CUE
See LICENSE in this repository and the license of the libcue/CUE
project for the respective licensing terms.
Warning
cue-dotnet depends on the separate libcue
project. The native library must be built first and copied to the root
of this repository before building, testing, or running the generator.
This project integrates with the native Go implementation of cue through a CGO adapter forked from libcue:
flowchart LR
A["cuelang/cue<br/>Go"]
B["intresrl/libcue<br/>Go + CGO"]
D["this repository<br/>Cue.Api<br/>P/Invoke"]
E[".NET consumers"]
F["this repository<br/>Cue.Generator<br/>C#"]
G["Generated C#"]
A --> B --> D
D --> E
D --> F --> G
This repository has the following layout:
Cue.Api--- managed .NET API overlibcueusing P/Invoke.Cue.Generator--- CLI that compiles CUE schemas and generates C# code.Examples--- sample CUE schemas, generated C# files, andgenerator debug output.
- .NET 10 SDK.
- Go 1.25.0: CGO must be enabled and a compatible C compiler must be installed.
To check CGO run:
go env CGO_ENABLED # should output '1'A convenient checkout layout is:
<your_clone_directory>/
├── libcue/
└── cue-dotnet/
The output of the libcue build must be generated or copied into the root of
cue-dotnet.
On Linux:
cd libcue && go build -buildmode=c-shared -o ../cue-dotnet/libcue.soOn Windows (Git Bash, msys2 or similar):
Caution
On Windows, keep the "lib" prefix in libcue.dll to avoid overwriting cue.h in libcue.
cd libcue && go build -buildmode=c-shared -o ../cue-dotnet/libcue.dllAfter the native dependency is present in the repository root:
dotnet restore
dotnet buildWarning
Run a rebuild every time you make changes in libcue. The DLL or shared library is copied in the output directory of Cue.Api.
CUE operations begin with a context:
CueContext owns the native CUE context while Value represents a
managed wrapper around a native CUE value.
Keep the context alive for the lifetime of values created from it and dispose native-backed objects appropriately.
Cue.Generator is a .NET CLI that compiles a CUE schema and generates
C# source.
You may execute it like this:
# dotnet run --project Cue.Generator -- <input.cue> <output.cs>
dotnet run --project Cue.Generator -- Examples/simple.cue generated.csAn optional debug output path can be supplied via the --debug parameter.
To regenerate every example, use the shell script from the repository
root. It discovers all .cue files beneath Examples automatically and writes
the corresponding .cs and .debug.log files alongside each schema:
bash ./run-generator-examples.shThe current implementation and tests cover CUE concepts including:
- structs;
- lists;
- definitions;
- references;
- nullable values;
- disjunctions and
matchNexpressions; - constrained primitive values.
The generated representation can model alternatives as interfaces and record implementations instead of arbitrarily reducing a CUE disjunction to one type.
CUE primitive definitions with constraints are encoded as readonly record structs
that wrap a value with an IsValid() validation method.
The inner value type is narrowed based on the constraint range or the literal magnitude. Unbounded or very
large ranges use BigInteger, floating point uses the ExtendedNumerics library's
BigDecimal type, and bounded ranges
select the smallest fitting type.
When a literal is encoded in a constraint definition its type is encoded to the smallest numeric type compatible with
its value. decimal is the only type used instead of BigDecimal for floating point types.
Constraint logic in IsValid() is encoded exactly as CUE expressions:
- Range bounds become comparisons:
int & >=0 & <=100→value >= 0 && value <= 100 - Regex constraints use
Regex.IsMatch():string & =~"pattern"→Regex.IsMatch(value, "pattern") - Literal disjunctions become
||chains:1 | 5 | 10→value == 1 || value == 5 || value == 10
CUE:
cue-dotnet/Readme/01-constrained-types.cue
Lines 1 to 10 in 6b101b5
Generated C#:
cue-dotnet/Readme/01-constrained-types.cs
Lines 7 to 39 in 6b101b5
See the full generated file for complete examples of constrained primitive types with validation logic.
CUE struct definitions are encoded as classes with properties.
Fields are required by default; optional fields (field?) or nullable fields
(null | type) become nullable/non-required properties.
CUE:
cue-dotnet/Readme/02-structs-and-composition.cue
Lines 1 to 25 in 6b101b5
Generated C#:
cue-dotnet/Readme/02-structs-and-composition.cs
Lines 7 to 28 in 6b101b5
CUE lists become List<T>. For concrete index-specific lists (e.g., [string, int, bool]),
the generator creates tuples or CueList<TConcrete, TAnyIndex> types to distinguish
fixed elements from variable-length tails.
Inline struct definitions are extracted as separate classes and referenced:
CUE:
cue-dotnet/Readme/03-lists-and-nesting.cue
Lines 1 to 21 in 6b101b5
Generated C#:
cue-dotnet/Readme/03-lists-and-nesting.cs
Lines 7 to 43 in 6b101b5
Named struct disjunctions create an interface with nested record types for each variant,
plus a special Value record that holds all possible branches:
CUE:
cue-dotnet/Readme/04-unions-and-references.cue
Lines 1 to 13 in 6b101b5
Generated C#:
cue-dotnet/Readme/04-unions-and-references.cs
Lines 7 to 29 in 6b101b5