Config, Code, and Crypto in One .mdix File
"I built this because I was tired of copy-pasting the same JSON config blocks 500 times. Turns out other people hate that too."
β Mid-D-Man, Creator
The Rust port is feature-complete but has not yet seen wide production use. The API is stable and the compiler pipeline is fully implemented. Use it in your own projects, test it, and give feedback β but treat it as beta for anything mission-critical until v1.0.0 is officially published.
| Package | Status |
|---|---|
dixscript (core Rust library) |
β Feature-complete, API stable |
mdix-cli |
β All commands implemented |
mdix-ffi / C# bindings |
β³ Bindings generated, packaging pending |
mdix-go |
β³ Header generated, Go wrapper pending |
mdix-java / mdix-python |
β³ Pending runtime wrappers |
mdix-wasm |
β³ Pending wasm-bindgen annotations |
mdix-c |
β³ Header stable, examples pending |
The C# prototype (https://github.com/Mid-D-Man/DixScript) remains the reference implementation for the language itself.
Started as: A quick hack to make a mobile game's remote config less painful.
The Problem: The game had weapons, camos, attainment missions, and shop data spread across Unity Remote Config as massive nested JSON blobs. Adding a single new field to a camo definition meant updating it in dozens of places. One typo and suddenly every weapon in the game was broken at runtime β with no error until the player hit that screen.
Here's a real slice of what that looked like:
{
"EquipableItemCamoClassId": "ALL_SMG_CAMOS_CONFIG",
"InventoryItemCamos": [
{
"MainItemId": "ALIYAHOO419",
"MainItemClass": "BASIC_SMG",
"CamoRaritySubClass": [
{
"RaritySubClassId": "Aliyahoo419_Basic_Camos",
"MainItemCamos": [
{
"CamoId": "ALIYAHOO419",
"CamoIndex": 0,
"CamoAvailableInSeason": "1",
"CamoRarity": "Basic",
"CamoAtlasSpriteName": "Aliyahoo419(Clone)",
"CamoInGameName": "Aliyahoo419",
"CamoType": "Sprite",
"MaterialAddress": "Null"
}
]
}
]
}
]
}This is just the camo config for one weapon class. Three separate blobs, hundreds of lines, all duplicating the same structure.
The Solution: "What if the shape of a camo was defined once, and I just filled in the data?"
The Result: The same config in DixScript:
@ENUMS(
WeaponClass { BASIC_SMG, RUNIC_SMG, LEGENDARY_SMG }
CamoRarity { Basic, Rare, Epic, Legendary, Runic }
CamoType { Sprite, SpriteAndMaterial }
)
@QUICKFUNCS(
~camo<object>(id, index<int>, rarity<enum>, sprite, inGameName, type<enum>) {
return {
CamoId = id
CamoIndex = index
CamoAvailableInSeason = "1"
CamoRarity = rarity
CamoAtlasSpriteName = $"{sprite}(Clone)"
CamoInGameName = inGameName
CamoType = type
MaterialAddress = "Null"
}
}
~rarityClass<object>(subClassId, camos) {
return { RaritySubClassId = subClassId, MainItemCamos = camos }
}
~weapon<object>(itemId, class<enum>, rarityClasses) {
return { MainItemId = itemId, MainItemClass = class, CamoRaritySubClass = rarityClasses }
}
)
@DATA(
EquipableItemCamoClassId = "ALL_SMG_CAMOS_CONFIG"
InventoryItemCamos::
weapon("ALIYAHOO419", WeaponClass.BASIC_SMG, [
rarityClass("Aliyahoo419_Basic_Camos", [
camo("ALIYAHOO419", 0, CamoRarity.Basic, "Aliyahoo419", "Aliyahoo419", CamoType.Sprite)
]),
rarityClass("Aliyahoo419_Epic_Camos", [
camo("ALIYAHOO419_HORIZON", 0, CamoRarity.Rare, "Aliyahoo419_Horizon", "Aliyahoo419-Horizon", CamoType.Sprite),
camo("ALIYAHOO419_ROSE", 1, CamoRarity.Epic, "Aliyahoo419_Rose", "Aliyahoo419-Rose", CamoType.Sprite)
])
])
)
| Config | JSON (formatted) | DixScript | Reduction |
|---|---|---|---|
ALL_SMG_CAMOS_CONFIG |
~350 lines | ~110 lines | 69% |
ALL_SMG_EQ_AND_CAMO_ATTAINMENT_CONFIGS |
~280 lines | ~90 lines | 68% |
ALL_SMG_UNIQUE_CAMOS_MISSIONS_CONFIG |
~230 lines | ~75 lines | 67% |
| All 3 configs combined | ~860 lines | ~275 lines | ~68% |
DixScript is a data interchange format that combines:
- π¦ Configuration (like TOML)
- π§ Compile-time functions (like Jsonnet, but less cryptic)
- π Built-in encryption (AES-256-GCM, not an afterthought)
- ποΈ Automatic compression (gzip/bzip2/lzma)
- π Type safety (enums, strong typing when you want it)
- π― Zero runtime dependencies (pure Rust, or C# in the original)
All in one file with a .mdix extension.
Modern projects have config sprawl: /config βββ base.json # 500 lines βββ development.json # 300 lines (80% duplicated from base) βββ production.json # 400 lines (90% duplicated) βββ secrets.env # Separate encryption βββ validation.js # Separate validation logic βββ build.sh # Compresses everything βββ deploy.yaml # References all of the above You change one setting β Update 3 files β Run 2 scripts β Hope you didn't break something.
@DLM(DCompressor.gzip, DEncryptor.aes256)
@QUICKFUNCS(
~createEnemy<object>(name, health, damage) {
return {
name = name,
health = health,
damage = damage,
armor = health / 10,
xp = health / 2,
gold = health / 4
}
}
)
@DATA(
environment<enum> = Environment.PROD
enemies::
createEnemy("Goblin", 50, 10),
createEnemy("Orc", 100, 20),
createEnemy("Troll", 200, 40)
)
@SECURITY(
encryption -> { mode = "keyfile", algorithm = "aes256-gcm" }
)
One file. Config, logic, encryption, compression. Done.
Write logic once, execute at compile time. No runtime overhead.
@QUICKFUNCS(
~calculateDamage<int>(base, difficulty<enum>) {
multiplier = difficulty == Difficulty.HARD ? 2.0 : 1.0
return Math.round(base * multiplier)
}
)
@DATA(
easy_enemy = { damage = calculateDamage(50, Difficulty.EASY) },
hard_enemy = { damage = calculateDamage(50, Difficulty.HARD) }
)
@DATA(
// Flat properties (single equals)
app_name = "MyApp"
version = "1.0.0"
port = 8080
// Table properties (single colon)
server: host = "localhost", port = 8080, ssl = true
// Group arrays (double colon)
admins:: "alice", "bob", "charlie"
)
Commas are optional between entries. Use horizontal style with commas or vertical style without β your choice.
@DLM(DCompressor.gzip, DEncryptor.aes256)
@SECURITY(
encryption -> { mode = "password" }
)
@DATA(
api_key = "super_secret_key"
)
Compile: mdix compile secrets.mdix --password
Output: secrets.mdix.enc (compressed + encrypted)
@ENUMS(
LogLevel { DEBUG = 0, INFO = 1, WARN = 2, ERROR = 3 }
Environment { DEV = 1, STAGING = 2, PROD = 3 }
)
@DATA(
log_level<enum> = LogLevel.INFO
current_env<enum> = Environment.PROD
)
@DATA(
// Inferred
count = 42
price = 19.99
enabled = true
// Explicit
max_users<int> = 1000
tax_rate<float> = 0.15f
color<hex> = #FF5733
avatar = b:("base64data...")
email_regex = r:("^[a-z@.]+$")
release_date = 2025-12-31
)
| Feature | JSON | YAML | TOML | Jsonnet | DixScript |
|---|---|---|---|---|---|
| Deduplication via functions | β | β | β | β | β |
| Built-in encryption | β | β | β | β | β |
| Built-in compression | β | β | β | β | β |
| Enums | β | β | β | β | |
| Optional commas | β | β | β | β | β |
| Compile-time execution | β | β | β | β | β |
| Zero runtime dependencies | β | β | β | β | β |
| Human-readable | β | β | β | β |
@CONFIG( // Compiler settings, metadata
version -> "1.0.0"
)
@IMPORTS( // Import from other .mdix files
Utils from "common/utils.mdix"
)
@DLM( // Data Lifecycle Modules β compression + encryption
DCompressor.gzip
DEncryptor.aes256
)
@ENUMS( // Named constants
Status { ACTIVE, INACTIVE, PENDING }
)
@QUICKFUNCS( // Compile-time functions
~calculate<int>(x, y) {
return x + y
}
)
@DATA( // Your actual data
result = calculate(10, 20)
)
@SECURITY( // Security configuration
encryption -> { mode = "password" }
)
All sections are optional. Use what you need.
Original: Written in C# (.NET 8), fully functional, available at https://github.com/Mid-D-Man/DixScript.
This Repo: Rust port β feature-complete, API stable, actively tested via CI.
| Component | Status | Notes |
|---|---|---|
| Utilities | β Complete | Logger, keywords, helpers |
| ErrorManager | β Complete | All 10 error types |
| Lexer | β Complete | All token types |
| Parser | β Complete | All 6 sections |
| Semantic Analyzer | β Complete | All 8 analysis phases |
| QuickFuncs Resolver | β Complete | Full function interpreter |
| Binary Serialization | β Complete | Packer + Unpacker |
| DLM Pipeline | β Complete | Forward and reverse |
| Runtime API | β Complete | Load, access, build, convert |
| LSP Server | β Complete | Full IDE integration |
| CLI | β Complete | All commands |
| FFI / C | β Complete | C header + 40+ exported functions |
All wrappers bind to the Rust runtime via FFI. The core is complete β wrappers are pending packaging and publishing.
| Package | Language | Status |
|---|---|---|
mdix-ffi + C# NuGet |
C# / Unity | β³ Bindings generated, NuGet packaging pending |
mdix-go |
Go | β³ C header generated, Go wrapper pending |
mdix-java |
Java / Kotlin | β³ Pending JNI wrappers |
mdix-python |
Python | β³ Pending PyO3 wrappers |
mdix-wasm |
JS / Browser | β³ Pending wasm-bindgen annotations |
mdix-c |
C / C++ | β³ Header stable, examples pending |
Why Rust?
- π Performance: The C# prototype is fast. Rust is faster.
- π§ Portability: Native binaries, WASM, embedded.
- π¦ Safety: Ownership model catches bugs at compile time.
- π FFI: One Rust core, wrappers for every major language.
# Build from source (crates.io publish coming soon)
git clone https://github.com/Mid-D-Man/DixScript-Rust
cd DixScript-Rust
cargo build -p mdix-cli --release
# Basic usage
mdix validate config.mdix
mdix compile config.mdix
mdix compile secrets.mdix --password
mdix convert config.json --to mdix
mdix inspect config.mdix --keysuse dixscript::Runtime::DixLoader;
use dixscript::Runtime::DixLoadOptions;
fn main() {
let loader = DixLoader::new();
let data = loader.load_text("config.mdix", &DixLoadOptions::new()).unwrap();
let port: i32 = data.get("server.port").unwrap_or(8080);
println!("Server on port {}", port);
}Great for: Game data configs (weapons, items, enemies, levels), multi-environment server configs, encrypted secrets bundles, any schema where you're copy-pasting structure repeatedly.
Maybe not for: Tiny configs under 50 lines with no repetition, simple key-value stores (TOML is simpler), situations requiring maximum existing tooling support.
The rule of thumb: If changing one field currently means editing it in more than three places, DixScript will help.
Contributions welcome. The Rust port is feature-complete β areas where help is most useful: language wrapper packaging (C#, Go, Python), LSP editor extensions, documentation, and test coverage.
Code style: Follow rustfmt.toml in the repo.
- Grammar spec:
others/midx.ebnf - C# reference implementation:
https://github.com/Mid-D-Man/DixScript - CI results:
https://mid-d-man.github.io/DixScript-Rust/
MIT β use it however you want, commercial or personal. See LICENSE.
Creator: Mid-D-Man
GitHub: https://github.com/Mid-D-Man/DixScript-Rust
Original (C#): https://github.com/Mid-D-Man/DixScript
Questions? Found a bug? Open an issue.
"Config files shouldn't require a PhD to maintain." β The DixScript Philosophy