ronix 0.1.0

Serialize Rust structs to Nix expressions — the bridge between serde and NixOS
Documentation

ronix

Serialize Rust structs to Nix expressions — the bridge between serde and NixOS.

ronix also ships a Nix library (toRON / fromRON) so the conversion works in both directions.

Quick start

[dependencies]
ronix = "0.1"
serde = { version = "1", features = ["derive"] }
use serde::Serialize;

#[derive(Serialize)]
struct FanCurve {
    name: String,
    temp: u32,
    pwm: u8,
}

let curve = FanCurve { name: "cpu".into(), temp: 60, pwm: 150 };
let nix = ronix::to_nix(&curve).unwrap();
// {
//   name = "cpu";
//   temp = 60;
//   pwm = 150;
// }

API

Function Description
to_nix(value) Serialize any Serialize type to a Nix expression string
to_nix_module(value, attr_path) Same, wrapped in a NixOS module (_: { attr.path = …; })
ron_to_nix(ron) Parse a RON string and convert to Nix
ron_to_nix_module(ron, attr_path) Parse RON and wrap in a NixOS module
escape_nix_string(s) Escape a string for embedding in Nix

RON → Nix

let nix = ronix::ron_to_nix(r#"(poll_ms: 2000, name: "hello")"#).unwrap();
assert!(nix.contains("poll_ms = 2000;"));

NixOS modules

let nix = ronix::to_nix_module(&config, "services.myapp.settings").unwrap();
// _: {
//   services.myapp.settings = {
//     ...
//   };
// }

CLI helpers

Enable the cli feature to get a reusable clap argument struct you can embed in your own binary:

[dependencies]
ronix = { version = "0.1", features = ["cli"] }
clap = { version = "4", features = ["derive"] }
use clap::Parser;

#[derive(Parser)]
struct MyCli {
    #[command(flatten)]
    ronix: ronix::cli::RonixArgs,
}

fn main() -> Result<(), ronix::Error> {
    let cli = MyCli::parse();
    cli.ronix.execute()
}

Nix library

ronix provides a pure-Nix library for converting between Nix values and RON. Add the flake as an input and use the library directly:

{
  inputs.ronix.url = "codeberg:caniko/ronix";

  outputs = { ronix, ... }: {
    # Nix → RON
    ronString = ronix.lib.toRON 0 {
      name = "hello";
      count = 42;
    };

    # RON → Nix
    nixValue = ronix.lib.fromRON ''(name: "hello", count: 42)'';

    # Read a .ron file directly
    config = ronix.lib.importRON ./config.ron;
  };
}

Type constructors

The Nix library uses mkRON to represent RON types that have no direct Nix equivalent:

{ mkRON, ... }: {
  char   = mkRON.char "A";
  opt    = mkRON.optional "value";    # Some("value")
  none   = mkRON.optional null;       # None
  tuple  = mkRON.tuple [ 1 2 3 ];
  enum_  = mkRON.enum "Variant" [ ];  # unit variant
  map    = mkRON.map [ { key = "a"; value = 1; } ];
}

NixOS service helper

{ ronix, ... }: {
  config = ronix.nixosHelpers.mkRonService {
    name = "smartcool";
    package = pkgs.smartcool;
    settings = { temp_target = 65; fan_mode = "auto"; };
    execStart = "${pkgs.smartcool}/bin/sc daemon -c /etc/smartcool/config.ron";
  };
}

This generates /etc/smartcool/config.ron and a matching systemd service.

Supported types

Rust / RON Nix output
bool true / false
integers 42, -7
floats 2.5, 1.0
String / &str "hello" (with proper escaping)
Vec<T> / sequences [ ... ]
structs / maps { key = value; ... }
Option<T> Some unwraps, None omits the field
char "c"

Nix interpolation syntax (${...}) is automatically escaped.

Building with Nix

nix build    # build the crate
nix flake check   # run tests, clippy, and fmt
nix develop  # enter a dev shell with cargo, clippy, rustfmt

License

MIT