o-santi/nix-forall

Nix bindings for all languages

Rust

44

87 commits

updated Jun 9, 2025

See the code

README

* Nix Forall

Nix FFI for all languages. This is a collection of packages built upon a solid interface for interacting with the nix evaluator in Rust, through the recently added [[https://github.com/NixOS/nix/pull/8699][Nix C-Api]], and a couple of wrapper libraries so that it can be seamlessly accessed from other languages as well.

It is very much in alpha and constantly changing, so don't expect the API's to be stable as of now. Still, the general interface for all libraries should stay relatively intact:
1. Build a nix evaluator instance with given settings, where the default is an empty one. You may pass per-evaluator settings to customize behavior; for instance, like ~extra-experimental-features="flakes"~ is needed in order to call ~builtins.getFlake~, used inside ~state.eval_flake~.
2. Use said nix evaluator to load a nix file, flake or string.
3. Interact with said nix value.

   If it is a "simple" value, ie. string, number, float, it should translate to a builtin value, while lists, attribute sets and functions will have wrappers around them to implement some niceties, like lazy evaluation by default, special accessors and derivation =build()= helpers.

Currently, only Rust and Python libraries are defined.

#+begin_quote
This crate uses [[https://github.com/oxalica/nocargo][nocargo]] to build directly through nix, without the use of cargo. If you want to use cargo, you may drop in the shell (through ~nix develop~).
#+end_quote

** =nix-for-py/=
=nix_for_py= exposes only one function, =nix_evaluator=, that is used to instantiate a nix evaluator, that will be used to evaluate nix code:

#+begin_src python
from nix_for_py import nix_evaluator

evaluator = nix_evaluator(settings={'experimental-features':'flakes'})
nixpkgs = evaluator.eval_flake('github:nixos/nixpkgs')
hello = nixpkgs.legacyPackages['x86_64-linux'].hello.build()
print(hello)
#+end_src

which should print
#+begin_src python
{'out': '/nix/store/26xbg1ndr7hbcncrlf9nhx5is2b25d13-hello-2.12.1'}
#+end_src

*** Install
The best way to utilize it is to use it through the =overlay= exposed by this flake, which will add the =nix_for_py= package to all python versions of nixpkgs.
#+begin_src nix
pkgs = import nixpkgs {
  overlays = [ nix-forall.overlays.${system}.default ];
};
#+end_src
and then create a python instance with it:
#+begin_src nix
pkgs.mkShell {
  buildInputs = [
    (pkgs.python3.withPackages (p: [ p.nix-for-py ]))
  ];
};
#+end_src

** =nix-for-rust/=
The main way to invoke an evaluator is through the =NixSettings= builder. It lets you set settings which customize the behavior of the evaluator, and to finalize it, you must give it a store path, which will return the instance of the evaluator.
#+begin_src rust
use nix_for_rust::settings::NixSettings;

pub fn main() -> anyhow::Result<()> {
  let mut state = NixSettings::default()
    .with_setting("experimental-features", "flakes")
    .with_default_store()?;
  let valid_pkgs = state.eval_flake("github:NixOS/nixpkgs")?
    .get("legacyPackages")?
    .get("x86_64-linux")?
    .items()?
    .filter_map(|(_name, term)| term.ok())
    .count();
  println!("Rejoice! You can build {valid_pkgs} packages from nixpkgs.");
  Ok(())
}
#+end_src

Not written in Markdown, so it's shown here as plain text — view it formatted on GitHub.

Contributors

o-santi

87 commits

o-santi/nix-forall

Nix bindings for all languages

Rust

44

87 commits

updated Jun 9, 2025

See the code

README

* Nix Forall

Nix FFI for all languages. This is a collection of packages built upon a solid interface for interacting with the nix evaluator in Rust, through the recently added [[https://github.com/NixOS/nix/pull/8699][Nix C-Api]], and a couple of wrapper libraries so that it can be seamlessly accessed from other languages as well.

It is very much in alpha and constantly changing, so don't expect the API's to be stable as of now. Still, the general interface for all libraries should stay relatively intact:
1. Build a nix evaluator instance with given settings, where the default is an empty one. You may pass per-evaluator settings to customize behavior; for instance, like ~extra-experimental-features="flakes"~ is needed in order to call ~builtins.getFlake~, used inside ~state.eval_flake~.
2. Use said nix evaluator to load a nix file, flake or string.
3. Interact with said nix value.

   If it is a "simple" value, ie. string, number, float, it should translate to a builtin value, while lists, attribute sets and functions will have wrappers around them to implement some niceties, like lazy evaluation by default, special accessors and derivation =build()= helpers.

Currently, only Rust and Python libraries are defined.

#+begin_quote
This crate uses [[https://github.com/oxalica/nocargo][nocargo]] to build directly through nix, without the use of cargo. If you want to use cargo, you may drop in the shell (through ~nix develop~).
#+end_quote

** =nix-for-py/=
=nix_for_py= exposes only one function, =nix_evaluator=, that is used to instantiate a nix evaluator, that will be used to evaluate nix code:

#+begin_src python
from nix_for_py import nix_evaluator

evaluator = nix_evaluator(settings={'experimental-features':'flakes'})
nixpkgs = evaluator.eval_flake('github:nixos/nixpkgs')
hello = nixpkgs.legacyPackages['x86_64-linux'].hello.build()
print(hello)
#+end_src

which should print
#+begin_src python
{'out': '/nix/store/26xbg1ndr7hbcncrlf9nhx5is2b25d13-hello-2.12.1'}
#+end_src

*** Install
The best way to utilize it is to use it through the =overlay= exposed by this flake, which will add the =nix_for_py= package to all python versions of nixpkgs.
#+begin_src nix
pkgs = import nixpkgs {
  overlays = [ nix-forall.overlays.${system}.default ];
};
#+end_src
and then create a python instance with it:
#+begin_src nix
pkgs.mkShell {
  buildInputs = [
    (pkgs.python3.withPackages (p: [ p.nix-for-py ]))
  ];
};
#+end_src

** =nix-for-rust/=
The main way to invoke an evaluator is through the =NixSettings= builder. It lets you set settings which customize the behavior of the evaluator, and to finalize it, you must give it a store path, which will return the instance of the evaluator.
#+begin_src rust
use nix_for_rust::settings::NixSettings;

pub fn main() -> anyhow::Result<()> {
  let mut state = NixSettings::default()
    .with_setting("experimental-features", "flakes")
    .with_default_store()?;
  let valid_pkgs = state.eval_flake("github:NixOS/nixpkgs")?
    .get("legacyPackages")?
    .get("x86_64-linux")?
    .items()?
    .filter_map(|(_name, term)| term.ok())
    .count();
  println!("Rejoice! You can build {valid_pkgs} packages from nixpkgs.");
  Ok(())
}
#+end_src

Not written in Markdown, so it's shown here as plain text — view it formatted on GitHub.

Contributors

o-santi

87 commits

Languages

Rust

94.7%

Nix

5.1%