1+ # Nix Bindings for Rust
12
2- # ` nix_bindings_* ` crates
3+ Rust bindings for the Nix [ C API ] , providing safe, idiomatic Rust interfaces to Nix's core functionality including store operations, expression evaluation, and flake management.
34
4- Use the Nix [ C API ] from Rust.
5+ ## Overview
56
6- ## Build with ` nix-cargo-integration `
7+ This workspace provides multiple crates that wrap different layers of the Nix C API:
78
8- The development environment and building with Nix are taken care of by [ nix-cargo-integration] ( https://github.com/90-008/nix-cargo-integration#readme ) ([ options] ( https://flake.parts/options/nix-cargo-integration.html ) ).
9+ - ** ` nix-bindings-util ` ** - Utility types and helpers (settings, context, version detection, string handling)
10+ - ** ` nix-bindings-store ` ** - Store operations (paths, derivations, store management)
11+ - ** ` nix-bindings-expr ` ** - Expression evaluation and type extraction
12+ - ** ` nix-bindings-flake ` ** - Flake operations
13+ - ** ` nix-bindings-fetchers ` ** - Fetcher functionality (requires Nix ≥ 2.29)
914
10- The dependency on Nix is taken care of with the [ ` nix-bindings-rust ` flake-parts module ] ( ) .
15+ The ` nix-bindings-bindgen-raw ` crate contains the generated FFI bindings and is not intended for direct use .
1116
12- Example usage:
17+ ## Features
18+
19+ - ** Nix evaluation** - Evaluate Nix expressions and create and extract values
20+ - ** Store integration** - Interact with the Nix store, manage paths, build derivations
21+ - ** Threading** - GC registration and memory management via ` Drop `
22+ - ** Lazy evaluation** - Fine-grained control over evaluation strictness
23+ - ** Version compatibility** - Conditional compilation for different Nix versions
24+
25+ ## Quick Start
26+
27+ Add the crates you need to your ` Cargo.toml ` :
28+
29+ ``` toml
30+ [dependencies ]
31+ nix-bindings-store = { git = " https://github.com/nixops4/nix-bindings-rust" }
32+ nix-bindings-expr = { git = " https://github.com/nixops4/nix-bindings-rust" }
33+ ```
34+
35+ Basic example:
36+
37+ ``` rust
38+ use nix_bindings_expr :: eval_state :: {EvalState , init, gc_register_my_thread};
39+ use nix_bindings_store :: store :: Store ;
40+ use std :: collections :: HashMap ;
41+
42+ fn main () -> anyhow :: Result <()> {
43+ // Initialize Nix library and register thread with GC
44+ init ()? ;
45+ let guard = gc_register_my_thread ()? ;
46+
47+ // Open a store connection and create an evaluation state
48+ let store = Store :: open (None , HashMap :: new ())? ;
49+ let mut eval_state = EvalState :: new (store , [])? ;
50+
51+ // Evaluate a Nix expression
52+ let value = eval_state . eval_from_string (" [1 2 3]" , " <example>" )? ;
53+
54+ // Extract typed values
55+ let elements : Vec <_ > = eval_state . require_list_strict (& value )? ;
56+ for element in elements {
57+ let num = eval_state . require_int (& element )? ;
58+ println! (" Element: {}" , num );
59+ }
60+
61+ drop (guard );
62+ Ok (())
63+ }
64+ ```
65+
66+ ## Usage Examples
67+
68+ ### Evaluating Nix Expressions
69+
70+ ``` rust
71+ use nix_bindings_expr :: eval_state :: EvalState ;
72+
73+ // Evaluate and extract different types
74+ let int_value = eval_state . eval_from_string (" 42" , " <example>" )? ;
75+ let num = eval_state . require_int (& int_value )? ;
76+
77+ let str_value = eval_state . eval_from_string (" \ " hello\ "" , " <example>" )? ;
78+ let text = eval_state . require_string (& str_value )? ;
79+
80+ let attr_value = eval_state . eval_from_string (" { x = 1; y = 2; }" , " <example>" )? ;
81+ let attrs = eval_state . require_attrs (& attr_value )? ;
82+ ```
83+
84+ ### Working with Lists
85+
86+ ``` rust
87+ let list_value = eval_state . eval_from_string (" [1 2 3 4 5]" , " <example>" )? ;
88+
89+ // Lazy: check size without evaluating elements
90+ let size = eval_state . require_list_size (& list_value )? ;
91+
92+ // Selective: evaluate only accessed elements
93+ if let Some (first ) = eval_state . require_list_select_idx_strict (& list_value , 0 )? {
94+ let value = eval_state . require_int (& first )? ;
95+ }
96+
97+ // Strict: evaluate all elements
98+ let all_elements : Vec <_ > = eval_state . require_list_strict (& list_value )? ;
99+ ```
100+
101+ ### Thread Safety
102+
103+ Before using ` EvalState ` in a thread, register with the garbage collector:
104+
105+ ``` rust
106+ use nix_bindings_expr :: eval_state :: {init, gc_register_my_thread};
107+
108+ init ()? ; // Once per process
109+ let guard = gc_register_my_thread ()? ; // Once per thread
110+ // ... use EvalState ...
111+ drop (guard ); // Unregister when done
112+ ```
113+
114+ For more examples, see the documentation in each crate's source code.
115+
116+ ## Nix Version Compatibility
117+
118+ The crates use conditional compilation to support multiple Nix versions:
119+
120+ - ** ` nix-bindings-fetchers ` ** requires Nix ≥ 2.29
121+ - Some features in other crates require specific Nix versions
122+
123+ The build system automatically detects the Nix version and enables appropriate features.
124+
125+ ## Integration with Nix Projects
126+
127+ These crates use [ nix-cargo-integration] for seamless integration with Nix builds. To use them in your Nix project:
13128
14129``` nix
15130{
16- outputs =
17- inputs@{ self, flake-parts, ... }:
18- flake-parts.lib.mkFlake { inherit inputs; }
19- {
131+ inputs = {
132+ nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
133+ flake-parts.url = "github:hercules-ci/flake-parts";
134+ nix-cargo-integration.url = "github:90-008/nix-cargo-integration";
135+ nix-bindings-rust.url = "github:nixops4/nix-bindings-rust";
136+ };
137+
138+ outputs = inputs@{ flake-parts, ... }:
139+ flake-parts.lib.mkFlake { inherit inputs; } {
20140 imports = [
21141 inputs.nix-cargo-integration.flakeModule
22142 inputs.nix-bindings-rust.modules.flake.default
23143 ];
24144
25145 perSystem = { config, pkgs, ... }: {
26- # optional:
146+ # Optional: override Nix package
27147 nix-bindings-rust.nixPackage = pkgs.nix;
28148
29149 nci.projects."myproject" = {
@@ -36,18 +156,68 @@ Example usage:
36156}
37157```
38158
39- ## Hacking
159+ See the [ nix-cargo-integration documentation ] [ nix-cargo-integration ] for more options.
40160
41- The following will open a shell with dependencies, and install pre-commit for automatic formatting.
161+ ## Development
162+
163+ ### Getting Started
42164
43165``` console
44166$ nix develop
45167```
46168
47- ### VSCode
169+ ### Building
170+
171+ ``` bash
172+ # Build specific crates (release mode)
173+ nix build .# nix-bindings-store-release
174+ nix build .# nix-bindings-expr-release
175+
176+ # Build with Cargo (in dev shell)
177+ cargo build
178+ cargo build --release
179+ ```
180+
181+ ### Testing
182+
183+ ``` bash
184+ # Run tests for specific crates via Nix (recommended - includes proper store setup)
185+ nix build .# checks.x86_64-linux.nix-bindings-store-tests
186+ nix build .# checks.x86_64-linux.nix-bindings-expr-tests
187+
188+ # Run all checks (tests + clippy + formatting)
189+ nix flake check
190+
191+ # Run tests with Cargo (in dev shell)
192+ cargo test
193+
194+ # Run specific test
195+ cargo test test_name
196+ ```
197+
198+ ### Memory Testing
199+
200+ For FFI memory leak testing with valgrind, see [ doc/hacking/test-ffi.md] ( doc/hacking/test-ffi.md ) .
201+
202+ ### Code Formatting
203+
204+ ``` bash
205+ treefmt
206+ ```
207+
208+ ### IDE Setup
209+
210+ For VSCode, load the dev shell via Nix Env Selector extension or direnv.
211+
212+ ## Documentation
213+
214+ - [ Nix C API Reference] [ C API ]
215+ - [ nix-cargo-integration] [ nix-cargo-integration ]
216+ - [ Hacking Guide] ( doc/hacking/test-ffi.md )
48217
49- #### rust-analyzer
218+ ## License
50219
51- If the rust-analyzer extension fails, make sure the devShell was loaded into VSCode via Nix Env Selector or direnv .
220+ See [ LICENSE ] ( LICENSE ) file in the repository .
52221
53222[ C API ] : https://nix.dev/manual/nix/latest/c-api.html
223+ [ nix-cargo-integration ] : https://github.com/90-008/nix-cargo-integration#readme
0 commit comments