From bfa3b3a063ebbd7f9e08bd2e58e702ac64515634 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 8 Feb 2024 15:34:43 +0100 Subject: [PATCH 001/306] feat: README and Flake --- .gitignore | 7 +++ README.md | 14 +++++ flake.lock | 175 +++++++++++++++++++++++++++++++++++++++++++++++++++++ flake.nix | 31 ++++++++++ 4 files changed, 227 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5bc3ddd --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ + +# Nix +result +result-* + +# pre-commit +.pre-commit-config.yaml diff --git a/README.md b/README.md new file mode 100644 index 0000000..49dbb7e --- /dev/null +++ b/README.md @@ -0,0 +1,14 @@ + +# `nix_bindings_*` crates + +Use the Nix [C API] from Rust. + +## Hacking + +The following will open a shell with dependencies, and install pre-commit for automatic formatting. + +```console +$ nix develop +``` + +[C API]: https://nix.dev/manual/nix/latest/c-api.html diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..d7dfcd5 --- /dev/null +++ b/flake.lock @@ -0,0 +1,175 @@ +{ + "nodes": { + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1706830856, + "narHash": "sha256-a0NYyp+h9hlb7ddVz4LUn1vT/PLwqfrWYcHMvFB1xYg=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "b253292d9c0a5ead9bc98c4e9a26c6312e27d69f", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1701680307, + "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "pre-commit-hooks-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1703887061, + "narHash": "sha256-gGPa9qWNc6eCXT/+Z5/zMkyYOuRZqeFZBDbopNZQkuY=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "43e1aa1308018f37118e34d3a9cb4f5e75dc11d5", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1707092692, + "narHash": "sha256-ZbHsm+mGk/izkWtT4xwwqz38fdlwu7nUUKXTOmm4SyE=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "faf912b086576fd1a15fca610166c98d47bc667e", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "dir": "lib", + "lastModified": 1706550542, + "narHash": "sha256-UcsnCG6wx++23yeER4Hg18CXWbgNpqNXcHIo5/1Y+hc=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "97b17f32362e475016f942bbdfda4a4a72a8a652", + "type": "github" + }, + "original": { + "dir": "lib", + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1704874635, + "narHash": "sha256-YWuCrtsty5vVZvu+7BchAxmcYzTMfolSPP5io8+WYCg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "3dc440faeee9e889fe2d1b4d25ad0f430d449356", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-23.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "pre-commit-hooks-nix": { + "inputs": { + "flake-compat": "flake-compat", + "flake-utils": "flake-utils", + "gitignore": "gitignore", + "nixpkgs": [ + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1707297608, + "narHash": "sha256-ADjo/5VySGlvtCW3qR+vdFF4xM9kJFlRDqcC9ZGI8EA=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "0db2e67ee49910adfa13010e7f012149660af7f0", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-parts": "flake-parts", + "nixpkgs": "nixpkgs", + "pre-commit-hooks-nix": "pre-commit-hooks-nix" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..23d86be --- /dev/null +++ b/flake.nix @@ -0,0 +1,31 @@ +{ + description = "A flake with pre-commit hooks"; + + inputs = { + flake-parts.url = "github:hercules-ci/flake-parts"; + flake-parts.inputs.nixpkgs.follows = "nixpkgs"; + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + pre-commit-hooks-nix.url = "github:cachix/pre-commit-hooks.nix"; + pre-commit-hooks-nix.inputs.nixpkgs.follows = "nixpkgs"; + }; + + outputs = inputs@{ self, flake-parts, ... }: + flake-parts.lib.mkFlake + { inherit inputs; } + { + imports = [ + inputs.pre-commit-hooks-nix.flakeModule + ]; + systems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ]; + perSystem = { config, self', inputs', pkgs, ... }: { + pre-commit.settings.hooks.nixpkgs-fmt.enable = true; + devShells.default = pkgs.mkShell { + shellHook = '' + ${config.pre-commit.installationScript} + echo 1>&2 "Welcome to the development shell!" + ''; + }; + }; + flake = { }; + }; +} From a4bdbe5618f5a35c17d6a9e7e6bd55df2e01a9da Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 8 Feb 2024 16:58:45 +0100 Subject: [PATCH 002/306] feat: Rust skeleton (cherry picked from commit 41a09289cda3137f96c69c69c924af34f0e64865) --- .envrc | 1 + .gitignore | 12 +++ .vscode/extensions.json | 6 ++ .vscode/settings.json | 4 + README.md | 6 ++ flake.lock | 226 ++++++++++++++++++++++++++++++++++++---- flake.nix | 21 +++- rust/Cargo.toml | 4 + rust/nci.nix | 6 ++ 9 files changed, 264 insertions(+), 22 deletions(-) create mode 100644 .envrc create mode 100644 .vscode/extensions.json create mode 100644 .vscode/settings.json create mode 100644 rust/Cargo.toml create mode 100644 rust/nci.nix diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore index 5bc3ddd..f5a46ae 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +# direnv +.direnv # Nix result @@ -5,3 +7,13 @@ result-* # pre-commit .pre-commit-config.yaml + +# Rust +**/target + +# VSCode +# We have an opinionated setup for VSCode, so we want to keep the settings in the repo. +!.vscode +!.vscode/settings.json +# Extension recommendations should be kept in the repo. +!.vscode/extensions.json diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..f44d85f --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,6 @@ +{ + "recommendations": [ + "rust-lang.rust", + "mkhl.direnv" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..4022d6d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + // Take from PATH, direnv + "rust-analyzer.server.path": "rust-analyzer" +} \ No newline at end of file diff --git a/README.md b/README.md index 49dbb7e..aa5eb40 100644 --- a/README.md +++ b/README.md @@ -11,4 +11,10 @@ The following will open a shell with dependencies, and install pre-commit for au $ nix develop ``` +### VSCode + +#### rust-analyzer + +If the rust-analyzer extension fails, make sure the devShell was loaded into VSCode via Nix Env Selector or direnv. + [C API]: https://nix.dev/manual/nix/latest/c-api.html diff --git a/flake.lock b/flake.lock index d7dfcd5..fd80b32 100644 --- a/flake.lock +++ b/flake.lock @@ -1,5 +1,45 @@ { "nodes": { + "crane": { + "flake": false, + "locked": { + "lastModified": 1699217310, + "narHash": "sha256-xpW3VFUG7yE6UE6Wl0dhqencuENSkV7qpnpe9I8VbPw=", + "owner": "ipetkov", + "repo": "crane", + "rev": "d535642bbe6f377077f7c23f0febb78b1463f449", + "type": "github" + }, + "original": { + "owner": "ipetkov", + "ref": "v0.15.0", + "repo": "crane", + "type": "github" + } + }, + "dream2nix": { + "inputs": { + "nixpkgs": [ + "nix-cargo-integration", + "nixpkgs" + ], + "purescript-overlay": "purescript-overlay", + "pyproject-nix": "pyproject-nix" + }, + "locked": { + "lastModified": 1707223475, + "narHash": "sha256-tDmoPA3gukJPfjR/pKl6hNcRP7cuEGw6ruoguHS4szo=", + "owner": "nix-community", + "repo": "dream2nix", + "rev": "936208ae7d88a178a0bcf7e6ac21bb6b87f6c8ea", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "dream2nix", + "type": "github" + } + }, "flake-compat": { "flake": false, "locked": { @@ -18,7 +58,9 @@ }, "flake-parts": { "inputs": { - "nixpkgs-lib": "nixpkgs-lib" + "nixpkgs-lib": [ + "nixpkgs" + ] }, "locked": { "lastModified": 1706830856, @@ -73,6 +115,48 @@ "type": "github" } }, + "mk-naked-shell": { + "flake": false, + "locked": { + "lastModified": 1681286841, + "narHash": "sha256-3XlJrwlR0nBiREnuogoa5i1b4+w/XPe0z8bbrJASw0g=", + "owner": "yusdacra", + "repo": "mk-naked-shell", + "rev": "7612f828dd6f22b7fb332cc69440e839d7ffe6bd", + "type": "github" + }, + "original": { + "owner": "yusdacra", + "repo": "mk-naked-shell", + "type": "github" + } + }, + "nix-cargo-integration": { + "inputs": { + "crane": "crane", + "dream2nix": "dream2nix", + "mk-naked-shell": "mk-naked-shell", + "nixpkgs": [ + "nixpkgs" + ], + "parts": "parts", + "rust-overlay": "rust-overlay", + "treefmt": "treefmt" + }, + "locked": { + "lastModified": 1707372711, + "narHash": "sha256-VixqzHPc4VGd3TVcdTSufrpZ6wequ0a8BGSQOuxaFps=", + "owner": "yusdacra", + "repo": "nix-cargo-integration", + "rev": "eddee765f67966646c487f554fc843e8513aad28", + "type": "github" + }, + "original": { + "owner": "yusdacra", + "repo": "nix-cargo-integration", + "type": "github" + } + }, "nixpkgs": { "locked": { "lastModified": 1707092692, @@ -89,24 +173,6 @@ "type": "github" } }, - "nixpkgs-lib": { - "locked": { - "dir": "lib", - "lastModified": 1706550542, - "narHash": "sha256-UcsnCG6wx++23yeER4Hg18CXWbgNpqNXcHIo5/1Y+hc=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "97b17f32362e475016f942bbdfda4a4a72a8a652", - "type": "github" - }, - "original": { - "dir": "lib", - "owner": "NixOS", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, "nixpkgs-stable": { "locked": { "lastModified": 1704874635, @@ -123,6 +189,27 @@ "type": "github" } }, + "parts": { + "inputs": { + "nixpkgs-lib": [ + "nix-cargo-integration", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1706830856, + "narHash": "sha256-a0NYyp+h9hlb7ddVz4LUn1vT/PLwqfrWYcHMvFB1xYg=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "b253292d9c0a5ead9bc98c4e9a26c6312e27d69f", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, "pre-commit-hooks-nix": { "inputs": { "flake-compat": "flake-compat", @@ -147,13 +234,93 @@ "type": "github" } }, + "purescript-overlay": { + "inputs": { + "nixpkgs": [ + "nix-cargo-integration", + "dream2nix", + "nixpkgs" + ], + "slimlock": "slimlock" + }, + "locked": { + "lastModified": 1696022621, + "narHash": "sha256-eMjFmsj2G1E0Q5XiibUNgFjTiSz0GxIeSSzzVdoN730=", + "owner": "thomashoneyman", + "repo": "purescript-overlay", + "rev": "047c7933abd6da8aa239904422e22d190ce55ead", + "type": "github" + }, + "original": { + "owner": "thomashoneyman", + "repo": "purescript-overlay", + "type": "github" + } + }, + "pyproject-nix": { + "flake": false, + "locked": { + "lastModified": 1702448246, + "narHash": "sha256-hFg5s/hoJFv7tDpiGvEvXP0UfFvFEDgTdyHIjDVHu1I=", + "owner": "davhau", + "repo": "pyproject.nix", + "rev": "5a06a2697b228c04dd2f35659b4b659ca74f7aeb", + "type": "github" + }, + "original": { + "owner": "davhau", + "ref": "dream2nix", + "repo": "pyproject.nix", + "type": "github" + } + }, "root": { "inputs": { "flake-parts": "flake-parts", + "nix-cargo-integration": "nix-cargo-integration", "nixpkgs": "nixpkgs", "pre-commit-hooks-nix": "pre-commit-hooks-nix" } }, + "rust-overlay": { + "flake": false, + "locked": { + "lastModified": 1707358215, + "narHash": "sha256-Nuhi8KEJ2e+2nTimSyEIPqN5eh7ECVWd+AnPXG6L+SY=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "dd917bb1b67fc049fd56fe6de70266a9ab74a4aa", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "slimlock": { + "inputs": { + "nixpkgs": [ + "nix-cargo-integration", + "dream2nix", + "purescript-overlay", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1688610262, + "narHash": "sha256-Wg0ViDotFWGWqKIQzyYCgayeH8s4U1OZcTiWTQYdAp4=", + "owner": "thomashoneyman", + "repo": "slimlock", + "rev": "b5c6cdcaf636ebbebd0a1f32520929394493f1a6", + "type": "github" + }, + "original": { + "owner": "thomashoneyman", + "repo": "slimlock", + "type": "github" + } + }, "systems": { "locked": { "lastModified": 1681028828, @@ -168,6 +335,27 @@ "repo": "default", "type": "github" } + }, + "treefmt": { + "inputs": { + "nixpkgs": [ + "nix-cargo-integration", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1707300477, + "narHash": "sha256-qQF0fEkHlnxHcrKIMRzOETnRBksUK048MXkX0SOmxvA=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "ac599dab59a66304eb511af07b3883114f061b9d", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index 23d86be..6268571 100644 --- a/flake.nix +++ b/flake.nix @@ -3,7 +3,9 @@ inputs = { flake-parts.url = "github:hercules-ci/flake-parts"; - flake-parts.inputs.nixpkgs.follows = "nixpkgs"; + flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs"; + nix-cargo-integration.url = "github:yusdacra/nix-cargo-integration"; + nix-cargo-integration.inputs.nixpkgs.follows = "nixpkgs"; nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; pre-commit-hooks-nix.url = "github:cachix/pre-commit-hooks.nix"; pre-commit-hooks-nix.inputs.nixpkgs.follows = "nixpkgs"; @@ -12,14 +14,27 @@ outputs = inputs@{ self, flake-parts, ... }: flake-parts.lib.mkFlake { inherit inputs; } - { + ({ lib, ... }: { imports = [ inputs.pre-commit-hooks-nix.flakeModule + inputs.nix-cargo-integration.flakeModule + ./rust/nci.nix ]; systems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ]; perSystem = { config, self', inputs', pkgs, ... }: { + pre-commit.settings.hooks.nixpkgs-fmt.enable = true; + pre-commit.settings.hooks.rustfmt.enable = true; + # Override to pass `--all` + pre-commit.settings.hooks.rustfmt.entry = lib.mkForce "${pkgs.rustfmt}/bin/cargo-fmt fmt --all --manifest-path ./rust/Cargo.toml -- --color always"; + devShells.default = pkgs.mkShell { + inputsFrom = [ config.nci.outputs.nix-bindings.devShell ]; + nativeBuildInputs = [ + pkgs.rust-analyzer + pkgs.nixpkgs-fmt + pkgs.rustfmt + ]; shellHook = '' ${config.pre-commit.installationScript} echo 1>&2 "Welcome to the development shell!" @@ -27,5 +42,5 @@ }; }; flake = { }; - }; + }); } diff --git a/rust/Cargo.toml b/rust/Cargo.toml new file mode 100644 index 0000000..d21c77c --- /dev/null +++ b/rust/Cargo.toml @@ -0,0 +1,4 @@ +[workspace] +members = [ +] +resolver = "2" diff --git a/rust/nci.nix b/rust/nci.nix new file mode 100644 index 0000000..1fbaa3a --- /dev/null +++ b/rust/nci.nix @@ -0,0 +1,6 @@ +{ + perSystem = { pkgs, config, ... }: { + # https://flake.parts/options/nix-cargo-integration + nci.projects.nix-bindings.path = ./.; + }; +} From 4dca986fe20d4dc3dc5808db390e06092be0629f Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 16 Feb 2024 09:14:15 +0100 Subject: [PATCH 003/306] feat: Add nix overrides to make bindgen work (cherry picked from commit 7caeec86a016fc7427d71ac3713adb2293c2e1ce) --- flake.lock | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++- flake.nix | 22 +++++++++++++++ rust/nci.nix | 31 ++++++++++++++++++++-- 3 files changed, 125 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index fd80b32..049d278 100644 --- a/flake.lock +++ b/flake.lock @@ -41,6 +41,22 @@ } }, "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-compat_2": { "flake": false, "locked": { "lastModified": 1696426674, @@ -115,6 +131,22 @@ "type": "github" } }, + "libgit2": { + "flake": false, + "locked": { + "lastModified": 1697646580, + "narHash": "sha256-oX4Z3S9WtJlwvj0uH9HlYcWv+x1hqp8mhXl7HsLu2f0=", + "owner": "libgit2", + "repo": "libgit2", + "rev": "45fd9ed7ae1a9b74b957ef4f337bc3c8b3df01b5", + "type": "github" + }, + "original": { + "owner": "libgit2", + "repo": "libgit2", + "type": "github" + } + }, "mk-naked-shell": { "flake": false, "locked": { @@ -131,6 +163,30 @@ "type": "github" } }, + "nix": { + "inputs": { + "flake-compat": "flake-compat", + "libgit2": "libgit2", + "nixpkgs": [ + "nixpkgs" + ], + "nixpkgs-regression": "nixpkgs-regression" + }, + "locked": { + "lastModified": 1705358697, + "narHash": "sha256-1SjVVJKIXfSoymtIBCx8mPkhw6LpyihrtzMXdFFOH2M=", + "owner": "tweag", + "repo": "nix", + "rev": "eb456d68bd22e1296c2eca30fa06281de0da289e", + "type": "github" + }, + "original": { + "owner": "tweag", + "ref": "nix-c-bindings", + "repo": "nix", + "type": "github" + } + }, "nix-cargo-integration": { "inputs": { "crane": "crane", @@ -173,6 +229,22 @@ "type": "github" } }, + "nixpkgs-regression": { + "locked": { + "lastModified": 1643052045, + "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + } + }, "nixpkgs-stable": { "locked": { "lastModified": 1704874635, @@ -212,7 +284,7 @@ }, "pre-commit-hooks-nix": { "inputs": { - "flake-compat": "flake-compat", + "flake-compat": "flake-compat_2", "flake-utils": "flake-utils", "gitignore": "gitignore", "nixpkgs": [ @@ -277,6 +349,7 @@ "root": { "inputs": { "flake-parts": "flake-parts", + "nix": "nix", "nix-cargo-integration": "nix-cargo-integration", "nixpkgs": "nixpkgs", "pre-commit-hooks-nix": "pre-commit-hooks-nix" diff --git a/flake.nix b/flake.nix index 6268571..b833505 100644 --- a/flake.nix +++ b/flake.nix @@ -4,6 +4,8 @@ inputs = { flake-parts.url = "github:hercules-ci/flake-parts"; flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs"; + nix.url = "github:tweag/nix/nix-c-bindings"; + nix.inputs.nixpkgs.follows = "nixpkgs"; nix-cargo-integration.url = "github:yusdacra/nix-cargo-integration"; nix-cargo-integration.inputs.nixpkgs.follows = "nixpkgs"; nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; @@ -23,22 +25,42 @@ systems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ]; perSystem = { config, self', inputs', pkgs, ... }: { + + packages.nix = inputs'.nix.packages.nix.overrideAttrs { + # checkPhase does not seem to terminate. + # TODO: remove override + doCheck = false; + }; + pre-commit.settings.hooks.nixpkgs-fmt.enable = true; pre-commit.settings.hooks.rustfmt.enable = true; # Override to pass `--all` pre-commit.settings.hooks.rustfmt.entry = lib.mkForce "${pkgs.rustfmt}/bin/cargo-fmt fmt --all --manifest-path ./rust/Cargo.toml -- --color always"; devShells.default = pkgs.mkShell { + name = "nix-bindings-devshell"; + strictDeps = true; inputsFrom = [ config.nci.outputs.nix-bindings.devShell ]; + inherit (config.nci.outputs.nix-bindings.devShell.env) + LIBCLANG_PATH + BINDGEN_EXTRA_CLANG_ARGS + ; + buildInputs = [ + config.packages.nix + ]; nativeBuildInputs = [ pkgs.rust-analyzer pkgs.nixpkgs-fmt pkgs.rustfmt + pkgs.pkg-config + pkgs.clang-tools # clangd ]; shellHook = '' ${config.pre-commit.installationScript} echo 1>&2 "Welcome to the development shell!" ''; + # rust-analyzer needs a NIX_PATH for some reason + NIX_PATH = "nixpkgs=${inputs.nixpkgs}"; }; }; flake = { }; diff --git a/rust/nci.nix b/rust/nci.nix index 1fbaa3a..bbdd766 100644 --- a/rust/nci.nix +++ b/rust/nci.nix @@ -1,6 +1,33 @@ { - perSystem = { pkgs, config, ... }: { + perSystem = { lib, config, pkgs, ... }: { # https://flake.parts/options/nix-cargo-integration - nci.projects.nix-bindings.path = ./.; + nci.projects.nix-bindings = { + path = ./.; + drvConfig = { + mkDerivation = { + buildInputs = [ + config.packages.nix + # stdbool.h + pkgs.stdenv.cc + ]; + nativeBuildInputs = [ + pkgs.pkg-config + ]; + }; + # NOTE: duplicated in flake.nix devShell + env = { + LIBCLANG_PATH = + if pkgs.stdenv.cc.isClang then + null # don't set the variable + else + lib.makeLibraryPath [ pkgs.buildPackages.llvmPackages.clang-unwrapped ]; + BINDGEN_EXTRA_CLANG_ARGS = + if pkgs.stdenv.cc.isClang then + null # don't set the variable + else + "-I${lib.getDev pkgs.stdenv.cc.cc}/lib/gcc/${pkgs.stdenv.hostPlatform.config}/${pkgs.stdenv.cc.cc.version}/include"; + }; + }; + }; }; } From 858bec68bc3e1b60c1fbd09e8b3b2a58e73e764a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 16 Feb 2024 09:45:29 +0100 Subject: [PATCH 004/306] feat: Add crate nix-c-raw See README. Most of this is in accordance with the bindgen introduction. The c_headers function was taken from Zach Mitchell's work on bindings. Co-authored-by: Zach Mitchell (cherry picked from commit 83d8ae6b26d341bbd452a77d7361f58c06a2d322) --- rust/Cargo.lock | 443 +++++++++++++++++++++++++++++ rust/Cargo.toml | 1 + rust/nix-c-raw/Cargo.toml | 11 + rust/nix-c-raw/README.md | 12 + rust/nix-c-raw/build.rs | 42 +++ rust/nix-c-raw/include/nix-c-raw.h | 3 + rust/nix-c-raw/src/lib.rs | 5 + 7 files changed, 517 insertions(+) create mode 100644 rust/Cargo.lock create mode 100644 rust/nix-c-raw/Cargo.toml create mode 100644 rust/nix-c-raw/README.md create mode 100644 rust/nix-c-raw/build.rs create mode 100644 rust/nix-c-raw/include/nix-c-raw.h create mode 100644 rust/nix-c-raw/src/lib.rs diff --git a/rust/Cargo.lock b/rust/Cargo.lock new file mode 100644 index 0000000..b987df7 --- /dev/null +++ b/rust/Cargo.lock @@ -0,0 +1,443 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "bindgen" +version = "0.69.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", + "which", +] + +[[package]] +name = "bitflags" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clang-sys" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "either" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "libloading" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nix-c-raw" +version = "0.1.0" +dependencies = [ + "bindgen", + "pkg-config", +] + +[[package]] +name = "nixops4" +version = "0.1.0" +dependencies = [ + "nixops4-core", +] + +[[package]] +name = "nixops4-core" +version = "0.1.0" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "prettyplease" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustix" +version = "0.38.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "syn" +version = "2.0.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915aea9e586f80826ee59f8453c1101f9d1c4b3964cd2460185ee8e299ada496" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index d21c77c..d4737e3 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,4 +1,5 @@ [workspace] members = [ + "nix-c-raw", ] resolver = "2" diff --git a/rust/nix-c-raw/Cargo.toml b/rust/nix-c-raw/Cargo.toml new file mode 100644 index 0000000..462a09e --- /dev/null +++ b/rust/nix-c-raw/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "nix-c-raw" +version = "0.1.0" +edition = "2021" +build = "build.rs" + +[lib] + +[build-dependencies] +bindgen = "0.69.4" +pkg-config = "0.3.30" diff --git a/rust/nix-c-raw/README.md b/rust/nix-c-raw/README.md new file mode 100644 index 0000000..dc9d8b4 --- /dev/null +++ b/rust/nix-c-raw/README.md @@ -0,0 +1,12 @@ +# nix-c-raw + +This crate contains generated bindings for the Nix C API. +**You should not have to use this crate directly,** and so you should probably not add it to your dependencies. +Instead, use the `nix-util`, `nix-store` and `nix-expr` crates, which _should_ be sufficient. + +## Design + +Rust bindgen currently does not allow "layered" libraries to be split into separate crates. +For example, the expr crate would have all-new types that are distinct and incompatible with the store crate. + +Ideally bindgen will support reusing already generated modules, and we could move the code generation into the appropriate crates, so that the system dependencies of each crate become accurate. diff --git a/rust/nix-c-raw/build.rs b/rust/nix-c-raw/build.rs new file mode 100644 index 0000000..d8c6679 --- /dev/null +++ b/rust/nix-c-raw/build.rs @@ -0,0 +1,42 @@ +use bindgen; +use std::env; +use std::path::PathBuf; + +fn main() { + // Tell cargo to invalidate the built crate whenever the wrapper changes + println!("cargo:rerun-if-changed=include/nix-c-raw.h"); + + // https://rust-lang.github.io/rust-bindgen/library-usage.html + let bindings = bindgen::Builder::default() + .header("include/nix-c-raw.h") + // Find the includes + .clang_args(c_headers()) + // Tell cargo to invalidate the built crate whenever any of the + // included header files changed. + .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) + // Finish the builder and generate the bindings. + .generate() + // Unwrap the Result and panic on failure. + .expect("Unable to generate bindings"); + + // Write the bindings to the $OUT_DIR/bindings.rs file. + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + bindings + .write_to_file(out_path.join("bindings.rs")) + .expect("Couldn't write bindings!"); +} + +fn c_headers() -> Vec { + let mut args = Vec::new(); + // args.push("-isystem".to_string()); + for path in pkg_config::probe_library("nix-expr-c") + .unwrap() + .include_paths + .iter() + { + args.push(format!("-I{}", path.to_str().unwrap())); + } + // write to stderr for debugging + eprintln!("c_headers: {:?}", args); + args +} diff --git a/rust/nix-c-raw/include/nix-c-raw.h b/rust/nix-c-raw/include/nix-c-raw.h new file mode 100644 index 0000000..81883f6 --- /dev/null +++ b/rust/nix-c-raw/include/nix-c-raw.h @@ -0,0 +1,3 @@ +#include +#include +#include diff --git a/rust/nix-c-raw/src/lib.rs b/rust/nix-c-raw/src/lib.rs new file mode 100644 index 0000000..a38a13a --- /dev/null +++ b/rust/nix-c-raw/src/lib.rs @@ -0,0 +1,5 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); From 61975ac8e4039e949a0d1eae2d3a2deb7a55b264 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 30 Sep 2025 18:39:04 +0200 Subject: [PATCH 005/306] temp: Disable rustfmt pre-commit hook --- flake.nix | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/flake.nix b/flake.nix index b833505..d4728d0 100644 --- a/flake.nix +++ b/flake.nix @@ -33,9 +33,10 @@ }; pre-commit.settings.hooks.nixpkgs-fmt.enable = true; - pre-commit.settings.hooks.rustfmt.enable = true; + # Temporarily disable rustfmt due to configuration issues + # pre-commit.settings.hooks.rustfmt.enable = true; # Override to pass `--all` - pre-commit.settings.hooks.rustfmt.entry = lib.mkForce "${pkgs.rustfmt}/bin/cargo-fmt fmt --all --manifest-path ./rust/Cargo.toml -- --color always"; + # pre-commit.settings.hooks.rustfmt.entry = lib.mkForce "${pkgs.rustfmt}/bin/cargo-fmt fmt --all --manifest-path ./rust/Cargo.toml -- --color always"; devShells.default = pkgs.mkShell { name = "nix-bindings-devshell"; From 7ef434a6d74a4200239f2f2a65aa9a8d2564b755 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 16 Feb 2024 09:59:51 +0100 Subject: [PATCH 006/306] feat: Add nix-util crate with Context wrapper (cherry picked from commit 6c8e116e8bbaecce2b77fd9e0db89f366b57f9b6) --- rust/Cargo.lock | 14 ++++++++++ rust/Cargo.toml | 1 + rust/nix-util/Cargo.toml | 10 +++++++ rust/nix-util/src/context.rs | 54 ++++++++++++++++++++++++++++++++++++ rust/nix-util/src/lib.rs | 1 + 5 files changed, 80 insertions(+) create mode 100644 rust/nix-util/Cargo.toml create mode 100644 rust/nix-util/src/context.rs create mode 100644 rust/nix-util/src/lib.rs diff --git a/rust/Cargo.lock b/rust/Cargo.lock index b987df7..9ae3c80 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -11,6 +11,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "anyhow" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" + [[package]] name = "bindgen" version = "0.69.4" @@ -166,6 +172,14 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "nix-util" +version = "0.1.0" +dependencies = [ + "anyhow", + "nix-c-raw", +] + [[package]] name = "nixops4" version = "0.1.0" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index d4737e3..32f4bfb 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,5 +1,6 @@ [workspace] members = [ "nix-c-raw", + "nix-util", ] resolver = "2" diff --git a/rust/nix-util/Cargo.toml b/rust/nix-util/Cargo.toml new file mode 100644 index 0000000..db15880 --- /dev/null +++ b/rust/nix-util/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "nix-util" +version = "0.1.0" +edition = "2021" + +[lib] + +[dependencies] +anyhow = "1.0.79" +nix-c-raw = { path = "../nix-c-raw" } diff --git a/rust/nix-util/src/context.rs b/rust/nix-util/src/context.rs new file mode 100644 index 0000000..a74fab0 --- /dev/null +++ b/rust/nix-util/src/context.rs @@ -0,0 +1,54 @@ +use anyhow::{bail, Result}; +use nix_c_raw as raw; +use std::ptr::null_mut; +use std::ptr::NonNull; + +pub struct Context { + inner: NonNull, +} + +impl Context { + pub fn new() -> Self { + let ctx = unsafe { raw::nix_c_context_create() }; + if ctx.is_null() { + panic!("nix_c_context_create returned a null pointer"); + } + let ctx = Context { + inner: NonNull::new(ctx).unwrap(), + }; + ctx + } + pub fn ptr(&self) -> *mut raw::nix_c_context { + self.inner.as_ptr() + } + pub fn check_err(&self) -> Result<()> { + let err = unsafe { raw::nix_err_code(self.inner.as_ptr()) }; + if err != raw::NIX_OK.try_into().unwrap() { + // msgp is a borrowed pointer, so we don't need to free it + let msgp = unsafe { raw::nix_err_msg(null_mut(), self.inner.as_ptr(), null_mut()) }; + // Turn the i8 pointer into a Rust string by copying + let msg: &str = unsafe { core::ffi::CStr::from_ptr(msgp).to_str()? }; + bail!("{}", msg); + } + Ok(()) + } +} + +impl Drop for Context { + fn drop(&mut self) { + unsafe { + raw::nix_c_context_free(self.inner.as_ptr()); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn context_new_and_drop() { + // don't crash + let _c = Context::new(); + } +} diff --git a/rust/nix-util/src/lib.rs b/rust/nix-util/src/lib.rs new file mode 100644 index 0000000..9efb2ab --- /dev/null +++ b/rust/nix-util/src/lib.rs @@ -0,0 +1 @@ +pub mod context; From 290f2654e1fcbcc3dfaca1cda4e004361da28b55 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 16 Feb 2024 11:05:24 +0100 Subject: [PATCH 007/306] feat: Add manual testing of bindings with Valgrind (cherry picked from commit 12fed067bf230d3258db172afe648ec690ba584c) --- doc/hacking/test-ffi.md | 25 +++++++++++++++++++++++++ flake.nix | 5 +++++ 2 files changed, 30 insertions(+) create mode 100644 doc/hacking/test-ffi.md diff --git a/doc/hacking/test-ffi.md b/doc/hacking/test-ffi.md new file mode 100644 index 0000000..2ae260e --- /dev/null +++ b/doc/hacking/test-ffi.md @@ -0,0 +1,25 @@ + +# Testing FFI code + +If `cargo-valgrind` is broken, you may run `valgrind` manually. + +1. `cd rust; cargo test -v` +2. find the relevant test suite executable in the log + - example: `/home/user/src/nix-bindings-rust/rust/target/debug/deps/nix_util-036ec381a9e3fd6d` +3. `valgrind --leak-check=full ` +4. check that + - `definitely lost: 0 bytes in 0 blocks` + +## Paranoid check + +Although normal valgrind tends to catch things, you may choose to enable `--show-leak-kinds=all`. +This will print a few false positive. + +Acceptable leaks are those involving (and this may be Linux-specific) +- `call_init`: static initializers + - `nix::GlobalConfig::Register::Register` + - `_GLOBAL__sub_I_logging.cc` + - ... +- `new`: a leak in the rust test framework + +When in doubt, compare the log to a run with your new test case commented out. diff --git a/flake.nix b/flake.nix index d4728d0..35d15f4 100644 --- a/flake.nix +++ b/flake.nix @@ -55,6 +55,11 @@ pkgs.rustfmt pkgs.pkg-config pkgs.clang-tools # clangd + pkgs.valgrind + # TODO: set up cargo-valgrind in shell and build + # currently both this and `cargo install cargo-valgrind` + # produce a binary that says ENOENT. + # pkgs.cargo-valgrind ]; shellHook = '' ${config.pre-commit.installationScript} From 57bb9b9d92d92ed85fed5b7c319a657584ad83f3 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 22 Feb 2024 22:31:20 +0100 Subject: [PATCH 008/306] dev: VSCode LSP rust -> rust-analyzer (cherry picked from commit 77acea0b187ec7f29658ca420dfde14aa2b4135a) --- .vscode/extensions.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.vscode/extensions.json b/.vscode/extensions.json index f44d85f..38d06b1 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,6 +1,6 @@ { "recommendations": [ - "rust-lang.rust", - "mkhl.direnv" + "mkhl.direnv", + "rust-lang.rust-analyzer" ] } From 270ccaaa57fe93c303e9b600d4f827f2aefe770d Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 22 Feb 2024 22:32:50 +0100 Subject: [PATCH 009/306] feat: Add create nix-store (cherry picked from commit b9150e82be712b2cd82d689f0f26c2a133564701) --- rust/Cargo.lock | 10 +++ rust/Cargo.toml | 1 + rust/nix-store/Cargo.toml | 12 ++++ rust/nix-store/src/lib.rs | 1 + rust/nix-store/src/store.rs | 129 ++++++++++++++++++++++++++++++++++++ 5 files changed, 153 insertions(+) create mode 100644 rust/nix-store/Cargo.toml create mode 100644 rust/nix-store/src/lib.rs create mode 100644 rust/nix-store/src/store.rs diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 9ae3c80..9ab8dd3 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -172,6 +172,16 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "nix-store" +version = "0.1.0" +dependencies = [ + "anyhow", + "lazy_static", + "nix-c-raw", + "nix-util", +] + [[package]] name = "nix-util" version = "0.1.0" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 32f4bfb..e3b6b73 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -2,5 +2,6 @@ members = [ "nix-c-raw", "nix-util", + "nix-store", ] resolver = "2" diff --git a/rust/nix-store/Cargo.toml b/rust/nix-store/Cargo.toml new file mode 100644 index 0000000..3af19d4 --- /dev/null +++ b/rust/nix-store/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "nix-store" +version = "0.1.0" +edition = "2021" + +[lib] + +[dependencies] +anyhow = "1.0.79" +nix-util = { path = "../nix-util" } +nix-c-raw = { path = "../nix-c-raw" } +lazy_static = "1.4.0" diff --git a/rust/nix-store/src/lib.rs b/rust/nix-store/src/lib.rs new file mode 100644 index 0000000..55c88cb --- /dev/null +++ b/rust/nix-store/src/lib.rs @@ -0,0 +1 @@ +pub mod store; diff --git a/rust/nix-store/src/store.rs b/rust/nix-store/src/store.rs new file mode 100644 index 0000000..8650dcc --- /dev/null +++ b/rust/nix-store/src/store.rs @@ -0,0 +1,129 @@ +use anyhow::{bail, Result}; +use lazy_static::lazy_static; +use nix_c_raw as raw; +use nix_util::context::Context; +use std::ffi::CString; +use std::mem::MaybeUninit; +use std::ptr::null_mut; +use std::ptr::NonNull; + +/* TODO make Nix itself thread safe */ +lazy_static! { + static ref INIT: Result<()> = { + unsafe { + let context: Context = Context::new(); + raw::nix_libstore_init(context.ptr()); + context.check_err() + } + }; +} + +struct StoreRef { + inner: NonNull, +} +impl StoreRef { + pub fn ptr(&self) -> *mut raw::Store { + self.inner.as_ptr() + } +} +impl Drop for StoreRef { + fn drop(&mut self) { + unsafe { + raw::nix_store_unref(self.inner.as_ptr()); + } + } +} + +pub struct Store { + inner: StoreRef, + /* An error context to reuse. This way we don't have to allocate them for each store operation. */ + context: Context, +} +impl Store { + pub fn open(url: &str) -> Result { + let x = INIT.as_ref(); + match x { + Ok(_) => {} + Err(e) => { + // Couldn't just clone the error, so we have to print it here. + bail!("nix_libstore_init error: {}", e); + } + } + + let context: Context = Context::new(); + + let uri_ptr = CString::new(url)?; + let store = unsafe { + raw::nix_store_open( + context.ptr(), + uri_ptr.as_ptr(), + null_mut::<*mut *const i8>(), + ) + }; + context.check_err()?; + if store.is_null() { + bail!("nix_c_store_open returned a null pointer"); + } + let store = Store { + inner: StoreRef { + inner: NonNull::new(store).unwrap(), + }, + context, + }; + Ok(store) + } + + pub fn get_uri(&self) -> Result { + const N: usize = 1024; + let mut buffer: [MaybeUninit; N] = unsafe { MaybeUninit::uninit().assume_init() }; + unsafe { + raw::nix_store_get_uri( + self.context.ptr(), + self.inner.ptr(), + buffer.as_mut_ptr() as *mut i8, + N as u32, + ) + }; + self.context.check_err()?; + unsafe { + // copy the c string from buffer + let cstr = core::ffi::CStr::from_ptr(buffer.as_ptr() as *const i8); + cstr.to_str().map(|s| s.to_string()).map_err(|e| e.into()) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn auto_works() { + let res = Store::open("auto"); + assert!(res.is_ok()); + } + + #[test] + fn invalid_uri_fails() { + let res = Store::open("invalid://uri"); + assert!(res.is_err()); + } + + #[test] + fn get_uri() { + let store = Store::open("auto").unwrap(); + let uri = store.get_uri().unwrap(); + assert!(!uri.is_empty()); + // must be ascii + assert!(uri.is_ascii()); + // usually something like "daemon", but that's not something we can check here. + println!("uri: {}", uri); + } + + #[test] + fn get_uri_nixos_cache() { + let store = Store::open("https://cache.nixos.org/").unwrap(); + let uri = store.get_uri().unwrap(); + assert_eq!(uri, "https://cache.nixos.org"); + } +} From 0d7146cb391b0faa5a8107729ad29f7292d81081 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 7 Mar 2024 10:56:24 +0100 Subject: [PATCH 010/306] feat: nix-c-raw: Generate value api bindings (cherry picked from commit 3c75dea63233670863f30547f90dd71498d547b0) --- flake.nix | 2 ++ rust/nix-c-raw/build.rs | 7 +++++++ rust/nix-c-raw/include/nix-c-raw.h | 1 + 3 files changed, 10 insertions(+) diff --git a/flake.nix b/flake.nix index 35d15f4..3b14cd2 100644 --- a/flake.nix +++ b/flake.nix @@ -49,6 +49,8 @@ buildInputs = [ config.packages.nix ]; + # Workaround: the gcc in the devshell doesn't find libc headers + RUST_NIX_C_RAW_EXTRA_CFLAGS = "-I${pkgs.stdenv.cc.libc.dev}/include"; nativeBuildInputs = [ pkgs.rust-analyzer pkgs.nixpkgs-fmt diff --git a/rust/nix-c-raw/build.rs b/rust/nix-c-raw/build.rs index d8c6679..ae37b02 100644 --- a/rust/nix-c-raw/build.rs +++ b/rust/nix-c-raw/build.rs @@ -36,6 +36,13 @@ fn c_headers() -> Vec { { args.push(format!("-I{}", path.to_str().unwrap())); } + + if let Ok(cflags) = std::env::var("RUST_NIX_C_RAW_EXTRA_CFLAGS") { + for flag in cflags.split_whitespace() { + args.push(flag.to_string()); + } + } + // write to stderr for debugging eprintln!("c_headers: {:?}", args); args diff --git a/rust/nix-c-raw/include/nix-c-raw.h b/rust/nix-c-raw/include/nix-c-raw.h index 81883f6..129181e 100644 --- a/rust/nix-c-raw/include/nix-c-raw.h +++ b/rust/nix-c-raw/include/nix-c-raw.h @@ -1,3 +1,4 @@ #include #include #include +#include From be8aa55a3870cfdeeab5139abaf6fccb783a3c4e Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 15 Mar 2024 18:29:26 +0100 Subject: [PATCH 011/306] dev: Add indentation rules to .vscode (cherry picked from commit 6849a8836c1120b4a11cd2511126271b1e815cfe) --- .vscode/settings.json | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 4022d6d..79f97da 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,16 @@ { // Take from PATH, direnv - "rust-analyzer.server.path": "rust-analyzer" + "rust-analyzer.server.path": "rust-analyzer", + + "[nix]": { + "editor.tabSize": 2, + "editor.insertSpaces": true, + "editor.detectIndentation": false + }, + + "[rust]": { + "editor.tabSize": 4, + "editor.insertSpaces": true, + "editor.detectIndentation": false + }, } \ No newline at end of file From 6978e91fa90d36a82769373acf4d8da761a97bf2 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 19 Mar 2024 14:43:01 +0100 Subject: [PATCH 012/306] feat: Add nix-expr library, update nix (cherry picked from commit bf1ddd1c9137578b86895577d5b80e0c5771b605) --- flake.lock | 6 +- flake.nix | 7 +- rust/Cargo.lock | 22 +++ rust/Cargo.toml | 1 + rust/nix-c-raw/build.rs | 8 + rust/nix-c-raw/include/nix-c-raw.h | 2 + rust/nix-expr/Cargo.toml | 14 ++ rust/nix-expr/src/eval_state.rs | 265 +++++++++++++++++++++++++++++ rust/nix-expr/src/lib.rs | 2 + rust/nix-expr/src/value.rs | 75 ++++++++ rust/nix-store/src/store.rs | 6 +- 11 files changed, 399 insertions(+), 9 deletions(-) create mode 100644 rust/nix-expr/Cargo.toml create mode 100644 rust/nix-expr/src/eval_state.rs create mode 100644 rust/nix-expr/src/lib.rs create mode 100644 rust/nix-expr/src/value.rs diff --git a/flake.lock b/flake.lock index 049d278..11269fb 100644 --- a/flake.lock +++ b/flake.lock @@ -173,11 +173,11 @@ "nixpkgs-regression": "nixpkgs-regression" }, "locked": { - "lastModified": 1705358697, - "narHash": "sha256-1SjVVJKIXfSoymtIBCx8mPkhw6LpyihrtzMXdFFOH2M=", + "lastModified": 1709227987, + "narHash": "sha256-ndFevohurD6MQCCBnOdHLDmsJ3vfpF5+cKizXvq5vmw=", "owner": "tweag", "repo": "nix", - "rev": "eb456d68bd22e1296c2eca30fa06281de0da289e", + "rev": "0fd441d0bf6331a1152cdc091724b4648d187f90", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 3b14cd2..d36075c 100644 --- a/flake.nix +++ b/flake.nix @@ -26,11 +26,8 @@ perSystem = { config, self', inputs', pkgs, ... }: { - packages.nix = inputs'.nix.packages.nix.overrideAttrs { - # checkPhase does not seem to terminate. - # TODO: remove override - doCheck = false; - }; + + packages.nix = inputs'.nix.packages.nix; pre-commit.settings.hooks.nixpkgs-fmt.enable = true; # Temporarily disable rustfmt due to configuration issues diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 9ab8dd3..0b9d59f 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -72,6 +72,16 @@ dependencies = [ "libloading", ] +[[package]] +name = "ctor" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad291aa74992b9b7a7e88c38acbbf6ad7e107f1d90ee8775b7bc1fc3394f485c" +dependencies = [ + "quote", + "syn", +] + [[package]] name = "either" version = "1.10.0" @@ -172,6 +182,18 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "nix-expr" +version = "0.1.0" +dependencies = [ + "anyhow", + "ctor", + "lazy_static", + "nix-c-raw", + "nix-store", + "nix-util", +] + [[package]] name = "nix-store" version = "0.1.0" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index e3b6b73..011675b 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ "nix-c-raw", + "nix-expr", "nix-util", "nix-store", ] diff --git a/rust/nix-c-raw/build.rs b/rust/nix-c-raw/build.rs index ae37b02..1ac9dec 100644 --- a/rust/nix-c-raw/build.rs +++ b/rust/nix-c-raw/build.rs @@ -37,6 +37,14 @@ fn c_headers() -> Vec { args.push(format!("-I{}", path.to_str().unwrap())); } + for path in pkg_config::probe_library("bdw-gc") + .unwrap() + .include_paths + .iter() + { + args.push(format!("-I{}", path.to_str().unwrap())); + } + if let Ok(cflags) = std::env::var("RUST_NIX_C_RAW_EXTRA_CFLAGS") { for flag in cflags.split_whitespace() { args.push(flag.to_string()); diff --git a/rust/nix-c-raw/include/nix-c-raw.h b/rust/nix-c-raw/include/nix-c-raw.h index 129181e..14fb03c 100644 --- a/rust/nix-c-raw/include/nix-c-raw.h +++ b/rust/nix-c-raw/include/nix-c-raw.h @@ -1,4 +1,6 @@ #include #include +#define GC_THREADS +#include #include #include diff --git a/rust/nix-expr/Cargo.toml b/rust/nix-expr/Cargo.toml new file mode 100644 index 0000000..66a1f25 --- /dev/null +++ b/rust/nix-expr/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "nix-expr" +version = "0.1.0" +edition = "2021" + +[lib] + +[dependencies] +anyhow = "1.0.79" +nix-store = { path = "../nix-store" } +nix-util = { path = "../nix-util" } +nix-c-raw = { path = "../nix-c-raw" } +lazy_static = "1.4.0" +ctor = "0.2.7" \ No newline at end of file diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs new file mode 100644 index 0000000..e1ec8a0 --- /dev/null +++ b/rust/nix-expr/src/eval_state.rs @@ -0,0 +1,265 @@ +use crate::value::{Value, ValueType}; +use anyhow::Context as _; +use anyhow::{bail, Result}; +use lazy_static::lazy_static; +use nix_c_raw as raw; +use nix_store::store::Store; +use nix_util::context::Context; +use std::ffi::CString; +use std::ptr::null_mut; +use std::ptr::NonNull; + +lazy_static! { + static ref INIT: Result<()> = { + unsafe { + raw::GC_allow_register_threads(); + } + let context: Context = Context::new(); + unsafe { + raw::nix_libexpr_init(context.ptr()); + } + context.check_err()?; + Ok(()) + }; +} +pub fn init() -> Result<()> { + let x = INIT.as_ref(); + match x { + Ok(_) => Ok(()), + Err(e) => { + // Couldn't just clone the error, so we have to print it here. + Err(anyhow::format_err!("nix_libstore_init error: {}", e)) + } + } +} + +pub struct EvalState { + eval_state: NonNull, + store: Store, + context: Context, +} +impl EvalState { + pub fn new(store: Store) -> Result { + let context = Context::new(); + + init()?; + + let eval_state = unsafe { + raw::nix_state_create( + context.ptr(), + /* searchPath */ null_mut(), + store.raw_ptr(), + ) + }; + if eval_state.is_null() { + bail!("nix_state_create returned a null pointer"); + } + Ok(EvalState { + eval_state: NonNull::new(eval_state).unwrap(), + store, + context, + }) + } + pub fn raw_ptr(&self) -> *mut raw::EvalState { + self.eval_state.as_ptr() + } + pub fn store(&self) -> &Store { + &self.store + } + pub fn eval_from_string(&self, expr: String, path: String) -> Result { + let expr_ptr = + CString::new(expr).with_context(|| "eval_from_string: expr contains null byte")?; + let path_ptr = + CString::new(path).with_context(|| "eval_from_string: path contains null byte")?; + let value = self.new_value_uninitialized(); + unsafe { + let ctx_ptr = self.context.ptr(); + raw::nix_expr_eval_from_string( + ctx_ptr, + self.raw_ptr(), + expr_ptr.as_ptr(), + path_ptr.as_ptr(), + value.raw_ptr(), + ); + }; + self.context.check_err()?; + Ok(value) + } + /** Try turn any Value into a Value that isn't a Thunk. */ + pub fn force(&self, v: &Value) -> Result<()> { + unsafe { + raw::nix_value_force(self.context.ptr(), self.raw_ptr(), v.raw_ptr()); + } + self.context.check_err() + } + pub fn value_is_thunk(&self, value: &Value) -> bool { + let r = unsafe { + raw::nix_get_type(self.context.ptr(), value.raw_ptr()) == raw::ValueType_NIX_TYPE_THUNK + }; + self.context.check_err().unwrap(); + r + } + pub fn value_type(&self, value: &Value) -> Result { + if self.value_is_thunk(value) { + self.force(value)?; + } + let r = unsafe { raw::nix_get_type(self.context.ptr(), value.raw_ptr()) }; + Ok(ValueType::from_raw(r)) + } + + fn new_value_uninitialized(&self) -> Value { + let value = unsafe { raw::nix_alloc_value(self.context.ptr(), self.raw_ptr()) }; + Value::new(value) + } +} + +pub fn gc_now() { + unsafe { + raw::nix_gc_now(); + } +} + +/** Run a function while making sure that the current thread is registered with the GC. */ +pub fn gc_registering_current_thread(f: F) -> Result +where + F: FnOnce() -> R, +{ + init()?; + if unsafe { raw::GC_thread_is_registered() } != 0 { + return Ok(f()); + } else { + gc_register_my_thread().unwrap(); + let r = f(); + unsafe { + raw::GC_unregister_my_thread(); + } + return Ok(r); + } +} + +pub fn gc_register_my_thread() -> Result<()> { + unsafe { + let already_done = raw::GC_thread_is_registered(); + if already_done != 0 { + return Ok(()); + } + let mut sb: raw::GC_stack_base = raw::GC_stack_base { + mem_base: 0 as *mut _, + }; + let r = raw::GC_get_stack_base(&mut sb); + if r as u32 != raw::GC_SUCCESS { + Err(anyhow::format_err!("GC_get_stack_base failed: {}", r))?; + } + raw::GC_register_my_thread(&sb); + Ok(()) + } +} + +impl Drop for EvalState { + fn drop(&mut self) { + unsafe { + raw::nix_state_free(self.raw_ptr()); + } + } +} + +#[cfg(test)] +mod tests { + use ctor::ctor; + + use super::*; + + #[ctor] + fn setup() { + init().unwrap(); + } + + #[test] + fn eval_state_new_and_drop() { + gc_registering_current_thread(|| { + // very basic test: make sure initialization doesn't crash + let store = Store::open("auto").unwrap(); + let _e = EvalState::new(store).unwrap(); + }) + .unwrap(); + } + + #[test] + fn eval_state_eval_from_string() { + gc_registering_current_thread(|| { + let store = Store::open("auto").unwrap(); + let es = EvalState::new(store).unwrap(); + let v = es + .eval_from_string("1".to_string(), "".to_string()) + .unwrap(); + let v2 = v.clone(); + es.force(&v).unwrap(); + let t = es.value_type(&v).unwrap(); + assert!(t == ValueType::Int); + let t2 = es.value_type(&v2).unwrap(); + assert!(t2 == ValueType::Int); + gc_now(); + }) + .unwrap(); + } + + #[test] + fn eval_state_value_bool() { + gc_registering_current_thread(|| { + let store = Store::open("auto").unwrap(); + let es = EvalState::new(store).unwrap(); + let v = es + .eval_from_string("true".to_string(), "".to_string()) + .unwrap(); + es.force(&v).unwrap(); + let t = es.value_type(&v).unwrap(); + assert!(t == ValueType::Bool); + }) + .unwrap(); + } + + #[test] + fn eval_state_value_string() { + gc_registering_current_thread(|| { + let store = Store::open("auto").unwrap(); + let es = EvalState::new(store).unwrap(); + let v = es + .eval_from_string("\"hello\"".to_string(), "".to_string()) + .unwrap(); + es.force(&v).unwrap(); + let t = es.value_type(&v).unwrap(); + assert!(t == ValueType::String); + }) + .unwrap(); + } + + #[test] + fn eval_state_value_attrset() { + gc_registering_current_thread(|| { + let store = Store::open("auto").unwrap(); + let es = EvalState::new(store).unwrap(); + let v = es + .eval_from_string("{ }".to_string(), "".to_string()) + .unwrap(); + es.force(&v).unwrap(); + let t = es.value_type(&v).unwrap(); + assert!(t == ValueType::AttrSet); + }) + .unwrap(); + } + + #[test] + fn eval_state_value_list() { + gc_registering_current_thread(|| { + let store = Store::open("auto").unwrap(); + let es = EvalState::new(store).unwrap(); + let v = es + .eval_from_string("[ ]".to_string(), "".to_string()) + .unwrap(); + es.force(&v).unwrap(); + let t = es.value_type(&v).unwrap(); + assert!(t == ValueType::List); + }) + .unwrap(); + } +} diff --git a/rust/nix-expr/src/lib.rs b/rust/nix-expr/src/lib.rs new file mode 100644 index 0000000..ead2024 --- /dev/null +++ b/rust/nix-expr/src/lib.rs @@ -0,0 +1,2 @@ +pub mod eval_state; +pub mod value; diff --git a/rust/nix-expr/src/value.rs b/rust/nix-expr/src/value.rs new file mode 100644 index 0000000..cbe1164 --- /dev/null +++ b/rust/nix-expr/src/value.rs @@ -0,0 +1,75 @@ +use nix_c_raw as raw; +use nix_util::context::Context; +use std::ptr::NonNull; + +// TODO: test: cloning a thunk does not duplicate the evaluation. + +/** The type of a value (or thunk) */ +#[derive(Eq, PartialEq)] +pub enum ValueType { + AttrSet, + Bool, + External, + Float, + Function, + Int, + List, + Null, + Path, + String, + Thunk, + Unknown, +} + +impl ValueType { + pub(crate) fn from_raw(raw: raw::ValueType) -> ValueType { + match raw { + raw::ValueType_NIX_TYPE_ATTRS => ValueType::AttrSet, + raw::ValueType_NIX_TYPE_BOOL => ValueType::Bool, + raw::ValueType_NIX_TYPE_EXTERNAL => ValueType::External, + raw::ValueType_NIX_TYPE_FLOAT => ValueType::Float, + raw::ValueType_NIX_TYPE_FUNCTION => ValueType::Function, + raw::ValueType_NIX_TYPE_INT => ValueType::Int, + raw::ValueType_NIX_TYPE_LIST => ValueType::List, + raw::ValueType_NIX_TYPE_NULL => ValueType::Null, + raw::ValueType_NIX_TYPE_PATH => ValueType::Path, + raw::ValueType_NIX_TYPE_STRING => ValueType::String, + raw::ValueType_NIX_TYPE_THUNK => ValueType::Thunk, + _ => ValueType::Unknown, + } + } +} + +/* A pointer to a value or thunk, to be used with EvalState methods. */ +pub struct Value { + inner: NonNull, +} +impl Value { + pub(crate) fn new(inner: *mut raw::Value) -> Self { + Value { + inner: NonNull::new(inner).unwrap(), + } + } + pub(crate) fn raw_ptr(&self) -> *mut raw::Value { + self.inner.as_ptr() + } +} +impl Drop for Value { + fn drop(&mut self) { + let context = Context::new(); + unsafe { + raw::nix_gc_decref(context.ptr(), self.inner.as_ptr()); + } + // ignore error from context, because drop should not panic + } +} +impl Clone for Value { + fn clone(&self) -> Self { + let context = Context::new(); + unsafe { raw::nix_gc_incref(context.ptr(), self.inner.as_ptr()) }; + context.check_err().unwrap(); + Value { inner: self.inner } + } +} + +// Tested in eval_state.rs diff --git a/rust/nix-store/src/store.rs b/rust/nix-store/src/store.rs index 8650dcc..c1d274c 100644 --- a/rust/nix-store/src/store.rs +++ b/rust/nix-store/src/store.rs @@ -29,7 +29,7 @@ impl StoreRef { impl Drop for StoreRef { fn drop(&mut self) { unsafe { - raw::nix_store_unref(self.inner.as_ptr()); + raw::nix_store_free(self.inner.as_ptr()); } } } @@ -73,6 +73,10 @@ impl Store { Ok(store) } + pub fn raw_ptr(&self) -> *mut raw::Store { + self.inner.ptr() + } + pub fn get_uri(&self) -> Result { const N: usize = 1024; let mut buffer: [MaybeUninit; N] = unsafe { MaybeUninit::uninit().assume_init() }; From 15b8ce951addf8ea9ea51cb789f40a935a00a306 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 19 Mar 2024 14:43:57 +0100 Subject: [PATCH 013/306] dev: Add gdb to devshell (cherry picked from commit 159880b57ce5ca8545ee4625deacb17b9dd3b74f) --- flake.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/flake.nix b/flake.nix index d36075c..da2a041 100644 --- a/flake.nix +++ b/flake.nix @@ -55,6 +55,7 @@ pkgs.pkg-config pkgs.clang-tools # clangd pkgs.valgrind + pkgs.gdb # TODO: set up cargo-valgrind in shell and build # currently both this and `cargo install cargo-valgrind` # produce a binary that says ENOENT. From 4f12dc012720921db3ffe38fa5abd24a202751d5 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 19 Mar 2024 15:08:52 +0100 Subject: [PATCH 014/306] dev: Basic CI config (cherry picked from commit de4a5a82a4fbdcc9bd786c09dc0f40c8b38a498f) --- flake.nix | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index da2a041..3505f8a 100644 --- a/flake.nix +++ b/flake.nix @@ -69,6 +69,8 @@ NIX_PATH = "nixpkgs=${inputs.nixpkgs}"; }; }; - flake = { }; + flake = { + herculesCI.ciSystems = [ "x86_64-linux" ]; + }; }); } From 08781aeef75909d42bc14f304855cde77c8d1a4e Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 19 Mar 2024 16:30:24 +0100 Subject: [PATCH 015/306] dev: Fix pre-commit check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'pre-commit-hooks-nix': 'github:cachix/pre-commit-hooks.nix/0db2e67ee49910adfa13010e7f012149660af7f0' (2024-02-07) → 'github:hercules-ci/pre-commit-hooks.nix/d43e4853f578739ac2264eadcd18faa5aeb41889' (2024-03-19) (cherry picked from commit 32a59f0f81b70a95ec7fda598b5823e96d3f8270) --- flake.lock | 11 ++++++----- flake.nix | 9 +++++---- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/flake.lock b/flake.lock index 11269fb..258898c 100644 --- a/flake.lock +++ b/flake.lock @@ -293,15 +293,16 @@ "nixpkgs-stable": "nixpkgs-stable" }, "locked": { - "lastModified": 1707297608, - "narHash": "sha256-ADjo/5VySGlvtCW3qR+vdFF4xM9kJFlRDqcC9ZGI8EA=", - "owner": "cachix", + "lastModified": 1710862133, + "narHash": "sha256-+e62qOvW6svSU59SOVniBodNe0WkIYgfwXPazUyEBNE=", + "owner": "hercules-ci", "repo": "pre-commit-hooks.nix", - "rev": "0db2e67ee49910adfa13010e7f012149660af7f0", + "rev": "d43e4853f578739ac2264eadcd18faa5aeb41889", "type": "github" }, "original": { - "owner": "cachix", + "owner": "hercules-ci", + "ref": "rustfmt-all", "repo": "pre-commit-hooks.nix", "type": "github" } diff --git a/flake.nix b/flake.nix index 3505f8a..43c4b8d 100644 --- a/flake.nix +++ b/flake.nix @@ -9,7 +9,8 @@ nix-cargo-integration.url = "github:yusdacra/nix-cargo-integration"; nix-cargo-integration.inputs.nixpkgs.follows = "nixpkgs"; nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; - pre-commit-hooks-nix.url = "github:cachix/pre-commit-hooks.nix"; + # https://github.com/cachix/pre-commit-hooks.nix/pull/410 + pre-commit-hooks-nix.url = "github:hercules-ci/pre-commit-hooks.nix/rustfmt-all"; pre-commit-hooks-nix.inputs.nixpkgs.follows = "nixpkgs"; }; @@ -26,14 +27,14 @@ perSystem = { config, self', inputs', pkgs, ... }: { - + packages.nix = inputs'.nix.packages.nix; pre-commit.settings.hooks.nixpkgs-fmt.enable = true; # Temporarily disable rustfmt due to configuration issues # pre-commit.settings.hooks.rustfmt.enable = true; - # Override to pass `--all` - # pre-commit.settings.hooks.rustfmt.entry = lib.mkForce "${pkgs.rustfmt}/bin/cargo-fmt fmt --all --manifest-path ./rust/Cargo.toml -- --color always"; + # New configuration for rustfmt + pre-commit.settings.settings.rust.cargoManifestPath = "./rust/Cargo.toml"; devShells.default = pkgs.mkShell { name = "nix-bindings-devshell"; From caff9a20195096fc3119febcf82821b5925c0758 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 19 Mar 2024 17:18:18 +0100 Subject: [PATCH 016/306] maint: Fix nix-packaged build (cherry picked from commit 9535898dc4fbe466addf7fde47a6cafc3d267039) --- rust/nci.nix | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rust/nci.nix b/rust/nci.nix index bbdd766..2385516 100644 --- a/rust/nci.nix +++ b/rust/nci.nix @@ -25,7 +25,9 @@ if pkgs.stdenv.cc.isClang then null # don't set the variable else - "-I${lib.getDev pkgs.stdenv.cc.cc}/lib/gcc/${pkgs.stdenv.hostPlatform.config}/${pkgs.stdenv.cc.cc.version}/include"; + "-I${pkgs.stdenv.cc.libc.dev}/include" + + " -I${lib.getDev pkgs.stdenv.cc.cc}/lib/gcc/${pkgs.stdenv.hostPlatform.config}/${pkgs.stdenv.cc.cc.version}/include" + ; }; }; }; From 9021f55a5337945e124a58faf1926b06cb428a64 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 19 Mar 2024 17:22:23 +0100 Subject: [PATCH 017/306] maint: Remove RUST_NIX_C_RAW_EXTRA_CFLAGS The standard bindgen flag BINDGEN_EXTRA_CLANG_ARGS seems to do the same thing. (cherry picked from commit 599eece08bee28d72c4ec20c8578428c158a19c1) --- flake.nix | 2 -- rust/nix-c-raw/build.rs | 6 ------ 2 files changed, 8 deletions(-) diff --git a/flake.nix b/flake.nix index 43c4b8d..03e8712 100644 --- a/flake.nix +++ b/flake.nix @@ -47,8 +47,6 @@ buildInputs = [ config.packages.nix ]; - # Workaround: the gcc in the devshell doesn't find libc headers - RUST_NIX_C_RAW_EXTRA_CFLAGS = "-I${pkgs.stdenv.cc.libc.dev}/include"; nativeBuildInputs = [ pkgs.rust-analyzer pkgs.nixpkgs-fmt diff --git a/rust/nix-c-raw/build.rs b/rust/nix-c-raw/build.rs index 1ac9dec..25251a0 100644 --- a/rust/nix-c-raw/build.rs +++ b/rust/nix-c-raw/build.rs @@ -45,12 +45,6 @@ fn c_headers() -> Vec { args.push(format!("-I{}", path.to_str().unwrap())); } - if let Ok(cflags) = std::env::var("RUST_NIX_C_RAW_EXTRA_CFLAGS") { - for flag in cflags.split_whitespace() { - args.push(flag.to_string()); - } - } - // write to stderr for debugging eprintln!("c_headers: {:?}", args); args From 38aab588fdc8aca74dac3c864c0751d88eb213eb Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 19 Mar 2024 18:01:05 +0100 Subject: [PATCH 018/306] dev: Use relocated store in Nix-packaged tests (cherry picked from commit 3bb16f6745cc2eb6f1474b73947d0fe2c20788e2) --- rust/nci.nix | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/rust/nci.nix b/rust/nci.nix index 2385516..08edb9f 100644 --- a/rust/nci.nix +++ b/rust/nci.nix @@ -13,6 +13,31 @@ nativeBuildInputs = [ pkgs.pkg-config ]; + # Prepare the environment for Nix to work. + # Nix does not provide a suitable environment for running itself in + # the sandbox - not by default. We configure it to use a relocated store. + preCheck = '' + # nix needs a home directory + export HOME="$(mktemp -d $TMPDIR/home.XXXXXX)" + + # configure a relocated store + store_data=$(mktemp -d $TMPDIR/store-data.XXXXXX) + export NIX_REMOTE="$store_data" + export NIX_BUILD_HOOK= + export NIX_CONF_DIR=$store_data/etc + export NIX_LOCALSTATE_DIR=$store_data/nix/var + export NIX_LOG_DIR=$store_data/nix/var/log/nix + export NIX_STATE_DIR=$store_data/nix/var/nix + + echo "Configuring relocated store at $NIX_REMOTE..." + + # Init ahead of time, because concurrent initialization is flaky + ${# Not using nativeBuildInputs because this should (hopefully) be + # the only place where we need a nix binary. Let's stay in control. + pkgs.buildPackages.nix}/bin/nix-store --init + + echo "Store initialized." + ''; }; # NOTE: duplicated in flake.nix devShell env = { From 36ea74c707326e70ae337c2ba3564dbbe0ed5bc7 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 19 Mar 2024 18:01:23 +0100 Subject: [PATCH 019/306] dev: Use unwrap to see error message in test (cherry picked from commit c1c86ebee4418d37053add884b9c1258b22dd7ed) --- rust/nix-store/src/store.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/nix-store/src/store.rs b/rust/nix-store/src/store.rs index c1d274c..7f6f930 100644 --- a/rust/nix-store/src/store.rs +++ b/rust/nix-store/src/store.rs @@ -104,7 +104,7 @@ mod tests { #[test] fn auto_works() { let res = Store::open("auto"); - assert!(res.is_ok()); + res.unwrap(); } #[test] From dbab66b38d9b011a847a97920b1ec02f45710b50 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 19 Mar 2024 18:02:04 +0100 Subject: [PATCH 020/306] maint: Ignore get_uri_nixos_cache because of network access (cherry picked from commit 89736dd18452d11b11e4581a549da1df956d81c6) --- rust/nix-store/src/store.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/rust/nix-store/src/store.rs b/rust/nix-store/src/store.rs index 7f6f930..420e022 100644 --- a/rust/nix-store/src/store.rs +++ b/rust/nix-store/src/store.rs @@ -125,6 +125,7 @@ mod tests { } #[test] + #[ignore] // Needs network access fn get_uri_nixos_cache() { let store = Store::open("https://cache.nixos.org/").unwrap(); let uri = store.get_uri().unwrap(); From be1c6bef6b5cbccdd2cc55853bf806f6da3af706 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 20 Mar 2024 14:21:23 +0100 Subject: [PATCH 021/306] maint: Update pre-commit-hooks-nix (cherry picked from commit dc8985d5bf77c76ee4b2a0816d314e9e2c7f5318) --- flake.lock | 11 +++++------ flake.nix | 3 +-- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/flake.lock b/flake.lock index 258898c..9c15db1 100644 --- a/flake.lock +++ b/flake.lock @@ -293,16 +293,15 @@ "nixpkgs-stable": "nixpkgs-stable" }, "locked": { - "lastModified": 1710862133, - "narHash": "sha256-+e62qOvW6svSU59SOVniBodNe0WkIYgfwXPazUyEBNE=", - "owner": "hercules-ci", + "lastModified": 1710923068, + "narHash": "sha256-6hOpUiuxuwpXXc/xfJsBUJeqqgGI+JMJuLo45aG3cKc=", + "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "d43e4853f578739ac2264eadcd18faa5aeb41889", + "rev": "e611897ddfdde3ed3eaac4758635d7177ff78673", "type": "github" }, "original": { - "owner": "hercules-ci", - "ref": "rustfmt-all", + "owner": "cachix", "repo": "pre-commit-hooks.nix", "type": "github" } diff --git a/flake.nix b/flake.nix index 03e8712..ed97fa8 100644 --- a/flake.nix +++ b/flake.nix @@ -9,8 +9,7 @@ nix-cargo-integration.url = "github:yusdacra/nix-cargo-integration"; nix-cargo-integration.inputs.nixpkgs.follows = "nixpkgs"; nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; - # https://github.com/cachix/pre-commit-hooks.nix/pull/410 - pre-commit-hooks-nix.url = "github:hercules-ci/pre-commit-hooks.nix/rustfmt-all"; + pre-commit-hooks-nix.url = "github:cachix/pre-commit-hooks.nix"; pre-commit-hooks-nix.inputs.nixpkgs.follows = "nixpkgs"; }; From 032a23c3068ac6ff1ffd1bf7d8f91afcf06fdeb1 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 4 Apr 2024 15:25:21 +0200 Subject: [PATCH 022/306] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nix': 'github:tweag/nix/0fd441d0bf6331a1152cdc091724b4648d187f90' (2024-02-29) → 'github:tweag/nix/926fbadcc30a4614b5f5a3d18a6f4096914f97da' (2024-03-29) (cherry picked from commit a317f66f19270f40c18006377f36ad43950198ef) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 9c15db1..c9b0f69 100644 --- a/flake.lock +++ b/flake.lock @@ -173,11 +173,11 @@ "nixpkgs-regression": "nixpkgs-regression" }, "locked": { - "lastModified": 1709227987, - "narHash": "sha256-ndFevohurD6MQCCBnOdHLDmsJ3vfpF5+cKizXvq5vmw=", + "lastModified": 1711717219, + "narHash": "sha256-8nzsGORuUIkM91yKYz9f8/+eRMrLRHrqs+ExwLT01+8=", "owner": "tweag", "repo": "nix", - "rev": "0fd441d0bf6331a1152cdc091724b4648d187f90", + "rev": "926fbadcc30a4614b5f5a3d18a6f4096914f97da", "type": "github" }, "original": { From 759577526d30b114b96f37522d983ade90d2f9bb Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 4 Apr 2024 15:24:31 +0200 Subject: [PATCH 023/306] maint: Update get_uri to be callback based (cherry picked from commit 1aaf9b4d764da5aa8c1297d87ad1002bf1d7a7ea) --- rust/nix-store/src/store.rs | 15 +++------ rust/nix-util/src/lib.rs | 1 + rust/nix-util/src/string_return.rs | 54 ++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 10 deletions(-) create mode 100644 rust/nix-util/src/string_return.rs diff --git a/rust/nix-store/src/store.rs b/rust/nix-store/src/store.rs index 420e022..9304a6f 100644 --- a/rust/nix-store/src/store.rs +++ b/rust/nix-store/src/store.rs @@ -2,8 +2,8 @@ use anyhow::{bail, Result}; use lazy_static::lazy_static; use nix_c_raw as raw; use nix_util::context::Context; +use nix_util::string_return::callback_get_vec_u8; use std::ffi::CString; -use std::mem::MaybeUninit; use std::ptr::null_mut; use std::ptr::NonNull; @@ -78,22 +78,17 @@ impl Store { } pub fn get_uri(&self) -> Result { - const N: usize = 1024; - let mut buffer: [MaybeUninit; N] = unsafe { MaybeUninit::uninit().assume_init() }; + let mut raw_buffer: Vec = Vec::new(); unsafe { raw::nix_store_get_uri( self.context.ptr(), self.inner.ptr(), - buffer.as_mut_ptr() as *mut i8, - N as u32, + callback_get_vec_u8 as *mut std::ffi::c_void, + &mut raw_buffer as *mut Vec as *mut std::ffi::c_void, ) }; self.context.check_err()?; - unsafe { - // copy the c string from buffer - let cstr = core::ffi::CStr::from_ptr(buffer.as_ptr() as *const i8); - cstr.to_str().map(|s| s.to_string()).map_err(|e| e.into()) - } + String::from_utf8(raw_buffer).map_err(|e| e.into()) } } diff --git a/rust/nix-util/src/lib.rs b/rust/nix-util/src/lib.rs index 9efb2ab..0306c93 100644 --- a/rust/nix-util/src/lib.rs +++ b/rust/nix-util/src/lib.rs @@ -1 +1,2 @@ pub mod context; +pub mod string_return; diff --git a/rust/nix-util/src/string_return.rs b/rust/nix-util/src/string_return.rs new file mode 100644 index 0000000..ee62e22 --- /dev/null +++ b/rust/nix-util/src/string_return.rs @@ -0,0 +1,54 @@ +/// Callback for nix_store_get_uri and other functions that return a string. +/// +/// This function is used by the other nix_* crates, and you should never need to call it yourself. +pub unsafe extern "C" fn callback_get_vec_u8( + start: *const ::std::os::raw::c_char, + n: std::os::raw::c_uint, + user_data: *mut std::os::raw::c_void, +) { + let ret = user_data as *mut Vec; + let slice = std::slice::from_raw_parts(start as *const u8, n as usize); + if !(*ret).is_empty() { + panic!("callback_get_vec_u8: slice must be empty. Were we called twice?"); + } + (*ret).extend_from_slice(slice); +} + +#[cfg(test)] +mod tests { + use super::*; + use nix_c_raw as raw; + + /// Typecheck the function signature against the generated bindings in nix_c_raw. + static _CALLBACK_GET_VEC_U8: raw::nix_get_string_callback = Some(callback_get_vec_u8); + + #[test] + fn test_callback_get_vec_u8_empty() { + let mut ret: Vec = Vec::new(); + let start: *const std::os::raw::c_char = std::ptr::null(); + let n: std::os::raw::c_uint = 0; + let user_data: *mut std::os::raw::c_void = + &mut ret as *mut Vec as *mut std::os::raw::c_void; + + unsafe { + callback_get_vec_u8(start, n, user_data); + } + + assert_eq!(ret, vec![]); + } + + #[test] + fn test_callback_get_vec_u8() { + let mut ret: Vec = Vec::new(); + let start: *const std::os::raw::c_char = b"helloGARBAGE".as_ptr() as *const i8; + let n: std::os::raw::c_uint = 5; + let user_data: *mut std::os::raw::c_void = + &mut ret as *mut Vec as *mut std::os::raw::c_void; + + unsafe { + callback_get_vec_u8(start, n, user_data); + } + + assert_eq!(ret, b"hello".to_vec()); + } +} From af8672838625d9db797cd9474496470f5910019e Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 4 Apr 2024 16:38:12 +0200 Subject: [PATCH 024/306] feat: EvalState.require_string (cherry picked from commit ac29248e10d6e2364138d11196151a9f445b379d) --- rust/nix-expr/src/eval_state.rs | 102 ++++++++++++++++++++++++++++++++ rust/nix-expr/src/value.rs | 2 +- 2 files changed, 103 insertions(+), 1 deletion(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index e1ec8a0..a5ed53a 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -106,6 +106,24 @@ impl EvalState { let r = unsafe { raw::nix_get_type(self.context.ptr(), value.raw_ptr()) }; Ok(ValueType::from_raw(r)) } + /// Not exposed, because the caller must always explicitly handle the context or not accept one at all. + fn get_string(&self, value: &Value) -> Result { + let c_str_raw = unsafe { raw::nix_get_string(self.context.ptr(), value.raw_ptr()) }; + self.context.check_err()?; + let cstring = unsafe { std::ffi::CStr::from_ptr(c_str_raw) }; + let str = cstring + .to_str() + .map_err(|e| anyhow::format_err!("Nix string is not valid UTF-8: {}", e))?; + Ok(str.to_owned()) + } + /// NOTE: this will be replaced by two methods, one that also returns the context, and one that checks that the context is empty + pub fn require_string(&self, value: &Value) -> Result { + let t = self.value_type(value)?; + if t != ValueType::String { + bail!("expected a string, but got a {:?}", t); + } + self.get_string(value) + } fn new_value_uninitialized(&self) -> Value { let value = unsafe { raw::nix_alloc_value(self.context.ptr(), self.raw_ptr()) }; @@ -229,6 +247,90 @@ mod tests { es.force(&v).unwrap(); let t = es.value_type(&v).unwrap(); assert!(t == ValueType::String); + let s = es.require_string(&v).unwrap(); + assert!(s == "hello"); + }) + .unwrap(); + } + + #[test] + fn eval_state_value_string_unexpected_bool() { + gc_registering_current_thread(|| { + let store = Store::open("auto").unwrap(); + let es = EvalState::new(store).unwrap(); + let v = es + .eval_from_string("true".to_string(), "".to_string()) + .unwrap(); + es.force(&v).unwrap(); + let r = es.require_string(&v); + assert!(r.is_err()); + // TODO: safe print value (like Nix would) + assert_eq!( + r.unwrap_err().to_string(), + "expected a string, but got a Bool" + ); + }) + .unwrap() + } + + #[test] + fn eval_state_value_string_unexpected_path_value() { + gc_registering_current_thread(|| { + let store = Store::open("auto").unwrap(); + let es = EvalState::new(store).unwrap(); + let v = es + .eval_from_string("/foo".to_string(), "".to_string()) + .unwrap(); + es.force(&v).unwrap(); + let r = es.require_string(&v); + assert!(r.is_err()); + assert_eq!( + r.unwrap_err().to_string(), + "expected a string, but got a Path" + ); + }) + .unwrap() + } + + #[test] + fn eval_state_value_string_bad_utf() { + gc_registering_current_thread(|| { + let store = Store::open("auto").unwrap(); + let es = EvalState::new(store).unwrap(); + let v = es + .eval_from_string( + "builtins.substring 0 1 \"ü\"".to_string(), + "".to_string(), + ) + .unwrap(); + es.force(&v).unwrap(); + let t = es.value_type(&v).unwrap(); + assert!(t == ValueType::String); + let r = es.require_string(&v); + assert!(r.is_err()); + assert!(r + .unwrap_err() + .to_string() + .contains("Nix string is not valid UTF-8")); + }) + .unwrap(); + } + + #[test] + fn eval_state_value_string_unexpected_context() { + gc_registering_current_thread(|| { + let store = Store::open("auto").unwrap(); + let es = EvalState::new(store).unwrap(); + let v = es + .eval_from_string("(derivation { name = \"hello\"; system = \"dummy\"; builder = \"cmd.exe\"; }).outPath".to_string(), "".to_string()) + .unwrap(); + es.force(&v).unwrap(); + let t = es.value_type(&v).unwrap(); + assert!(t == ValueType::String); + // TODO + // let r = es.require_string_without_context(&v); + // assert!(r.is_err()); + // assert!(r.unwrap_err().to_string().contains("unexpected context")); }) .unwrap(); } diff --git a/rust/nix-expr/src/value.rs b/rust/nix-expr/src/value.rs index cbe1164..0f4ec13 100644 --- a/rust/nix-expr/src/value.rs +++ b/rust/nix-expr/src/value.rs @@ -5,7 +5,7 @@ use std::ptr::NonNull; // TODO: test: cloning a thunk does not duplicate the evaluation. /** The type of a value (or thunk) */ -#[derive(Eq, PartialEq)] +#[derive(Eq, PartialEq, Debug)] pub enum ValueType { AttrSet, Bool, From 9d26e36b857a5893bf2b6c5b12ef6eb5d09ff1f9 Mon Sep 17 00:00:00 2001 From: Erin van der Veen Date: Tue, 23 Apr 2024 13:23:42 +0200 Subject: [PATCH 025/306] feat: update nix input to latest master The nix c bindings have now merged into master, with some small changes since these bindings were created. This commit updates the input, and adjusts it to the new API. (cherry picked from commit aa02380bfcc6221615f65152de60cf4cca3bbbbe) --- flake.lock | 103 +++++++++++++++++++++++++++++--- flake.nix | 2 +- rust/nix-expr/src/eval_state.rs | 18 ++++-- rust/nix-store/src/store.rs | 2 +- 4 files changed, 109 insertions(+), 16 deletions(-) diff --git a/flake.lock b/flake.lock index c9b0f69..bca20aa 100644 --- a/flake.lock +++ b/flake.lock @@ -92,10 +92,49 @@ "type": "github" } }, + "flake-parts_2": { + "inputs": { + "nixpkgs-lib": [ + "nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1712014858, + "narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "9126214d0a59633752a136528f5f3b9aa8565b7d", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, "flake-utils": { "inputs": { "systems": "systems" }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, "locked": { "lastModified": 1701680307, "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", @@ -166,23 +205,24 @@ "nix": { "inputs": { "flake-compat": "flake-compat", + "flake-parts": "flake-parts_2", "libgit2": "libgit2", "nixpkgs": [ "nixpkgs" ], - "nixpkgs-regression": "nixpkgs-regression" + "nixpkgs-regression": "nixpkgs-regression", + "pre-commit-hooks": "pre-commit-hooks" }, "locked": { - "lastModified": 1711717219, - "narHash": "sha256-8nzsGORuUIkM91yKYz9f8/+eRMrLRHrqs+ExwLT01+8=", - "owner": "tweag", + "lastModified": 1713862243, + "narHash": "sha256-mfJSQyO7je+/WSBmnl/LTGvqrzv3k1F0kEd7Wg+VXw4=", + "owner": "nixos", "repo": "nix", - "rev": "926fbadcc30a4614b5f5a3d18a6f4096914f97da", + "rev": "1cfc9da472f8dcfa7f521e544531d5e4daf2076c", "type": "github" }, "original": { - "owner": "tweag", - "ref": "nix-c-bindings", + "owner": "nixos", "repo": "nix", "type": "github" } @@ -282,10 +322,42 @@ "type": "github" } }, + "pre-commit-hooks": { + "inputs": { + "flake-compat": [ + "nix" + ], + "flake-utils": "flake-utils", + "gitignore": [ + "nix" + ], + "nixpkgs": [ + "nix", + "nixpkgs" + ], + "nixpkgs-stable": [ + "nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1713775815, + "narHash": "sha256-Wu9cdYTnGQQwtT20QQMg7jzkANKQjwBD9iccfGKkfls=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "2ac4dcbf55ed43f3be0bae15e181f08a57af24a4", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, "pre-commit-hooks-nix": { "inputs": { "flake-compat": "flake-compat_2", - "flake-utils": "flake-utils", + "flake-utils": "flake-utils_2", "gitignore": "gitignore", "nixpkgs": [ "nixpkgs" @@ -409,6 +481,21 @@ "type": "github" } }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, "treefmt": { "inputs": { "nixpkgs": [ diff --git a/flake.nix b/flake.nix index ed97fa8..af50710 100644 --- a/flake.nix +++ b/flake.nix @@ -4,7 +4,7 @@ inputs = { flake-parts.url = "github:hercules-ci/flake-parts"; flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs"; - nix.url = "github:tweag/nix/nix-c-bindings"; + nix.url = "github:nixos/nix"; nix.inputs.nixpkgs.follows = "nixpkgs"; nix-cargo-integration.url = "github:yusdacra/nix-cargo-integration"; nix-cargo-integration.inputs.nixpkgs.follows = "nixpkgs"; diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index a5ed53a..37458b1 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -5,6 +5,7 @@ use lazy_static::lazy_static; use nix_c_raw as raw; use nix_store::store::Store; use nix_util::context::Context; +use nix_util::string_return::callback_get_vec_u8; use std::ffi::CString; use std::ptr::null_mut; use std::ptr::NonNull; @@ -108,13 +109,18 @@ impl EvalState { } /// Not exposed, because the caller must always explicitly handle the context or not accept one at all. fn get_string(&self, value: &Value) -> Result { - let c_str_raw = unsafe { raw::nix_get_string(self.context.ptr(), value.raw_ptr()) }; + let mut raw_buffer: Vec = Vec::new(); + unsafe { + raw::nix_get_string( + self.context.ptr(), + value.raw_ptr(), + Some(callback_get_vec_u8), + &mut raw_buffer as *mut Vec as *mut std::ffi::c_void, + ) + }; self.context.check_err()?; - let cstring = unsafe { std::ffi::CStr::from_ptr(c_str_raw) }; - let str = cstring - .to_str() - .map_err(|e| anyhow::format_err!("Nix string is not valid UTF-8: {}", e))?; - Ok(str.to_owned()) + String::from_utf8(raw_buffer) + .map_err(|e| anyhow::format_err!("Nix string is not valid UTF-8: {}", e)) } /// NOTE: this will be replaced by two methods, one that also returns the context, and one that checks that the context is empty pub fn require_string(&self, value: &Value) -> Result { diff --git a/rust/nix-store/src/store.rs b/rust/nix-store/src/store.rs index 9304a6f..6b568dd 100644 --- a/rust/nix-store/src/store.rs +++ b/rust/nix-store/src/store.rs @@ -83,7 +83,7 @@ impl Store { raw::nix_store_get_uri( self.context.ptr(), self.inner.ptr(), - callback_get_vec_u8 as *mut std::ffi::c_void, + Some(callback_get_vec_u8), &mut raw_buffer as *mut Vec as *mut std::ffi::c_void, ) }; From 9d05ce49e214f34bf5af0c30b815ba6b3d12a468 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 1 May 2024 17:04:12 +0200 Subject: [PATCH 026/306] dev: Add debug symbols to shell (cherry picked from commit b72191f8c1669bc8df242e8386014363d096fad4) --- flake.nix | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/flake.nix b/flake.nix index af50710..393fab5 100644 --- a/flake.nix +++ b/flake.nix @@ -43,6 +43,15 @@ LIBCLANG_PATH BINDGEN_EXTRA_CLANG_ARGS ; + NIX_DEBUG_INFO_DIRS = + let + # TODO: add to Nixpkgs lib + getDebug = pkg: + if pkg?debug then pkg.debug + else if pkg?lib then pkg.lib + else pkg; + in + "${getDebug config.packages.nix}/lib/debug"; buildInputs = [ config.packages.nix ]; From 1edaffcc097bda7f429d470e547c9e38fbfdef8d Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 1 May 2024 18:58:09 +0200 Subject: [PATCH 027/306] flake: nix: NixOS/master -> hercules-ci/fix-eval-state-baseEnv-gc-root MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nix': 'github:nixos/nix/1cfc9da472f8dcfa7f521e544531d5e4daf2076c' (2024-04-23) → 'github:hercules-ci/nix/38974360102e67aaf2434fd3f920e2cd1bb3fa75' (2024-05-01) (cherry picked from commit b8001272187137243685870d414df28d1a229e02) --- flake.lock | 11 ++++++----- flake.nix | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index bca20aa..65bd0dc 100644 --- a/flake.lock +++ b/flake.lock @@ -214,15 +214,16 @@ "pre-commit-hooks": "pre-commit-hooks" }, "locked": { - "lastModified": 1713862243, - "narHash": "sha256-mfJSQyO7je+/WSBmnl/LTGvqrzv3k1F0kEd7Wg+VXw4=", - "owner": "nixos", + "lastModified": 1714582044, + "narHash": "sha256-+I7oZyclF/qcQxJYCsKRfYDk5Yzo/Xug/XhI+bSrnx8=", + "owner": "hercules-ci", "repo": "nix", - "rev": "1cfc9da472f8dcfa7f521e544531d5e4daf2076c", + "rev": "38974360102e67aaf2434fd3f920e2cd1bb3fa75", "type": "github" }, "original": { - "owner": "nixos", + "owner": "hercules-ci", + "ref": "fix-eval-state-baseEnv-gc-root", "repo": "nix", "type": "github" } diff --git a/flake.nix b/flake.nix index 393fab5..fc0ad04 100644 --- a/flake.nix +++ b/flake.nix @@ -4,7 +4,7 @@ inputs = { flake-parts.url = "github:hercules-ci/flake-parts"; flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs"; - nix.url = "github:nixos/nix"; + nix.url = "github:hercules-ci/nix/fix-eval-state-baseEnv-gc-root"; nix.inputs.nixpkgs.follows = "nixpkgs"; nix-cargo-integration.url = "github:yusdacra/nix-cargo-integration"; nix-cargo-integration.inputs.nixpkgs.follows = "nixpkgs"; From c2159c883482be7b5f3db1ba0501371ab8e5e657 Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Thu, 9 May 2024 12:29:32 -0400 Subject: [PATCH 028/306] bindgen strip nix_ prefix (cherry picked from commit d07ec1990015f5d12b39da40ad0cb1ef0e798642) --- rust/nix-c-raw/build.rs | 9 +++++++++ rust/nix-expr/src/eval_state.rs | 20 ++++++++++---------- rust/nix-expr/src/value.rs | 4 ++-- rust/nix-store/src/store.rs | 8 ++++---- rust/nix-util/src/context.rs | 12 ++++++------ rust/nix-util/src/string_return.rs | 2 +- 6 files changed, 32 insertions(+), 23 deletions(-) diff --git a/rust/nix-c-raw/build.rs b/rust/nix-c-raw/build.rs index 25251a0..c544fe8 100644 --- a/rust/nix-c-raw/build.rs +++ b/rust/nix-c-raw/build.rs @@ -2,6 +2,14 @@ use bindgen; use std::env; use std::path::PathBuf; +#[derive(Debug)] +struct StripNixPrefix {} +impl bindgen::callbacks::ParseCallbacks for StripNixPrefix { + fn item_name(&self, name: &str) -> Option { + name.strip_prefix("nix_").map(String::from) + } +} + fn main() { // Tell cargo to invalidate the built crate whenever the wrapper changes println!("cargo:rerun-if-changed=include/nix-c-raw.h"); @@ -14,6 +22,7 @@ fn main() { // Tell cargo to invalidate the built crate whenever any of the // included header files changed. .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) + .parse_callbacks(Box::new(StripNixPrefix {})) // Finish the builder and generate the bindings. .generate() // Unwrap the Result and panic on failure. diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 37458b1..40c3a45 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -17,7 +17,7 @@ lazy_static! { } let context: Context = Context::new(); unsafe { - raw::nix_libexpr_init(context.ptr()); + raw::libexpr_init(context.ptr()); } context.check_err()?; Ok(()) @@ -46,7 +46,7 @@ impl EvalState { init()?; let eval_state = unsafe { - raw::nix_state_create( + raw::state_create( context.ptr(), /* searchPath */ null_mut(), store.raw_ptr(), @@ -75,7 +75,7 @@ impl EvalState { let value = self.new_value_uninitialized(); unsafe { let ctx_ptr = self.context.ptr(); - raw::nix_expr_eval_from_string( + raw::expr_eval_from_string( ctx_ptr, self.raw_ptr(), expr_ptr.as_ptr(), @@ -89,13 +89,13 @@ impl EvalState { /** Try turn any Value into a Value that isn't a Thunk. */ pub fn force(&self, v: &Value) -> Result<()> { unsafe { - raw::nix_value_force(self.context.ptr(), self.raw_ptr(), v.raw_ptr()); + raw::value_force(self.context.ptr(), self.raw_ptr(), v.raw_ptr()); } self.context.check_err() } pub fn value_is_thunk(&self, value: &Value) -> bool { let r = unsafe { - raw::nix_get_type(self.context.ptr(), value.raw_ptr()) == raw::ValueType_NIX_TYPE_THUNK + raw::get_type(self.context.ptr(), value.raw_ptr()) == raw::ValueType_NIX_TYPE_THUNK }; self.context.check_err().unwrap(); r @@ -104,14 +104,14 @@ impl EvalState { if self.value_is_thunk(value) { self.force(value)?; } - let r = unsafe { raw::nix_get_type(self.context.ptr(), value.raw_ptr()) }; + let r = unsafe { raw::get_type(self.context.ptr(), value.raw_ptr()) }; Ok(ValueType::from_raw(r)) } /// Not exposed, because the caller must always explicitly handle the context or not accept one at all. fn get_string(&self, value: &Value) -> Result { let mut raw_buffer: Vec = Vec::new(); unsafe { - raw::nix_get_string( + raw::get_string( self.context.ptr(), value.raw_ptr(), Some(callback_get_vec_u8), @@ -132,14 +132,14 @@ impl EvalState { } fn new_value_uninitialized(&self) -> Value { - let value = unsafe { raw::nix_alloc_value(self.context.ptr(), self.raw_ptr()) }; + let value = unsafe { raw::alloc_value(self.context.ptr(), self.raw_ptr()) }; Value::new(value) } } pub fn gc_now() { unsafe { - raw::nix_gc_now(); + raw::gc_now(); } } @@ -182,7 +182,7 @@ pub fn gc_register_my_thread() -> Result<()> { impl Drop for EvalState { fn drop(&mut self) { unsafe { - raw::nix_state_free(self.raw_ptr()); + raw::state_free(self.raw_ptr()); } } } diff --git a/rust/nix-expr/src/value.rs b/rust/nix-expr/src/value.rs index 0f4ec13..9d48412 100644 --- a/rust/nix-expr/src/value.rs +++ b/rust/nix-expr/src/value.rs @@ -58,7 +58,7 @@ impl Drop for Value { fn drop(&mut self) { let context = Context::new(); unsafe { - raw::nix_gc_decref(context.ptr(), self.inner.as_ptr()); + raw::gc_decref(context.ptr(), self.inner.as_ptr()); } // ignore error from context, because drop should not panic } @@ -66,7 +66,7 @@ impl Drop for Value { impl Clone for Value { fn clone(&self) -> Self { let context = Context::new(); - unsafe { raw::nix_gc_incref(context.ptr(), self.inner.as_ptr()) }; + unsafe { raw::gc_incref(context.ptr(), self.inner.as_ptr()) }; context.check_err().unwrap(); Value { inner: self.inner } } diff --git a/rust/nix-store/src/store.rs b/rust/nix-store/src/store.rs index 6b568dd..e717d24 100644 --- a/rust/nix-store/src/store.rs +++ b/rust/nix-store/src/store.rs @@ -12,7 +12,7 @@ lazy_static! { static ref INIT: Result<()> = { unsafe { let context: Context = Context::new(); - raw::nix_libstore_init(context.ptr()); + raw::libstore_init(context.ptr()); context.check_err() } }; @@ -29,7 +29,7 @@ impl StoreRef { impl Drop for StoreRef { fn drop(&mut self) { unsafe { - raw::nix_store_free(self.inner.as_ptr()); + raw::store_free(self.inner.as_ptr()); } } } @@ -54,7 +54,7 @@ impl Store { let uri_ptr = CString::new(url)?; let store = unsafe { - raw::nix_store_open( + raw::store_open( context.ptr(), uri_ptr.as_ptr(), null_mut::<*mut *const i8>(), @@ -80,7 +80,7 @@ impl Store { pub fn get_uri(&self) -> Result { let mut raw_buffer: Vec = Vec::new(); unsafe { - raw::nix_store_get_uri( + raw::store_get_uri( self.context.ptr(), self.inner.ptr(), Some(callback_get_vec_u8), diff --git a/rust/nix-util/src/context.rs b/rust/nix-util/src/context.rs index a74fab0..fc8ad48 100644 --- a/rust/nix-util/src/context.rs +++ b/rust/nix-util/src/context.rs @@ -4,12 +4,12 @@ use std::ptr::null_mut; use std::ptr::NonNull; pub struct Context { - inner: NonNull, + inner: NonNull, } impl Context { pub fn new() -> Self { - let ctx = unsafe { raw::nix_c_context_create() }; + let ctx = unsafe { raw::c_context_create() }; if ctx.is_null() { panic!("nix_c_context_create returned a null pointer"); } @@ -18,14 +18,14 @@ impl Context { }; ctx } - pub fn ptr(&self) -> *mut raw::nix_c_context { + pub fn ptr(&self) -> *mut raw::c_context { self.inner.as_ptr() } pub fn check_err(&self) -> Result<()> { - let err = unsafe { raw::nix_err_code(self.inner.as_ptr()) }; + let err = unsafe { raw::err_code(self.inner.as_ptr()) }; if err != raw::NIX_OK.try_into().unwrap() { // msgp is a borrowed pointer, so we don't need to free it - let msgp = unsafe { raw::nix_err_msg(null_mut(), self.inner.as_ptr(), null_mut()) }; + let msgp = unsafe { raw::err_msg(null_mut(), self.inner.as_ptr(), null_mut()) }; // Turn the i8 pointer into a Rust string by copying let msg: &str = unsafe { core::ffi::CStr::from_ptr(msgp).to_str()? }; bail!("{}", msg); @@ -37,7 +37,7 @@ impl Context { impl Drop for Context { fn drop(&mut self) { unsafe { - raw::nix_c_context_free(self.inner.as_ptr()); + raw::c_context_free(self.inner.as_ptr()); } } } diff --git a/rust/nix-util/src/string_return.rs b/rust/nix-util/src/string_return.rs index ee62e22..28b8c09 100644 --- a/rust/nix-util/src/string_return.rs +++ b/rust/nix-util/src/string_return.rs @@ -20,7 +20,7 @@ mod tests { use nix_c_raw as raw; /// Typecheck the function signature against the generated bindings in nix_c_raw. - static _CALLBACK_GET_VEC_U8: raw::nix_get_string_callback = Some(callback_get_vec_u8); + static _CALLBACK_GET_VEC_U8: raw::get_string_callback = Some(callback_get_vec_u8); #[test] fn test_callback_get_vec_u8_empty() { From 48af0f9e41acafb4d5f7461352ea88d54c4c71d7 Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Thu, 9 May 2024 12:41:34 -0400 Subject: [PATCH 029/306] error checking tweaks (cherry picked from commit 6ac38519b710e69a0c30eb0fe8fc5fa712168cb8) --- rust/nix-expr/src/eval_state.rs | 5 +++-- rust/nix-expr/src/value.rs | 9 +++++---- rust/nix-store/src/store.rs | 2 +- rust/nix-util/src/context.rs | 4 +++- rust/nix-util/src/string_return.rs | 2 ++ 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 40c3a45..d564adb 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -52,8 +52,9 @@ impl EvalState { store.raw_ptr(), ) }; + context.check_err()?; if eval_state.is_null() { - bail!("nix_state_create returned a null pointer"); + panic!("nix_state_create returned a null pointer without an error"); } Ok(EvalState { eval_state: NonNull::new(eval_state).unwrap(), @@ -152,7 +153,7 @@ where if unsafe { raw::GC_thread_is_registered() } != 0 { return Ok(f()); } else { - gc_register_my_thread().unwrap(); + gc_register_my_thread()?; let r = f(); unsafe { raw::GC_unregister_my_thread(); diff --git a/rust/nix-expr/src/value.rs b/rust/nix-expr/src/value.rs index 9d48412..f1211e3 100644 --- a/rust/nix-expr/src/value.rs +++ b/rust/nix-expr/src/value.rs @@ -1,6 +1,6 @@ use nix_c_raw as raw; use nix_util::context::Context; -use std::ptr::NonNull; +use std::ptr::{null_mut, NonNull}; // TODO: test: cloning a thunk does not duplicate the evaluation. @@ -35,6 +35,7 @@ impl ValueType { raw::ValueType_NIX_TYPE_PATH => ValueType::Path, raw::ValueType_NIX_TYPE_STRING => ValueType::String, raw::ValueType_NIX_TYPE_THUNK => ValueType::Thunk, + // This would happen if a new type of value is added in Nix. _ => ValueType::Unknown, } } @@ -56,17 +57,17 @@ impl Value { } impl Drop for Value { fn drop(&mut self) { - let context = Context::new(); unsafe { - raw::gc_decref(context.ptr(), self.inner.as_ptr()); + // ignoring error because the only failure mode is leaking memory + raw::gc_decref(null_mut(), self.inner.as_ptr()); } - // ignore error from context, because drop should not panic } } impl Clone for Value { fn clone(&self) -> Self { let context = Context::new(); unsafe { raw::gc_incref(context.ptr(), self.inner.as_ptr()) }; + // can't return an error here, but we don't want to ignore the error either as it means we could use-after-free context.check_err().unwrap(); Value { inner: self.inner } } diff --git a/rust/nix-store/src/store.rs b/rust/nix-store/src/store.rs index e717d24..ba1b4bc 100644 --- a/rust/nix-store/src/store.rs +++ b/rust/nix-store/src/store.rs @@ -62,7 +62,7 @@ impl Store { }; context.check_err()?; if store.is_null() { - bail!("nix_c_store_open returned a null pointer"); + panic!("nix_c_store_open returned a null pointer without an error"); } let store = Store { inner: StoreRef { diff --git a/rust/nix-util/src/context.rs b/rust/nix-util/src/context.rs index fc8ad48..772511e 100644 --- a/rust/nix-util/src/context.rs +++ b/rust/nix-util/src/context.rs @@ -11,6 +11,8 @@ impl Context { pub fn new() -> Self { let ctx = unsafe { raw::c_context_create() }; if ctx.is_null() { + // We've failed to allocate a (relatively small) Context struct. + // We're almost certainly going to crash anyways. panic!("nix_c_context_create returned a null pointer"); } let ctx = Context { @@ -24,7 +26,7 @@ impl Context { pub fn check_err(&self) -> Result<()> { let err = unsafe { raw::err_code(self.inner.as_ptr()) }; if err != raw::NIX_OK.try_into().unwrap() { - // msgp is a borrowed pointer, so we don't need to free it + // msgp is a borrowed pointer (pointing into the context), so we don't need to free it let msgp = unsafe { raw::err_msg(null_mut(), self.inner.as_ptr(), null_mut()) }; // Turn the i8 pointer into a Rust string by copying let msg: &str = unsafe { core::ffi::CStr::from_ptr(msgp).to_str()? }; diff --git a/rust/nix-util/src/string_return.rs b/rust/nix-util/src/string_return.rs index 28b8c09..9fe5a8f 100644 --- a/rust/nix-util/src/string_return.rs +++ b/rust/nix-util/src/string_return.rs @@ -1,6 +1,8 @@ /// Callback for nix_store_get_uri and other functions that return a string. /// /// This function is used by the other nix_* crates, and you should never need to call it yourself. +/// +/// Some functions in the nix library "return" strings without giving you ownership over them, by letting you pass a callback function that gets to look at that string. This callback simply turns that string pointer into an owned rust String. pub unsafe extern "C" fn callback_get_vec_u8( start: *const ::std::os::raw::c_char, n: std::os::raw::c_uint, From 7ce6900bfd393b8f27ecabea8768c1d236dc7f7e Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Thu, 9 May 2024 12:41:54 -0400 Subject: [PATCH 030/306] `0 as *mut _` => `null_mut()` (cherry picked from commit 6a345c87760f4c657bd62c62308e33e266769087) --- rust/nix-expr/src/eval_state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index d564adb..15e2e6a 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -169,7 +169,7 @@ pub fn gc_register_my_thread() -> Result<()> { return Ok(()); } let mut sb: raw::GC_stack_base = raw::GC_stack_base { - mem_base: 0 as *mut _, + mem_base: null_mut(), }; let r = raw::GC_get_stack_base(&mut sb); if r as u32 != raw::GC_SUCCESS { From eadd2827a55398069eee0d95e4aa42ce30ca1e1b Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 8 Apr 2024 16:23:59 +0200 Subject: [PATCH 031/306] refact: Accept &str in eval_from_string (cherry picked from commit 0ce86f66801ce0a052316a742ded241c773099dd) --- rust/nix-expr/src/eval_state.rs | 33 +++++++++------------------------ 1 file changed, 9 insertions(+), 24 deletions(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 15e2e6a..30baa34 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -68,7 +68,7 @@ impl EvalState { pub fn store(&self) -> &Store { &self.store } - pub fn eval_from_string(&self, expr: String, path: String) -> Result { + pub fn eval_from_string(&self, expr: &str, path: &str) -> Result { let expr_ptr = CString::new(expr).with_context(|| "eval_from_string: expr contains null byte")?; let path_ptr = @@ -214,9 +214,7 @@ mod tests { gc_registering_current_thread(|| { let store = Store::open("auto").unwrap(); let es = EvalState::new(store).unwrap(); - let v = es - .eval_from_string("1".to_string(), "".to_string()) - .unwrap(); + let v = es.eval_from_string("1", "").unwrap(); let v2 = v.clone(); es.force(&v).unwrap(); let t = es.value_type(&v).unwrap(); @@ -233,9 +231,7 @@ mod tests { gc_registering_current_thread(|| { let store = Store::open("auto").unwrap(); let es = EvalState::new(store).unwrap(); - let v = es - .eval_from_string("true".to_string(), "".to_string()) - .unwrap(); + let v = es.eval_from_string("true", "").unwrap(); es.force(&v).unwrap(); let t = es.value_type(&v).unwrap(); assert!(t == ValueType::Bool); @@ -248,9 +244,7 @@ mod tests { gc_registering_current_thread(|| { let store = Store::open("auto").unwrap(); let es = EvalState::new(store).unwrap(); - let v = es - .eval_from_string("\"hello\"".to_string(), "".to_string()) - .unwrap(); + let v = es.eval_from_string("\"hello\"", "").unwrap(); es.force(&v).unwrap(); let t = es.value_type(&v).unwrap(); assert!(t == ValueType::String); @@ -265,9 +259,7 @@ mod tests { gc_registering_current_thread(|| { let store = Store::open("auto").unwrap(); let es = EvalState::new(store).unwrap(); - let v = es - .eval_from_string("true".to_string(), "".to_string()) - .unwrap(); + let v = es.eval_from_string("true", "").unwrap(); es.force(&v).unwrap(); let r = es.require_string(&v); assert!(r.is_err()); @@ -285,9 +277,7 @@ mod tests { gc_registering_current_thread(|| { let store = Store::open("auto").unwrap(); let es = EvalState::new(store).unwrap(); - let v = es - .eval_from_string("/foo".to_string(), "".to_string()) - .unwrap(); + let v = es.eval_from_string("/foo", "").unwrap(); es.force(&v).unwrap(); let r = es.require_string(&v); assert!(r.is_err()); @@ -305,10 +295,7 @@ mod tests { let store = Store::open("auto").unwrap(); let es = EvalState::new(store).unwrap(); let v = es - .eval_from_string( - "builtins.substring 0 1 \"ü\"".to_string(), - "".to_string(), - ) + .eval_from_string("builtins.substring 0 1 \"ü\"", "") .unwrap(); es.force(&v).unwrap(); let t = es.value_type(&v).unwrap(); @@ -329,7 +316,7 @@ mod tests { let store = Store::open("auto").unwrap(); let es = EvalState::new(store).unwrap(); let v = es - .eval_from_string("(derivation { name = \"hello\"; system = \"dummy\"; builder = \"cmd.exe\"; }).outPath".to_string(), "".to_string()) + .eval_from_string("(derivation { name = \"hello\"; system = \"dummy\"; builder = \"cmd.exe\"; }).outPath", "") .unwrap(); es.force(&v).unwrap(); let t = es.value_type(&v).unwrap(); @@ -347,9 +334,7 @@ mod tests { gc_registering_current_thread(|| { let store = Store::open("auto").unwrap(); let es = EvalState::new(store).unwrap(); - let v = es - .eval_from_string("{ }".to_string(), "".to_string()) - .unwrap(); + let v = es.eval_from_string("{ }", "").unwrap(); es.force(&v).unwrap(); let t = es.value_type(&v).unwrap(); assert!(t == ValueType::AttrSet); From 7cc9cfbb2b9915acaba980d8b1821c3462ae4515 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 30 Sep 2025 20:18:59 +0200 Subject: [PATCH 032/306] fix: Update test calls for &str API change --- rust/nix-expr/src/eval_state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 30baa34..c82d770 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -348,7 +348,7 @@ mod tests { let store = Store::open("auto").unwrap(); let es = EvalState::new(store).unwrap(); let v = es - .eval_from_string("[ ]".to_string(), "".to_string()) + .eval_from_string("[ ]", "") .unwrap(); es.force(&v).unwrap(); let t = es.value_type(&v).unwrap(); From acb1274c089c60488ee0060bd84f537faff54bc8 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 8 Apr 2024 16:25:37 +0200 Subject: [PATCH 033/306] refact: Add callback_get_vec_u8_data Make it easier to correctly use the string callbacks. (cherry picked from commit 32db5dfc020053c36b7a4776313ee72d7bc961dd) --- rust/nix-store/src/store.rs | 4 ++-- rust/nix-util/src/string_return.rs | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/rust/nix-store/src/store.rs b/rust/nix-store/src/store.rs index ba1b4bc..3aeaa48 100644 --- a/rust/nix-store/src/store.rs +++ b/rust/nix-store/src/store.rs @@ -2,7 +2,7 @@ use anyhow::{bail, Result}; use lazy_static::lazy_static; use nix_c_raw as raw; use nix_util::context::Context; -use nix_util::string_return::callback_get_vec_u8; +use nix_util::string_return::{callback_get_vec_u8, callback_get_vec_u8_data}; use std::ffi::CString; use std::ptr::null_mut; use std::ptr::NonNull; @@ -84,7 +84,7 @@ impl Store { self.context.ptr(), self.inner.ptr(), Some(callback_get_vec_u8), - &mut raw_buffer as *mut Vec as *mut std::ffi::c_void, + callback_get_vec_u8_data(&mut raw_buffer), ) }; self.context.check_err()?; diff --git a/rust/nix-util/src/string_return.rs b/rust/nix-util/src/string_return.rs index 9fe5a8f..cfead12 100644 --- a/rust/nix-util/src/string_return.rs +++ b/rust/nix-util/src/string_return.rs @@ -16,6 +16,10 @@ pub unsafe extern "C" fn callback_get_vec_u8( (*ret).extend_from_slice(slice); } +pub fn callback_get_vec_u8_data(vec: &mut Vec) -> *mut std::os::raw::c_void { + vec as *mut Vec as *mut std::os::raw::c_void +} + #[cfg(test)] mod tests { use super::*; From daffade511d14c07b1b7564cb38bbebd0b1785b7 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 8 Apr 2024 16:45:47 +0200 Subject: [PATCH 034/306] feat: nix_util::settings::set, get (cherry picked from commit 19ed7bd655e72e10373f1afab5af214f2f47881e) --- rust/Cargo.lock | 1 + rust/nix-util/Cargo.toml | 1 + rust/nix-util/src/lib.rs | 1 + rust/nix-util/src/settings.rs | 71 +++++++++++++++++++++++++++++++++++ 4 files changed, 74 insertions(+) create mode 100644 rust/nix-util/src/settings.rs diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 0b9d59f..fe37409 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -209,6 +209,7 @@ name = "nix-util" version = "0.1.0" dependencies = [ "anyhow", + "ctor", "nix-c-raw", ] diff --git a/rust/nix-util/Cargo.toml b/rust/nix-util/Cargo.toml index db15880..e323aa3 100644 --- a/rust/nix-util/Cargo.toml +++ b/rust/nix-util/Cargo.toml @@ -8,3 +8,4 @@ edition = "2021" [dependencies] anyhow = "1.0.79" nix-c-raw = { path = "../nix-c-raw" } +ctor = "0.2.7" diff --git a/rust/nix-util/src/lib.rs b/rust/nix-util/src/lib.rs index 0306c93..e1a943b 100644 --- a/rust/nix-util/src/lib.rs +++ b/rust/nix-util/src/lib.rs @@ -1,2 +1,3 @@ pub mod context; +pub mod settings; pub mod string_return; diff --git a/rust/nix-util/src/settings.rs b/rust/nix-util/src/settings.rs new file mode 100644 index 0000000..5abe418 --- /dev/null +++ b/rust/nix-util/src/settings.rs @@ -0,0 +1,71 @@ +use anyhow::Result; +use nix_c_raw as raw; + +use crate::{ + context, + string_return::{callback_get_vec_u8, callback_get_vec_u8_data}, +}; + +pub fn set(key: &str, value: &str) -> Result<()> { + let ctx = context::Context::new(); + let key = std::ffi::CString::new(key)?; + let value = std::ffi::CString::new(value)?; + unsafe { + raw::setting_set(ctx.ptr(), key.as_ptr(), value.as_ptr()); + }; + ctx.check_err() +} + +pub fn get(key: &str) -> Result { + let ctx = context::Context::new(); + let key = std::ffi::CString::new(key)?; + let mut raw_buffer: Vec = Vec::new(); + unsafe { + raw::setting_get( + ctx.ptr(), + key.as_ptr(), + callback_get_vec_u8 as *mut std::ffi::c_void, + callback_get_vec_u8_data(&mut raw_buffer), + ) + }; + ctx.check_err()?; + String::from_utf8(raw_buffer).map_err(|e| e.into()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[ctor::ctor] + fn setup() { + let ctx = context::Context::new(); + unsafe { + nix_c_raw::libstore_init(ctx.ptr()); + }; + ctx.check_err().unwrap(); + } + + #[test] + fn set_get() { + // Something that shouldn't matter if it's a different value temporarily + let key = "user-agent-suffix"; + + // Save the old value, in case it's important. Probably not. + // If this doesn't work, pick a different setting to test with + let old_value = get(key).unwrap(); + + let new_value = "just a string that we're storing into some option for testing purposes"; + + let res_e = (|| { + set(key, new_value)?; + get(key) + })(); + + // Restore immediately; try not to affect other tests (if relevant). + set(key, old_value.as_str()).unwrap(); + + let res = res_e.unwrap(); + + assert_eq!(res, new_value); + } +} From 6736f05a3f124986f7beb4a0e7088cb4ae238f3d Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 30 Sep 2025 20:22:11 +0200 Subject: [PATCH 035/306] fix: Use function pointer for settings callback --- rust/nix-util/src/settings.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/nix-util/src/settings.rs b/rust/nix-util/src/settings.rs index 5abe418..10f05cc 100644 --- a/rust/nix-util/src/settings.rs +++ b/rust/nix-util/src/settings.rs @@ -24,7 +24,7 @@ pub fn get(key: &str) -> Result { raw::setting_get( ctx.ptr(), key.as_ptr(), - callback_get_vec_u8 as *mut std::ffi::c_void, + Some(callback_get_vec_u8), callback_get_vec_u8_data(&mut raw_buffer), ) }; From 87203ef394905aca8e1b71e48f9145c2ff3d3e20 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 8 Apr 2024 16:54:36 +0200 Subject: [PATCH 036/306] feat: EvalState.realise_string (cherry picked from commit f2b1142018fd64dd45ec97f1eccf0c48cc4a8c6d) --- rust/nix-expr/src/eval_state.rs | 107 +++++++++++++++++++++++++++++++- rust/nix-store/src/lib.rs | 1 + rust/nix-store/src/path.rs | 33 ++++++++++ 3 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 rust/nix-store/src/path.rs diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index c82d770..51e236c 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -3,6 +3,7 @@ use anyhow::Context as _; use anyhow::{bail, Result}; use lazy_static::lazy_static; use nix_c_raw as raw; +use nix_store::path::StorePath; use nix_store::store::Store; use nix_util::context::Context; use nix_util::string_return::callback_get_vec_u8; @@ -34,6 +35,11 @@ pub fn init() -> Result<()> { } } +pub struct RealisedString { + pub s: String, + pub paths: Vec, +} + pub struct EvalState { eval_state: NonNull, store: Store, @@ -131,6 +137,46 @@ impl EvalState { } self.get_string(value) } + pub fn realise_string( + &self, + value: &Value, + is_import_from_derivation: bool, + ) -> Result { + let t = self.value_type(value)?; + if t != ValueType::String { + bail!("expected a string, but got a {:?}", t); + } + + let rs = unsafe { + raw::string_realise( + self.context.ptr(), + self.raw_ptr(), + value.raw_ptr(), + is_import_from_derivation, + ) + }; + self.context.check_err()?; + + let s = unsafe { + let start = raw::realised_string_get_buffer_start(rs) as *const u8; + let size = raw::realised_string_get_buffer_size(rs); + let slice = std::slice::from_raw_parts(start, size); + String::from_utf8(slice.to_vec()) + .map_err(|e| anyhow::format_err!("Nix string is not valid UTF-8: {}", e))? + }; + + let paths = unsafe { + let n = raw::realised_string_get_store_path_count(rs); + let mut paths = Vec::with_capacity(n as usize); + for i in 0..n { + let path = raw::realised_string_get_store_path(rs, i); + paths.push(StorePath::new_raw_clone(path)); + } + paths + }; + + Ok(RealisedString { s, paths }) + } fn new_value_uninitialized(&self) -> Value { let value = unsafe { raw::alloc_value(self.context.ptr(), self.raw_ptr()) }; @@ -191,12 +237,24 @@ impl Drop for EvalState { #[cfg(test)] mod tests { use ctor::ctor; + use nix_util::settings; use super::*; #[ctor] fn setup() { - init().unwrap(); + (|| -> Result<()> { + init()?; + // If it reinvokes the test suite, + // settings::set("build-hook", "")?; + + // When testing in the sandbox, the default build dir would be a parent of the storeDir, + // which causes an error. So we set a custom build dir here. + settings::set("sandbox-build-dir", "/custom-build-dir-for-test")?; + Ok(()) + })() + .unwrap(); + std::env::set_var("_NIX_TEST_NO_SANDBOX", "1"); } #[test] @@ -356,4 +414,51 @@ mod tests { }) .unwrap(); } + + #[test] + fn eval_state_realise_string() { + gc_registering_current_thread(|| { + let store = Store::open("auto").unwrap(); + let es = EvalState::new(store).unwrap(); + let expr = r#" + '' + a derivation output: ${ + derivation { name = "letsbuild"; + system = builtins.currentSystem; + builder = "/bin/sh"; + args = [ "-c" "echo foo > $out" ]; + }} + a path: ${builtins.toFile "just-a-file" "ooh file good"} + a derivation path by itself: ${ + builtins.unsafeDiscardOutputDependency + (derivation { + name = "not-actually-built-yet"; + system = builtins.currentSystem; + builder = "/bin/sh"; + args = [ "-c" "echo foo > $out" ]; + }).drvPath} + '' + "#; + let v = es.eval_from_string(expr, "").unwrap(); + es.force(&v).unwrap(); + let rs = es.realise_string(&v, false).unwrap(); + + assert!(rs.s.starts_with("a derivation output:")); + assert!(rs.s.contains("-letsbuild\n")); + assert!(!rs.s.contains("-letsbuild.drv")); + assert!(rs.s.contains("a path:")); + assert!(rs.s.contains("-just-a-file")); + assert!(!rs.s.contains("-just-a-file.drv")); + assert!(!rs.s.contains("ooh file good")); + assert!(rs.s.ends_with("-not-actually-built-yet.drv\n")); + + assert_eq!(rs.paths.len(), 3); + let mut names: Vec = rs.paths.iter().map(|p| p.name().unwrap()).collect(); + names.sort(); + assert_eq!(names[0], "just-a-file"); + assert_eq!(names[1], "letsbuild"); + assert_eq!(names[2], "not-actually-built-yet.drv"); + }) + .unwrap(); + } } diff --git a/rust/nix-store/src/lib.rs b/rust/nix-store/src/lib.rs index 55c88cb..5c57e2c 100644 --- a/rust/nix-store/src/lib.rs +++ b/rust/nix-store/src/lib.rs @@ -1 +1,2 @@ +pub mod path; pub mod store; diff --git a/rust/nix-store/src/path.rs b/rust/nix-store/src/path.rs new file mode 100644 index 0000000..1a7350c --- /dev/null +++ b/rust/nix-store/src/path.rs @@ -0,0 +1,33 @@ +use anyhow::Result; +use nix_c_raw as raw; +use nix_util::string_return::{callback_get_vec_u8, callback_get_vec_u8_data}; + +pub struct StorePath { + raw: *mut raw::StorePath, +} +impl StorePath { + pub fn new_raw_clone(raw: *const raw::StorePath) -> Self { + Self::new_raw(unsafe { raw::store_path_clone(raw as *mut raw::StorePath) }) + } + pub fn new_raw(raw: *mut raw::StorePath) -> Self { + StorePath { raw } + } + pub fn name(&self) -> Result { + unsafe { + let mut vec = Vec::new(); + raw::store_path_name( + self.raw, + Some(callback_get_vec_u8), + callback_get_vec_u8_data(&mut vec), + ); + String::from_utf8(vec).map_err(|e| e.into()) + } + } +} +impl Drop for StorePath { + fn drop(&mut self) { + unsafe { + raw::store_path_free(self.raw); + } + } +} From 7e347314bb1d8137489335766e9f53160977163f Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 22 May 2024 13:38:55 +0200 Subject: [PATCH 037/306] fix: Plug leak in EvalState::realise_string (cherry picked from commit d9f9aa64ceb48c39c028d48680a664063d70bc23) --- rust/nix-expr/src/eval_state.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 51e236c..ec1b818 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -175,6 +175,11 @@ impl EvalState { paths }; + // We've converted the nix_realised_string to a native struct containing copies, so we can free it now. + unsafe { + raw::realised_string_free(rs); + } + Ok(RealisedString { s, paths }) } From 625d17b0dbc5555fa33ab9d28156727e8a36a50e Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 22 May 2024 13:39:19 +0200 Subject: [PATCH 038/306] doc: Explain why we might need to clear the build hook (cherry picked from commit 52d81586c671226ab2c1bbe0e1914c11b58239da) --- rust/nix-expr/src/eval_state.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index ec1b818..e558f1b 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -250,7 +250,10 @@ mod tests { fn setup() { (|| -> Result<()> { init()?; - // If it reinvokes the test suite, + // During development, we encountered a problem where the build hook + // would cause the test suite to reinvokes itself, causing an infinite loop. + // While _NIX_TEST_NO_SANDBOX=1 should prevent this, we may also set the + // build hook to "" to prevent this. // settings::set("build-hook", "")?; // When testing in the sandbox, the default build dir would be a parent of the storeDir, From a2686c31b25d55678a05a94cbbba31e04382acd9 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 22 May 2024 13:40:01 +0200 Subject: [PATCH 039/306] refact: Distribute unwrap() This gives us better error messages in case it does go wrong. (cherry picked from commit 0fec573d82b6c51e72183ae5a19c922a53863d5b) --- rust/nix-expr/src/eval_state.rs | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index e558f1b..c929278 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -248,20 +248,17 @@ mod tests { #[ctor] fn setup() { - (|| -> Result<()> { - init()?; - // During development, we encountered a problem where the build hook - // would cause the test suite to reinvokes itself, causing an infinite loop. - // While _NIX_TEST_NO_SANDBOX=1 should prevent this, we may also set the - // build hook to "" to prevent this. - // settings::set("build-hook", "")?; + init().unwrap(); - // When testing in the sandbox, the default build dir would be a parent of the storeDir, - // which causes an error. So we set a custom build dir here. - settings::set("sandbox-build-dir", "/custom-build-dir-for-test")?; - Ok(()) - })() - .unwrap(); + // During development, we encountered a problem where the build hook + // would cause the test suite to reinvokes itself, causing an infinite loop. + // While _NIX_TEST_NO_SANDBOX=1 should prevent this, we may also set the + // build hook to "" to prevent this. + // settings::set("build-hook", "")?; + + // When testing in the sandbox, the default build dir would be a parent of the storeDir, + // which causes an error. So we set a custom build dir here. + settings::set("sandbox-build-dir", "/custom-build-dir-for-test").unwrap(); std::env::set_var("_NIX_TEST_NO_SANDBOX", "1"); } From 9a6ef0489ef85e3089f3d352d937bb4144097196 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 22 May 2024 14:01:36 +0200 Subject: [PATCH 040/306] doc: Clarify ownership around StorePath::new_* (cherry picked from commit 397f00e8eefff376759f05bd0cbaecc106c681df) --- rust/nix-store/src/path.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/rust/nix-store/src/path.rs b/rust/nix-store/src/path.rs index 1a7350c..f8ca699 100644 --- a/rust/nix-store/src/path.rs +++ b/rust/nix-store/src/path.rs @@ -6,9 +6,20 @@ pub struct StorePath { raw: *mut raw::StorePath, } impl StorePath { + /** + * This is a low level function that you shouldn't have to call unless you are developing the Nix bindings. + * + * Construct a new `StorePath` by first cloning the C store path. + * This does not take ownership of the C store path, so it should be a borrowed value, or you should free it. + */ pub fn new_raw_clone(raw: *const raw::StorePath) -> Self { Self::new_raw(unsafe { raw::store_path_clone(raw as *mut raw::StorePath) }) } + /** + * This is a low level function that you shouldn't have to call unless you are developing the Nix bindings. + * + * Takes ownership of a C `nix_store_path`. It will be freed when the `StorePath` is dropped. + */ pub fn new_raw(raw: *mut raw::StorePath) -> Self { StorePath { raw } } From 4688ccbf953566fd77e4ecc2e46c451a69e53d0c Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 22 May 2024 14:45:12 +0200 Subject: [PATCH 041/306] refact: Make the callback convert to Result immediately This is slightly easier to use than the previous pattern that was always followed up by the same conversions. (cherry picked from commit 756c080730cd4fa81d4c0e3a99688cbe8debe57f) --- rust/nix-expr/src/eval_state.rs | 12 +++---- rust/nix-store/src/path.rs | 13 ++++--- rust/nix-store/src/store.rs | 11 +++--- rust/nix-util/src/lib.rs | 1 + rust/nix-util/src/settings.rs | 12 +++---- rust/nix-util/src/string_return.rs | 55 ++++++++++++++++++------------ 6 files changed, 61 insertions(+), 43 deletions(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index c929278..6ff4d54 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -6,7 +6,8 @@ use nix_c_raw as raw; use nix_store::path::StorePath; use nix_store::store::Store; use nix_util::context::Context; -use nix_util::string_return::callback_get_vec_u8; +use nix_util::result_string_init; +use nix_util::string_return::{callback_get_result_string, callback_get_result_string_data}; use std::ffi::CString; use std::ptr::null_mut; use std::ptr::NonNull; @@ -116,18 +117,17 @@ impl EvalState { } /// Not exposed, because the caller must always explicitly handle the context or not accept one at all. fn get_string(&self, value: &Value) -> Result { - let mut raw_buffer: Vec = Vec::new(); + let mut r = result_string_init!(); unsafe { raw::get_string( self.context.ptr(), value.raw_ptr(), - Some(callback_get_vec_u8), - &mut raw_buffer as *mut Vec as *mut std::ffi::c_void, + Some(callback_get_result_string), + callback_get_result_string_data(&mut r), ) }; self.context.check_err()?; - String::from_utf8(raw_buffer) - .map_err(|e| anyhow::format_err!("Nix string is not valid UTF-8: {}", e)) + r } /// NOTE: this will be replaced by two methods, one that also returns the context, and one that checks that the context is empty pub fn require_string(&self, value: &Value) -> Result { diff --git a/rust/nix-store/src/path.rs b/rust/nix-store/src/path.rs index f8ca699..17e136d 100644 --- a/rust/nix-store/src/path.rs +++ b/rust/nix-store/src/path.rs @@ -1,6 +1,9 @@ use anyhow::Result; use nix_c_raw as raw; -use nix_util::string_return::{callback_get_vec_u8, callback_get_vec_u8_data}; +use nix_util::{ + result_string_init, + string_return::{callback_get_result_string, callback_get_result_string_data}, +}; pub struct StorePath { raw: *mut raw::StorePath, @@ -25,13 +28,13 @@ impl StorePath { } pub fn name(&self) -> Result { unsafe { - let mut vec = Vec::new(); + let mut r = result_string_init!(); raw::store_path_name( self.raw, - Some(callback_get_vec_u8), - callback_get_vec_u8_data(&mut vec), + Some(callback_get_result_string), + callback_get_result_string_data(&mut r), ); - String::from_utf8(vec).map_err(|e| e.into()) + r } } } diff --git a/rust/nix-store/src/store.rs b/rust/nix-store/src/store.rs index 3aeaa48..98fc79d 100644 --- a/rust/nix-store/src/store.rs +++ b/rust/nix-store/src/store.rs @@ -2,7 +2,8 @@ use anyhow::{bail, Result}; use lazy_static::lazy_static; use nix_c_raw as raw; use nix_util::context::Context; -use nix_util::string_return::{callback_get_vec_u8, callback_get_vec_u8_data}; +use nix_util::result_string_init; +use nix_util::string_return::{callback_get_result_string, callback_get_result_string_data}; use std::ffi::CString; use std::ptr::null_mut; use std::ptr::NonNull; @@ -78,17 +79,17 @@ impl Store { } pub fn get_uri(&self) -> Result { - let mut raw_buffer: Vec = Vec::new(); + let mut r = result_string_init!(); unsafe { raw::store_get_uri( self.context.ptr(), self.inner.ptr(), - Some(callback_get_vec_u8), - callback_get_vec_u8_data(&mut raw_buffer), + Some(callback_get_result_string), + callback_get_result_string_data(&mut r), ) }; self.context.check_err()?; - String::from_utf8(raw_buffer).map_err(|e| e.into()) + r } } diff --git a/rust/nix-util/src/lib.rs b/rust/nix-util/src/lib.rs index e1a943b..29ad7dd 100644 --- a/rust/nix-util/src/lib.rs +++ b/rust/nix-util/src/lib.rs @@ -1,3 +1,4 @@ pub mod context; pub mod settings; +#[macro_use] pub mod string_return; diff --git a/rust/nix-util/src/settings.rs b/rust/nix-util/src/settings.rs index 10f05cc..36964ad 100644 --- a/rust/nix-util/src/settings.rs +++ b/rust/nix-util/src/settings.rs @@ -2,8 +2,8 @@ use anyhow::Result; use nix_c_raw as raw; use crate::{ - context, - string_return::{callback_get_vec_u8, callback_get_vec_u8_data}, + context, result_string_init, + string_return::{callback_get_result_string, callback_get_result_string_data}, }; pub fn set(key: &str, value: &str) -> Result<()> { @@ -19,17 +19,17 @@ pub fn set(key: &str, value: &str) -> Result<()> { pub fn get(key: &str) -> Result { let ctx = context::Context::new(); let key = std::ffi::CString::new(key)?; - let mut raw_buffer: Vec = Vec::new(); + let mut r: Result = result_string_init!(); unsafe { raw::setting_get( ctx.ptr(), key.as_ptr(), - Some(callback_get_vec_u8), - callback_get_vec_u8_data(&mut raw_buffer), + Some(callback_get_result_string), + callback_get_result_string_data(&mut r), ) }; ctx.check_err()?; - String::from_utf8(raw_buffer).map_err(|e| e.into()) + r } #[cfg(test)] diff --git a/rust/nix-util/src/string_return.rs b/rust/nix-util/src/string_return.rs index cfead12..004f1ae 100644 --- a/rust/nix-util/src/string_return.rs +++ b/rust/nix-util/src/string_return.rs @@ -1,23 +1,37 @@ +use anyhow::Result; + /// Callback for nix_store_get_uri and other functions that return a string. /// /// This function is used by the other nix_* crates, and you should never need to call it yourself. /// /// Some functions in the nix library "return" strings without giving you ownership over them, by letting you pass a callback function that gets to look at that string. This callback simply turns that string pointer into an owned rust String. -pub unsafe extern "C" fn callback_get_vec_u8( +pub unsafe extern "C" fn callback_get_result_string( start: *const ::std::os::raw::c_char, n: std::os::raw::c_uint, user_data: *mut std::os::raw::c_void, ) { - let ret = user_data as *mut Vec; + let ret = user_data as *mut Result; let slice = std::slice::from_raw_parts(start as *const u8, n as usize); - if !(*ret).is_empty() { - panic!("callback_get_vec_u8: slice must be empty. Were we called twice?"); + + if !(*ret).is_err() { + panic!( + "callback_get_result_string: Result must be initialized to Err. Did Nix call us twice?" + ); } - (*ret).extend_from_slice(slice); + + *ret = String::from_utf8(slice.to_vec()) + .map_err(|e| anyhow::format_err!("Nix string is not valid UTF-8: {}", e)); } -pub fn callback_get_vec_u8_data(vec: &mut Vec) -> *mut std::os::raw::c_void { - vec as *mut Vec as *mut std::os::raw::c_void +pub fn callback_get_result_string_data(vec: &mut Result) -> *mut std::os::raw::c_void { + vec as *mut Result as *mut std::os::raw::c_void +} + +#[macro_export] +macro_rules! result_string_init { + () => { + Err(anyhow::anyhow!("String was not set by Nix C API")) + }; } #[cfg(test)] @@ -26,35 +40,34 @@ mod tests { use nix_c_raw as raw; /// Typecheck the function signature against the generated bindings in nix_c_raw. - static _CALLBACK_GET_VEC_U8: raw::get_string_callback = Some(callback_get_vec_u8); + static _CALLBACK_GET_RESULT_STRING: raw::get_string_callback = Some(callback_get_result_string); #[test] - fn test_callback_get_vec_u8_empty() { - let mut ret: Vec = Vec::new(); + fn test_callback_get_result_string_empty() { + let mut ret: Result = result_string_init!(); let start: *const std::os::raw::c_char = std::ptr::null(); let n: std::os::raw::c_uint = 0; - let user_data: *mut std::os::raw::c_void = - &mut ret as *mut Vec as *mut std::os::raw::c_void; + let user_data: *mut std::os::raw::c_void = callback_get_result_string_data(&mut ret); unsafe { - callback_get_vec_u8(start, n, user_data); + callback_get_result_string(start, n, user_data); } - assert_eq!(ret, vec![]); + let s = ret.unwrap(); + assert_eq!(s, ""); } #[test] - fn test_callback_get_vec_u8() { - let mut ret: Vec = Vec::new(); + fn test_callback_result_string() { + let mut ret: Result = result_string_init!(); let start: *const std::os::raw::c_char = b"helloGARBAGE".as_ptr() as *const i8; let n: std::os::raw::c_uint = 5; - let user_data: *mut std::os::raw::c_void = - &mut ret as *mut Vec as *mut std::os::raw::c_void; - + let user_data: *mut std::os::raw::c_void = callback_get_result_string_data(&mut ret); unsafe { - callback_get_vec_u8(start, n, user_data); + callback_get_result_string(start, n, user_data); } - assert_eq!(ret, b"hello".to_vec()); + let s = ret.unwrap(); + assert_eq!(s, "hello"); } } From fbbc7cb4908d6d886b0250cc1e1004061345d264 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 9 Apr 2024 13:13:52 +0200 Subject: [PATCH 042/306] feat: EvalState.require_int (cherry picked from commit 890441adf963f1a33ba75889cc9735deeefbf51c) --- rust/nix-expr/src/eval_state.rs | 26 +++++++++++++++++++++++++- rust/nix-expr/src/value.rs | 2 ++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 6ff4d54..1559727 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -1,4 +1,4 @@ -use crate::value::{Value, ValueType}; +use crate::value::{Int, Value, ValueType}; use anyhow::Context as _; use anyhow::{bail, Result}; use lazy_static::lazy_static; @@ -115,6 +115,15 @@ impl EvalState { let r = unsafe { raw::get_type(self.context.ptr(), value.raw_ptr()) }; Ok(ValueType::from_raw(r)) } + pub fn require_int(&self, v: &Value) -> Result { + let t = self.value_type(v).unwrap(); + if t != ValueType::Int { + bail!("expected an int, but got a {:?}", t); + } + let i = unsafe { raw::get_int(self.context.ptr(), v.raw_ptr()) }; + Ok(i) + } + /// Not exposed, because the caller must always explicitly handle the context or not accept one at all. fn get_string(&self, value: &Value) -> Result { let mut r = result_string_init!(); @@ -302,6 +311,21 @@ mod tests { .unwrap(); } + #[test] + fn eval_state_value_int() { + gc_registering_current_thread(|| { + let store = Store::open("auto").unwrap(); + let es = EvalState::new(store).unwrap(); + let v = es.eval_from_string("1", "").unwrap(); + es.force(&v).unwrap(); + let t = es.value_type(&v).unwrap(); + assert!(t == ValueType::Int); + let i = es.require_int(&v).unwrap(); + assert!(i == 1); + }) + .unwrap(); + } + #[test] fn eval_state_value_string() { gc_registering_current_thread(|| { diff --git a/rust/nix-expr/src/value.rs b/rust/nix-expr/src/value.rs index f1211e3..88f5f76 100644 --- a/rust/nix-expr/src/value.rs +++ b/rust/nix-expr/src/value.rs @@ -4,6 +4,8 @@ use std::ptr::{null_mut, NonNull}; // TODO: test: cloning a thunk does not duplicate the evaluation. +pub type Int = i64; + /** The type of a value (or thunk) */ #[derive(Eq, PartialEq, Debug)] pub enum ValueType { From 52778c37e9aa4b90c007dbfb3403b8fb766a77cd Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 9 Apr 2024 13:14:53 +0200 Subject: [PATCH 043/306] feat: EvalState.call (cherry picked from commit f3aa6e523c0c5214533d0005d8c3f85a0579b1de) --- rust/nix-expr/src/eval_state.rs | 76 +++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 1559727..006eaa6 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -192,6 +192,23 @@ impl EvalState { Ok(RealisedString { s, paths }) } + /// Eagerly apply a function to an argument. + pub fn call(&self, f: Value, a: Value) -> Result { + let v = unsafe { + let value = self.new_value_uninitialized(); + raw::value_call( + self.context.ptr(), + self.raw_ptr(), + f.raw_ptr(), + a.raw_ptr(), + value.raw_ptr(), + ); + value + }; + self.context.check_err()?; + Ok(v) + } + fn new_value_uninitialized(&self) -> Value { let value = unsafe { raw::alloc_value(self.context.ptr(), self.raw_ptr()) }; Value::new(value) @@ -490,4 +507,63 @@ mod tests { }) .unwrap(); } + + #[test] + fn eval_state_call() { + gc_registering_current_thread(|| { + let store = Store::open("auto").unwrap(); + let es = EvalState::new(store).unwrap(); + let f = es.eval_from_string("x: x + 1", "").unwrap(); + let a = es.eval_from_string("2", "").unwrap(); + let v = es.call(f, a).unwrap(); + es.force(&v).unwrap(); + let t = es.value_type(&v).unwrap(); + assert!(t == ValueType::Int); + let i = es.require_int(&v).unwrap(); + assert!(i == 3); + }) + .unwrap(); + } + + #[test] + fn eval_state_call_fail_body() { + gc_registering_current_thread(|| { + let store = Store::open("auto").unwrap(); + let es = EvalState::new(store).unwrap(); + let f = es.eval_from_string("x: x + 1", "").unwrap(); + let a = es.eval_from_string("true", "").unwrap(); + let r = es.call(f, a); + match r { + Ok(_) => panic!("expected an error"), + Err(e) => { + if !e.to_string().contains("cannot coerce") { + eprintln!("{}", e); + assert!(false); + } + } + } + }) + .unwrap(); + } + + #[test] + fn eval_state_call_fail_args() { + gc_registering_current_thread(|| { + let store = Store::open("auto").unwrap(); + let es = EvalState::new(store).unwrap(); + let f = es.eval_from_string("{x}: x + 1", "").unwrap(); + let a = es.eval_from_string("{}", "").unwrap(); + let r = es.call(f, a); + match r { + Ok(_) => panic!("expected an error"), + Err(e) => { + if !e.to_string().contains("called without required argument") { + eprintln!("{}", e); + assert!(false); + } + } + } + }) + .unwrap(); + } } From cd5fa278ebe33e740b6db1e97549fb8855d54bf5 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 9 Apr 2024 13:27:27 +0200 Subject: [PATCH 044/306] feat: EvalState.new_value_str (cherry picked from commit 94830e4c23ddc742eb7a70cb3e0c4cf17a1a0231) --- rust/nix-expr/src/eval_state.rs | 62 +++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 006eaa6..e172a6a 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -124,6 +124,19 @@ impl EvalState { Ok(i) } + /// Create a new value containing the passed string. + /// Returns a string value without any string context. + pub fn new_value_str(&self, s: &str) -> Result { + let s = CString::new(s).with_context(|| "new_value_str: contains null byte")?; + let v = unsafe { + let value = self.new_value_uninitialized(); + raw::init_string(self.context.ptr(), value.raw_ptr(), s.as_ptr()); + value + }; + self.context.check_err()?; + Ok(v) + } + /// Not exposed, because the caller must always explicitly handle the context or not accept one at all. fn get_string(&self, value: &Value) -> Result { let mut r = result_string_init!(); @@ -433,6 +446,55 @@ mod tests { .unwrap(); } + #[test] + fn eval_state_new_string() { + gc_registering_current_thread(|| { + let store = Store::open("auto").unwrap(); + let es = EvalState::new(store).unwrap(); + let v = es.new_value_str("hello").unwrap(); + es.force(&v).unwrap(); + let t = es.value_type(&v).unwrap(); + assert!(t == ValueType::String); + let s = es.require_string(&v).unwrap(); + assert!(s == "hello"); + }) + .unwrap(); + } + + #[test] + fn eval_state_new_string_empty() { + gc_registering_current_thread(|| { + let store = Store::open("auto").unwrap(); + let es = EvalState::new(store).unwrap(); + let v = es.new_value_str("").unwrap(); + es.force(&v).unwrap(); + let t = es.value_type(&v).unwrap(); + assert!(t == ValueType::String); + let s = es.require_string(&v).unwrap(); + assert!(s == ""); + }) + .unwrap(); + } + + #[test] + fn eval_state_new_string_invalid() { + gc_registering_current_thread(|| { + let store = Store::open("auto").unwrap(); + let es = EvalState::new(store).unwrap(); + let r = es.new_value_str("hell\0no"); + match r { + Ok(_) => panic!("expected an error"), + Err(e) => { + if !e.to_string().contains("contains null byte") { + eprintln!("{}", e); + assert!(false); + } + } + } + }) + .unwrap(); + } + #[test] fn eval_state_value_attrset() { gc_registering_current_thread(|| { From 024ef333cb40643eaafd2637d402e22b5cf3adda Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 9 Apr 2024 13:29:14 +0200 Subject: [PATCH 045/306] feat: EvalState.new_value_int (cherry picked from commit aa79e4d0db7795ab059349e6f6bc764d17afe003) --- rust/nix-expr/src/eval_state.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index e172a6a..f8c7a14 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -137,6 +137,16 @@ impl EvalState { Ok(v) } + pub fn new_value_int(&self, i: Int) -> Result { + let v = unsafe { + let value = self.new_value_uninitialized(); + raw::init_int(self.context.ptr(), value.raw_ptr(), i); + value + }; + self.context.check_err()?; + Ok(v) + } + /// Not exposed, because the caller must always explicitly handle the context or not accept one at all. fn get_string(&self, value: &Value) -> Result { let mut r = result_string_init!(); @@ -495,6 +505,21 @@ mod tests { .unwrap(); } + #[test] + fn eval_state_new_int() { + gc_registering_current_thread(|| { + let store = Store::open("auto").unwrap(); + let es = EvalState::new(store).unwrap(); + let v = es.new_value_int(42).unwrap(); + es.force(&v).unwrap(); + let t = es.value_type(&v).unwrap(); + assert!(t == ValueType::Int); + let i = es.require_int(&v).unwrap(); + assert!(i == 42); + }) + .unwrap(); + } + #[test] fn eval_state_value_attrset() { gc_registering_current_thread(|| { From 49576f82e4b3c86a258b033ae05369b20cb0b9b4 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 9 Apr 2024 16:20:18 +0200 Subject: [PATCH 046/306] feat: EvalState.require_attrs_names (cherry picked from commit 1f9fdcb3805ecf74e3c0451c11bdfae96f6f997b) --- rust/nix-expr/src/eval_state.rs | 76 +++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index f8c7a14..71ccbcf 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -9,6 +9,7 @@ use nix_util::context::Context; use nix_util::result_string_init; use nix_util::string_return::{callback_get_result_string, callback_get_result_string_data}; use std::ffi::CString; +use std::os::raw::c_uint; use std::ptr::null_mut; use std::ptr::NonNull; @@ -123,6 +124,34 @@ impl EvalState { let i = unsafe { raw::get_int(self.context.ptr(), v.raw_ptr()) }; Ok(i) } + /// Evaluate, and require that the value is an attrset. + /// Returns a list of the keys in the attrset. + pub fn require_attrs_names(&self, v: &Value) -> Result> { + let t = self.value_type(v)?; + if t != ValueType::AttrSet { + bail!("expected an attrset, but got a {:?}", t); + } + let n = unsafe { raw::get_attrs_size(self.context.ptr(), v.raw_ptr()) as usize }; + self.context.check_err()?; + let mut attrs = Vec::with_capacity(n); + unsafe { + for i in 0..n { + let cstr_ptr: *const i8 = raw::get_attr_name_byidx( + self.context.ptr(), + v.raw_ptr(), + self.raw_ptr(), + i as c_uint, + ); + self.context.check_err()?; + let cstr = std::ffi::CStr::from_ptr(cstr_ptr); + let s = cstr.to_str().map_err(|e| { + anyhow::format_err!("Nix attrset key is not valid UTF-8: {}", e) + })?; + attrs.insert(i, s.to_owned()); + } + } + Ok(attrs) + } /// Create a new value containing the passed string. /// Returns a string value without any string context. @@ -366,6 +395,53 @@ mod tests { .unwrap(); } + #[test] + fn eval_state_value_attrs_names_empty() { + gc_registering_current_thread(|| { + let store = Store::open("auto").unwrap(); + let es = EvalState::new(store).unwrap(); + let v = es.eval_from_string("{ }", "").unwrap(); + es.force(&v).unwrap(); + let t = es.value_type(&v).unwrap(); + assert!(t == ValueType::AttrSet); + let attrs = es.require_attrs_names(&v).unwrap(); + assert_eq!(attrs.len(), 0); + }) + .unwrap() + } + + #[test] + fn eval_state_require_attrs_names_bad_type() { + gc_registering_current_thread(|| { + let store = Store::open("auto").unwrap(); + let es = EvalState::new(store).unwrap(); + let v = es.eval_from_string("1", "").unwrap(); + es.force(&v).unwrap(); + let r = es.require_attrs_names(&v); + assert!(r.is_err()); + assert_eq!( + r.unwrap_err().to_string(), + "expected an attrset, but got a Int" + ); + }) + .unwrap() + } + + #[test] + fn eval_state_value_attrs_names_example() { + gc_registering_current_thread(|| { + let store = Store::open("auto").unwrap(); + let es = EvalState::new(store).unwrap(); + let expr = r#"{ a = throw "nope a"; b = throw "nope b"; }"#; + let v = es.eval_from_string(expr, "").unwrap(); + let attrs = es.require_attrs_names(&v).unwrap(); + assert_eq!(attrs.len(), 2); + assert_eq!(attrs[0], "a"); + assert_eq!(attrs[1], "b"); + }) + .unwrap(); + } + #[test] fn eval_state_value_string() { gc_registering_current_thread(|| { From d38cf51c63061cfe44d67bd1bf93694c8a89b41e Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 10 Apr 2024 14:24:01 +0200 Subject: [PATCH 047/306] feat: Context.is_key_error (cherry picked from commit f754e243852103164dbc6fdaf8c0cccb3c85cfb1) --- rust/nix-util/src/context.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rust/nix-util/src/context.rs b/rust/nix-util/src/context.rs index 772511e..73540ca 100644 --- a/rust/nix-util/src/context.rs +++ b/rust/nix-util/src/context.rs @@ -34,6 +34,10 @@ impl Context { } Ok(()) } + /// NIX_ERR_KEY is returned when e.g. an attribute is missing. Return true if the error is of this type. + pub fn is_key_error(&self) -> bool { + unsafe { raw::err_code(self.inner.as_ptr()) == raw::NIX_ERR_KEY } + } } impl Drop for Context { From 65f411d35e0092858c018dfa0d2b25bd5680c308 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 10 Apr 2024 14:26:28 +0200 Subject: [PATCH 048/306] maint: Move conditionally unused use (cherry picked from commit 21e3f3f569758b6cbf668c27330675c579efff5c) --- rust/nix-util/src/settings.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/rust/nix-util/src/settings.rs b/rust/nix-util/src/settings.rs index 36964ad..b889f63 100644 --- a/rust/nix-util/src/settings.rs +++ b/rust/nix-util/src/settings.rs @@ -35,6 +35,7 @@ pub fn get(key: &str) -> Result { #[cfg(test)] mod tests { use super::*; + use ctor::ctor; #[ctor::ctor] fn setup() { From 4095bda02aa879de9bc71ef093d5a02739db326b Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 10 Apr 2024 14:32:42 +0200 Subject: [PATCH 049/306] feat: EvalState.require_attrs_select{,_opt} (cherry picked from commit cbba454aba1a90bb862d49925edd13820b6ace1b) --- rust/nix-expr/src/eval_state.rs | 110 ++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 71ccbcf..d5fd4f7 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -153,6 +153,42 @@ impl EvalState { Ok(attrs) } + pub fn require_attrs_select(&self, v: &Value, attr_name: &str) -> Result { + let r = self.require_attrs_select_opt(v, attr_name)?; + match r { + Some(v) => Ok(v), + None => self.context.check_err().and_then(|_| { + // should be unreachable + bail!("attribute not found: {}", attr_name) + }), + } + } + + /// Evaluate, require that the value is an attrset, and select an attribute by name. + pub fn require_attrs_select_opt(&self, v: &Value, attr_name: &str) -> Result> { + let t = self.value_type(v)?; + if t != ValueType::AttrSet { + bail!("expected an attrset, but got a {:?}", t); + } + let attr_name = CString::new(attr_name) + .with_context(|| "require_attrs_select_opt: attrName contains null byte")?; + // c_void should be Value; why was void generated? + let v = unsafe { + raw::get_attr_byname( + self.context.ptr(), + v.raw_ptr(), + self.raw_ptr(), + attr_name.as_ptr(), + ) + }; + if self.context.is_key_error() { + Ok(None) + } else { + self.context.check_err()?; + Ok(Some(Value::new(v))) + } + } + /// Create a new value containing the passed string. /// Returns a string value without any string context. pub fn new_value_str(&self, s: &str) -> Result { @@ -442,6 +478,80 @@ mod tests { .unwrap(); } + #[test] + fn eval_state_require_attrs_select() { + gc_registering_current_thread(|| { + let store = Store::open("auto").unwrap(); + let es = EvalState::new(store).unwrap(); + let expr = r#"{ a = "aye"; b = "bee"; }"#; + let v = es.eval_from_string(expr, "").unwrap(); + let a = es.require_attrs_select(&v, "a").unwrap(); + let b = es.require_attrs_select(&v, "b").unwrap(); + assert_eq!(es.require_string(&a).unwrap(), "aye"); + assert_eq!(es.require_string(&b).unwrap(), "bee"); + }) + .unwrap() + } + + #[test] + fn eval_state_require_attrs_select_error() { + gc_registering_current_thread(|| { + let store = Store::open("auto").unwrap(); + let es = EvalState::new(store).unwrap(); + let expr = r#"{ a = throw "oh no the error"; }"#; + let v = es.eval_from_string(expr, "").unwrap(); + let r = es.require_attrs_select(&v, "a"); + match r { + Ok(_) => panic!("expected an error"), + Err(e) => { + if !e.to_string().contains("oh no the error") { + eprintln!("unexpected error message: {}", e); + assert!(false); + } + } + } + }) + .unwrap() + } + + #[test] + fn eval_state_require_attrs_select_opt() { + gc_registering_current_thread(|| { + let store = Store::open("auto").unwrap(); + let es = EvalState::new(store).unwrap(); + let expr = r#"{ a = "aye"; b = "bee"; }"#; + let v = es.eval_from_string(expr, "").unwrap(); + let a = es.require_attrs_select_opt(&v, "a").unwrap().unwrap(); + let b = es.require_attrs_select_opt(&v, "b").unwrap().unwrap(); + assert_eq!(es.require_string(&a).unwrap(), "aye"); + assert_eq!(es.require_string(&b).unwrap(), "bee"); + let c = es.require_attrs_select_opt(&v, "c").unwrap(); + assert!(c.is_none()); + }) + .unwrap() + } + + #[test] + fn eval_state_require_attrs_select_opt_error() { + gc_registering_current_thread(|| { + let store = Store::open("auto").unwrap(); + let es = EvalState::new(store).unwrap(); + let expr = r#"{ a = throw "oh no the error"; }"#; + let v = es.eval_from_string(expr, "").unwrap(); + let r = es.require_attrs_select_opt(&v, "a"); + match r { + Ok(_) => panic!("expected an error"), + Err(e) => { + if !e.to_string().contains("oh no the error") { + eprintln!("unexpected error message: {}", e); + assert!(false); + } + } + } + }) + .unwrap() + } + #[test] fn eval_state_value_string() { gc_registering_current_thread(|| { From 726e891be8fb55880fbe0d642a5e691a238bf3ff Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 10 Apr 2024 14:34:51 +0200 Subject: [PATCH 050/306] feat: eval_state::test_init (cherry picked from commit 040982222c9eb8ea91d7174e6f2e21bbc6cda8a4) --- rust/nix-expr/src/eval_state.rs | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index d5fd4f7..854cb65 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -353,27 +353,32 @@ impl Drop for EvalState { } } +/// Initialize the Nix library for testing. This includes some modifications to the Nix settings, that must not be used in production. +/// Use at your own peril, in rust test suites. +pub fn test_init() { + init().unwrap(); + + // During development, we encountered a problem where the build hook + // would cause the test suite to reinvokes itself, causing an infinite loop. + // While _NIX_TEST_NO_SANDBOX=1 should prevent this, we may also set the + // build hook to "" to prevent this. + // settings::set("build-hook", "")?; + + // When testing in the sandbox, the default build dir would be a parent of the storeDir, + // which causes an error. So we set a custom build dir here. + nix_util::settings::set("sandbox-build-dir", "/custom-build-dir-for-test").unwrap(); + std::env::set_var("_NIX_TEST_NO_SANDBOX", "1"); +} + #[cfg(test)] mod tests { use ctor::ctor; - use nix_util::settings; use super::*; #[ctor] fn setup() { - init().unwrap(); - - // During development, we encountered a problem where the build hook - // would cause the test suite to reinvokes itself, causing an infinite loop. - // While _NIX_TEST_NO_SANDBOX=1 should prevent this, we may also set the - // build hook to "" to prevent this. - // settings::set("build-hook", "")?; - - // When testing in the sandbox, the default build dir would be a parent of the storeDir, - // which causes an error. So we set a custom build dir here. - settings::set("sandbox-build-dir", "/custom-build-dir-for-test").unwrap(); - std::env::set_var("_NIX_TEST_NO_SANDBOX", "1"); + test_init(); } #[test] From d817e86c59138c4b316aff31593cae4a8d3ccdf5 Mon Sep 17 00:00:00 2001 From: Philipp Zander Date: Mon, 13 May 2024 19:26:58 +0200 Subject: [PATCH 051/306] feat: support `nix_state_create`'s `lookupPath` argument (cherry picked from commit 6d0906688ea40760217c2e017a4a0a61eb0fd611) --- rust/nix-expr/src/eval_state.rs | 86 +++++++++++++++++++-------------- 1 file changed, 49 insertions(+), 37 deletions(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 854cb65..b473629 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -8,10 +8,9 @@ use nix_store::store::Store; use nix_util::context::Context; use nix_util::result_string_init; use nix_util::string_return::{callback_get_result_string, callback_get_result_string_data}; -use std::ffi::CString; +use std::ffi::{c_char, CString}; use std::os::raw::c_uint; -use std::ptr::null_mut; -use std::ptr::NonNull; +use std::ptr::{null, null_mut, NonNull}; lazy_static! { static ref INIT: Result<()> = { @@ -48,18 +47,31 @@ pub struct EvalState { context: Context, } impl EvalState { - pub fn new(store: Store) -> Result { + pub fn new<'a>(store: Store, lookup_path: impl IntoIterator) -> Result { let context = Context::new(); + // this intermediate value must be here and must not be moved + // because it owns the data the `*const c_char` pointers point to. + let lookup_path: Vec = lookup_path + .into_iter() + .map(|path| { + CString::new(path).with_context(|| { + format!("EvalState::new: lookup_path `{path}` contains null byte") + }) + }) + .collect::>()?; + + // this intermediate value owns the data the `*mut *const c_char` pointer points to. + let mut lookup_path: Vec<*const c_char> = lookup_path + .iter() + .map(|s| s.as_ptr()) + .chain(std::iter::once(null())) // signal the end of the array + .collect(); + init()?; - let eval_state = unsafe { - raw::state_create( - context.ptr(), - /* searchPath */ null_mut(), - store.raw_ptr(), - ) - }; + let eval_state = + unsafe { raw::state_create(context.ptr(), lookup_path.as_mut_ptr(), store.raw_ptr()) }; context.check_err()?; if eval_state.is_null() { panic!("nix_state_create returned a null pointer without an error"); @@ -386,7 +398,7 @@ mod tests { gc_registering_current_thread(|| { // very basic test: make sure initialization doesn't crash let store = Store::open("auto").unwrap(); - let _e = EvalState::new(store).unwrap(); + let _e = EvalState::new(store, []).unwrap(); }) .unwrap(); } @@ -395,7 +407,7 @@ mod tests { fn eval_state_eval_from_string() { gc_registering_current_thread(|| { let store = Store::open("auto").unwrap(); - let es = EvalState::new(store).unwrap(); + let es = EvalState::new(store, []).unwrap(); let v = es.eval_from_string("1", "").unwrap(); let v2 = v.clone(); es.force(&v).unwrap(); @@ -412,7 +424,7 @@ mod tests { fn eval_state_value_bool() { gc_registering_current_thread(|| { let store = Store::open("auto").unwrap(); - let es = EvalState::new(store).unwrap(); + let es = EvalState::new(store, []).unwrap(); let v = es.eval_from_string("true", "").unwrap(); es.force(&v).unwrap(); let t = es.value_type(&v).unwrap(); @@ -425,7 +437,7 @@ mod tests { fn eval_state_value_int() { gc_registering_current_thread(|| { let store = Store::open("auto").unwrap(); - let es = EvalState::new(store).unwrap(); + let es = EvalState::new(store, []).unwrap(); let v = es.eval_from_string("1", "").unwrap(); es.force(&v).unwrap(); let t = es.value_type(&v).unwrap(); @@ -440,7 +452,7 @@ mod tests { fn eval_state_value_attrs_names_empty() { gc_registering_current_thread(|| { let store = Store::open("auto").unwrap(); - let es = EvalState::new(store).unwrap(); + let es = EvalState::new(store, []).unwrap(); let v = es.eval_from_string("{ }", "").unwrap(); es.force(&v).unwrap(); let t = es.value_type(&v).unwrap(); @@ -455,7 +467,7 @@ mod tests { fn eval_state_require_attrs_names_bad_type() { gc_registering_current_thread(|| { let store = Store::open("auto").unwrap(); - let es = EvalState::new(store).unwrap(); + let es = EvalState::new(store, []).unwrap(); let v = es.eval_from_string("1", "").unwrap(); es.force(&v).unwrap(); let r = es.require_attrs_names(&v); @@ -472,7 +484,7 @@ mod tests { fn eval_state_value_attrs_names_example() { gc_registering_current_thread(|| { let store = Store::open("auto").unwrap(); - let es = EvalState::new(store).unwrap(); + let es = EvalState::new(store, []).unwrap(); let expr = r#"{ a = throw "nope a"; b = throw "nope b"; }"#; let v = es.eval_from_string(expr, "").unwrap(); let attrs = es.require_attrs_names(&v).unwrap(); @@ -487,7 +499,7 @@ mod tests { fn eval_state_require_attrs_select() { gc_registering_current_thread(|| { let store = Store::open("auto").unwrap(); - let es = EvalState::new(store).unwrap(); + let es = EvalState::new(store, []).unwrap(); let expr = r#"{ a = "aye"; b = "bee"; }"#; let v = es.eval_from_string(expr, "").unwrap(); let a = es.require_attrs_select(&v, "a").unwrap(); @@ -502,7 +514,7 @@ mod tests { fn eval_state_require_attrs_select_error() { gc_registering_current_thread(|| { let store = Store::open("auto").unwrap(); - let es = EvalState::new(store).unwrap(); + let es = EvalState::new(store, []).unwrap(); let expr = r#"{ a = throw "oh no the error"; }"#; let v = es.eval_from_string(expr, "").unwrap(); let r = es.require_attrs_select(&v, "a"); @@ -523,7 +535,7 @@ mod tests { fn eval_state_require_attrs_select_opt() { gc_registering_current_thread(|| { let store = Store::open("auto").unwrap(); - let es = EvalState::new(store).unwrap(); + let es = EvalState::new(store, []).unwrap(); let expr = r#"{ a = "aye"; b = "bee"; }"#; let v = es.eval_from_string(expr, "").unwrap(); let a = es.require_attrs_select_opt(&v, "a").unwrap().unwrap(); @@ -540,7 +552,7 @@ mod tests { fn eval_state_require_attrs_select_opt_error() { gc_registering_current_thread(|| { let store = Store::open("auto").unwrap(); - let es = EvalState::new(store).unwrap(); + let es = EvalState::new(store, []).unwrap(); let expr = r#"{ a = throw "oh no the error"; }"#; let v = es.eval_from_string(expr, "").unwrap(); let r = es.require_attrs_select_opt(&v, "a"); @@ -561,7 +573,7 @@ mod tests { fn eval_state_value_string() { gc_registering_current_thread(|| { let store = Store::open("auto").unwrap(); - let es = EvalState::new(store).unwrap(); + let es = EvalState::new(store, []).unwrap(); let v = es.eval_from_string("\"hello\"", "").unwrap(); es.force(&v).unwrap(); let t = es.value_type(&v).unwrap(); @@ -576,7 +588,7 @@ mod tests { fn eval_state_value_string_unexpected_bool() { gc_registering_current_thread(|| { let store = Store::open("auto").unwrap(); - let es = EvalState::new(store).unwrap(); + let es = EvalState::new(store, []).unwrap(); let v = es.eval_from_string("true", "").unwrap(); es.force(&v).unwrap(); let r = es.require_string(&v); @@ -594,7 +606,7 @@ mod tests { fn eval_state_value_string_unexpected_path_value() { gc_registering_current_thread(|| { let store = Store::open("auto").unwrap(); - let es = EvalState::new(store).unwrap(); + let es = EvalState::new(store, []).unwrap(); let v = es.eval_from_string("/foo", "").unwrap(); es.force(&v).unwrap(); let r = es.require_string(&v); @@ -611,7 +623,7 @@ mod tests { fn eval_state_value_string_bad_utf() { gc_registering_current_thread(|| { let store = Store::open("auto").unwrap(); - let es = EvalState::new(store).unwrap(); + let es = EvalState::new(store, []).unwrap(); let v = es .eval_from_string("builtins.substring 0 1 \"ü\"", "") .unwrap(); @@ -632,7 +644,7 @@ mod tests { fn eval_state_value_string_unexpected_context() { gc_registering_current_thread(|| { let store = Store::open("auto").unwrap(); - let es = EvalState::new(store).unwrap(); + let es = EvalState::new(store, []).unwrap(); let v = es .eval_from_string("(derivation { name = \"hello\"; system = \"dummy\"; builder = \"cmd.exe\"; }).outPath", "") .unwrap(); @@ -651,7 +663,7 @@ mod tests { fn eval_state_new_string() { gc_registering_current_thread(|| { let store = Store::open("auto").unwrap(); - let es = EvalState::new(store).unwrap(); + let es = EvalState::new(store, []).unwrap(); let v = es.new_value_str("hello").unwrap(); es.force(&v).unwrap(); let t = es.value_type(&v).unwrap(); @@ -666,7 +678,7 @@ mod tests { fn eval_state_new_string_empty() { gc_registering_current_thread(|| { let store = Store::open("auto").unwrap(); - let es = EvalState::new(store).unwrap(); + let es = EvalState::new(store, []).unwrap(); let v = es.new_value_str("").unwrap(); es.force(&v).unwrap(); let t = es.value_type(&v).unwrap(); @@ -681,7 +693,7 @@ mod tests { fn eval_state_new_string_invalid() { gc_registering_current_thread(|| { let store = Store::open("auto").unwrap(); - let es = EvalState::new(store).unwrap(); + let es = EvalState::new(store, []).unwrap(); let r = es.new_value_str("hell\0no"); match r { Ok(_) => panic!("expected an error"), @@ -700,7 +712,7 @@ mod tests { fn eval_state_new_int() { gc_registering_current_thread(|| { let store = Store::open("auto").unwrap(); - let es = EvalState::new(store).unwrap(); + let es = EvalState::new(store, []).unwrap(); let v = es.new_value_int(42).unwrap(); es.force(&v).unwrap(); let t = es.value_type(&v).unwrap(); @@ -715,7 +727,7 @@ mod tests { fn eval_state_value_attrset() { gc_registering_current_thread(|| { let store = Store::open("auto").unwrap(); - let es = EvalState::new(store).unwrap(); + let es = EvalState::new(store, []).unwrap(); let v = es.eval_from_string("{ }", "").unwrap(); es.force(&v).unwrap(); let t = es.value_type(&v).unwrap(); @@ -728,7 +740,7 @@ mod tests { fn eval_state_value_list() { gc_registering_current_thread(|| { let store = Store::open("auto").unwrap(); - let es = EvalState::new(store).unwrap(); + let es = EvalState::new(store, []).unwrap(); let v = es .eval_from_string("[ ]", "") .unwrap(); @@ -743,7 +755,7 @@ mod tests { fn eval_state_realise_string() { gc_registering_current_thread(|| { let store = Store::open("auto").unwrap(); - let es = EvalState::new(store).unwrap(); + let es = EvalState::new(store, []).unwrap(); let expr = r#" '' a derivation output: ${ @@ -790,7 +802,7 @@ mod tests { fn eval_state_call() { gc_registering_current_thread(|| { let store = Store::open("auto").unwrap(); - let es = EvalState::new(store).unwrap(); + let es = EvalState::new(store, []).unwrap(); let f = es.eval_from_string("x: x + 1", "").unwrap(); let a = es.eval_from_string("2", "").unwrap(); let v = es.call(f, a).unwrap(); @@ -807,7 +819,7 @@ mod tests { fn eval_state_call_fail_body() { gc_registering_current_thread(|| { let store = Store::open("auto").unwrap(); - let es = EvalState::new(store).unwrap(); + let es = EvalState::new(store, []).unwrap(); let f = es.eval_from_string("x: x + 1", "").unwrap(); let a = es.eval_from_string("true", "").unwrap(); let r = es.call(f, a); @@ -828,7 +840,7 @@ mod tests { fn eval_state_call_fail_args() { gc_registering_current_thread(|| { let store = Store::open("auto").unwrap(); - let es = EvalState::new(store).unwrap(); + let es = EvalState::new(store, []).unwrap(); let f = es.eval_from_string("{x}: x + 1", "").unwrap(); let a = es.eval_from_string("{}", "").unwrap(); let r = es.call(f, a); From 9a62664fb1d867c7e9d2bf11664ad5dddcafd046 Mon Sep 17 00:00:00 2001 From: Philipp Zander Date: Mon, 13 May 2024 19:29:48 +0200 Subject: [PATCH 052/306] test `nix_state_create`'s `lookupPath` argument (cherry picked from commit 6e5a259f60725b632e52d4839e6e3c285d6971d4) --- rust/Cargo.lock | 19 +++++++++++++++++++ rust/nix-expr/Cargo.toml | 3 ++- rust/nix-expr/src/eval_state.rs | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/rust/Cargo.lock b/rust/Cargo.lock index fe37409..f9800e7 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -98,6 +98,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "fastrand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" + [[package]] name = "glob" version = "0.3.1" @@ -192,6 +198,7 @@ dependencies = [ "nix-c-raw", "nix-store", "nix-util", + "tempfile", ] [[package]] @@ -339,6 +346,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys 0.52.0", +] + [[package]] name = "unicode-ident" version = "1.0.12" diff --git a/rust/nix-expr/Cargo.toml b/rust/nix-expr/Cargo.toml index 66a1f25..2733579 100644 --- a/rust/nix-expr/Cargo.toml +++ b/rust/nix-expr/Cargo.toml @@ -11,4 +11,5 @@ nix-store = { path = "../nix-store" } nix-util = { path = "../nix-util" } nix-c-raw = { path = "../nix-c-raw" } lazy_static = "1.4.0" -ctor = "0.2.7" \ No newline at end of file +ctor = "0.2.7" +tempfile = "3.10.1" diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index b473629..935d75a 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -385,6 +385,7 @@ pub fn test_init() { #[cfg(test)] mod tests { use ctor::ctor; + use std::io::Write as _; use super::*; @@ -403,6 +404,37 @@ mod tests { .unwrap(); } + #[test] + fn eval_state_lookup_path() { + let import_expression = "import + import "; + let integer0 = 83; + let integer1 = 103; + let mut test_file0 = tempfile::NamedTempFile::new().unwrap(); + let mut test_file1 = tempfile::NamedTempFile::new().unwrap(); + writeln!(test_file0, "{integer0}").unwrap(); + writeln!(test_file1, "{integer1}").unwrap(); + gc_registering_current_thread(|| { + let es = EvalState::new(Store::open("auto").unwrap(), []).unwrap(); + assert!(es.eval_from_string(import_expression, "").is_err()); + + let es = EvalState::new( + Store::open("auto").unwrap(), + [ + format!("test_file0={}", test_file0.path().to_str().unwrap()).as_str(), + format!("test_file1={}", test_file1.path().to_str().unwrap()).as_str(), + ], + ) + .unwrap(); + let v = es + .require_int(&es.eval_from_string(import_expression, "").unwrap()) + .unwrap(); + assert_eq!(v, integer0 + integer1); + }) + .unwrap(); + test_file0.close().unwrap(); + test_file1.close().unwrap(); + } + #[test] fn eval_state_eval_from_string() { gc_registering_current_thread(|| { From 1ac0f10f74091ae9e6bbca6abbd7c456f76485ef Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 23 May 2024 14:04:51 +0200 Subject: [PATCH 053/306] refact: Fix warning (cherry picked from commit e9371a0838eec93a0158a408c58be1a1efd44ab2) --- rust/nix-util/src/settings.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/rust/nix-util/src/settings.rs b/rust/nix-util/src/settings.rs index b889f63..36964ad 100644 --- a/rust/nix-util/src/settings.rs +++ b/rust/nix-util/src/settings.rs @@ -35,7 +35,6 @@ pub fn get(key: &str) -> Result { #[cfg(test)] mod tests { use super::*; - use ctor::ctor; #[ctor::ctor] fn setup() { From 8fcc645c5d003b96f80be67b785391197c6a4606 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 24 May 2024 09:54:15 +0200 Subject: [PATCH 054/306] maint: Update nix to master (cherry picked from commit 941dfa89024bde628dc1add30442d1d122a82ffb) --- flake.lock | 11 +++++------ flake.nix | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/flake.lock b/flake.lock index 65bd0dc..70d82fa 100644 --- a/flake.lock +++ b/flake.lock @@ -214,16 +214,15 @@ "pre-commit-hooks": "pre-commit-hooks" }, "locked": { - "lastModified": 1714582044, - "narHash": "sha256-+I7oZyclF/qcQxJYCsKRfYDk5Yzo/Xug/XhI+bSrnx8=", - "owner": "hercules-ci", + "lastModified": 1716532322, + "narHash": "sha256-Lsd3e8xDtAfo/RgpzYgi1P2Wkp1DqTP3n8TI1BK1JPY=", + "owner": "NixOS", "repo": "nix", - "rev": "38974360102e67aaf2434fd3f920e2cd1bb3fa75", + "rev": "c90a763273f94ec5e82f21bfb2b359d04af86383", "type": "github" }, "original": { - "owner": "hercules-ci", - "ref": "fix-eval-state-baseEnv-gc-root", + "owner": "NixOS", "repo": "nix", "type": "github" } diff --git a/flake.nix b/flake.nix index fc0ad04..b231f62 100644 --- a/flake.nix +++ b/flake.nix @@ -4,7 +4,7 @@ inputs = { flake-parts.url = "github:hercules-ci/flake-parts"; flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs"; - nix.url = "github:hercules-ci/nix/fix-eval-state-baseEnv-gc-root"; + nix.url = "github:NixOS/nix"; nix.inputs.nixpkgs.follows = "nixpkgs"; nix-cargo-integration.url = "github:yusdacra/nix-cargo-integration"; nix-cargo-integration.inputs.nixpkgs.follows = "nixpkgs"; From 78435d4ed78c9a31de612108df4d4a850b3565cc Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 24 May 2024 09:34:20 +0200 Subject: [PATCH 055/306] feat: EvalState::new_value_apply (cherry picked from commit f8143ae4ed7b62b86cb232f8d73e53bae30d632c) --- rust/nix-expr/src/eval_state.rs | 84 +++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 935d75a..c9d4728 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -293,6 +293,8 @@ impl EvalState { } /// Eagerly apply a function to an argument. + /// + /// For a lazy version, see [`new_value_apply`][`EvalState::new_value_apply`]. pub fn call(&self, f: Value, a: Value) -> Result { let v = unsafe { let value = self.new_value_uninitialized(); @@ -309,6 +311,23 @@ impl EvalState { Ok(v) } + /// Apply a function to an argument, but don't evaluate the result just yet. + /// + /// For an eager version, see [`call`][`EvalState::call`]. + pub fn new_value_apply(&self, f: &Value, a: &Value) -> Result { + let value = self.new_value_uninitialized(); + unsafe { + raw::init_apply( + self.context.ptr(), + value.raw_ptr(), + f.raw_ptr(), + a.raw_ptr(), + ); + }; + self.context.check_err()?; + Ok(value) + } + fn new_value_uninitialized(&self) -> Value { let value = unsafe { raw::alloc_value(self.context.ptr(), self.raw_ptr()) }; Value::new(value) @@ -847,6 +866,23 @@ mod tests { .unwrap(); } + #[test] + fn eval_state_apply() { + gc_registering_current_thread(|| { + let store = Store::open("auto").unwrap(); + let es = EvalState::new(store, []).unwrap(); + // This is a function that takes two arguments. + let f = es.eval_from_string("x: x + 1", "").unwrap(); + let a = es.eval_from_string("2", "").unwrap(); + let v = es.new_value_apply(&f, &a).unwrap(); + assert!(es.value_is_thunk(&v)); + es.force(&v).unwrap(); + let i = es.require_int(&v).unwrap(); + assert!(i == 3); + }) + .unwrap(); + } + #[test] fn eval_state_call_fail_body() { gc_registering_current_thread(|| { @@ -868,6 +904,30 @@ mod tests { .unwrap(); } + #[test] + fn eval_state_apply_fail_body() { + gc_registering_current_thread(|| { + let store = Store::open("auto").unwrap(); + let es = EvalState::new(store, []).unwrap(); + let f = es.eval_from_string("x: x + 1", "").unwrap(); + let a = es.eval_from_string("true", "").unwrap(); + // Lazy => no error + let r = es.new_value_apply(&f, &a).unwrap(); + // Force it => error + let res = es.force(&r); + match res { + Ok(_) => panic!("expected an error"), + Err(e) => { + if !e.to_string().contains("cannot coerce") { + eprintln!("{}", e); + assert!(false); + } + } + } + }) + .unwrap(); + } + #[test] fn eval_state_call_fail_args() { gc_registering_current_thread(|| { @@ -888,4 +948,28 @@ mod tests { }) .unwrap(); } + + #[test] + fn eval_state_apply_fail_args() { + gc_registering_current_thread(|| { + let store = Store::open("auto").unwrap(); + let es = EvalState::new(store, []).unwrap(); + let f = es.eval_from_string("{x}: x + 1", "").unwrap(); + let a = es.eval_from_string("{}", "").unwrap(); + // Lazy => no error + let r = es.new_value_apply(&f, &a).unwrap(); + // Force it => error + let res = es.force(&r); + match res { + Ok(_) => panic!("expected an error"), + Err(e) => { + if !e.to_string().contains("called without required argument") { + eprintln!("{}", e); + assert!(false); + } + } + } + }) + .unwrap(); + } } From ffdbf49ec52b7072ef1fa090415231c402d65b67 Mon Sep 17 00:00:00 2001 From: Philipp Zander Date: Fri, 24 May 2024 10:27:59 +0200 Subject: [PATCH 056/306] test: Add assertion in eval_state::tests::eval_state_apply (cherry picked from commit f8ff14274b6f7accf987328928186b25f842071f) --- rust/nix-expr/src/eval_state.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index c9d4728..647b466 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -877,6 +877,8 @@ mod tests { let v = es.new_value_apply(&f, &a).unwrap(); assert!(es.value_is_thunk(&v)); es.force(&v).unwrap(); + let t = es.value_type(&v).unwrap(); + assert!(t == ValueType::Int); let i = es.require_int(&v).unwrap(); assert!(i == 3); }) From 37da6411d03b27cacc7df69de31383228d4d1953 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 24 May 2024 11:15:43 +0200 Subject: [PATCH 057/306] maint: Use /// for doc comments (cherry picked from commit 4a390197f71653dda99696ed5c9cbca8c6a95a4b) --- rust/nix-expr/src/eval_state.rs | 4 ++-- rust/nix-expr/src/value.rs | 2 +- rust/nix-store/src/path.rs | 18 +++++++----------- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 647b466..6232114 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -107,7 +107,7 @@ impl EvalState { self.context.check_err()?; Ok(value) } - /** Try turn any Value into a Value that isn't a Thunk. */ + /// Try turn any Value into a Value that isn't a Thunk. pub fn force(&self, v: &Value) -> Result<()> { unsafe { raw::value_force(self.context.ptr(), self.raw_ptr(), v.raw_ptr()); @@ -340,7 +340,7 @@ pub fn gc_now() { } } -/** Run a function while making sure that the current thread is registered with the GC. */ +/// Run a function while making sure that the current thread is registered with the GC. pub fn gc_registering_current_thread(f: F) -> Result where F: FnOnce() -> R, diff --git a/rust/nix-expr/src/value.rs b/rust/nix-expr/src/value.rs index 88f5f76..46371a2 100644 --- a/rust/nix-expr/src/value.rs +++ b/rust/nix-expr/src/value.rs @@ -6,7 +6,7 @@ use std::ptr::{null_mut, NonNull}; pub type Int = i64; -/** The type of a value (or thunk) */ +/// The type of a value (or thunk) #[derive(Eq, PartialEq, Debug)] pub enum ValueType { AttrSet, diff --git a/rust/nix-store/src/path.rs b/rust/nix-store/src/path.rs index 17e136d..ad1a0f3 100644 --- a/rust/nix-store/src/path.rs +++ b/rust/nix-store/src/path.rs @@ -9,20 +9,16 @@ pub struct StorePath { raw: *mut raw::StorePath, } impl StorePath { - /** - * This is a low level function that you shouldn't have to call unless you are developing the Nix bindings. - * - * Construct a new `StorePath` by first cloning the C store path. - * This does not take ownership of the C store path, so it should be a borrowed value, or you should free it. - */ + /// This is a low level function that you shouldn't have to call unless you are developing the Nix bindings. + /// + /// Construct a new `StorePath` by first cloning the C store path. + /// This does not take ownership of the C store path, so it should be a borrowed value, or you should free it. pub fn new_raw_clone(raw: *const raw::StorePath) -> Self { Self::new_raw(unsafe { raw::store_path_clone(raw as *mut raw::StorePath) }) } - /** - * This is a low level function that you shouldn't have to call unless you are developing the Nix bindings. - * - * Takes ownership of a C `nix_store_path`. It will be freed when the `StorePath` is dropped. - */ + /// This is a low level function that you shouldn't have to call unless you are developing the Nix bindings. + /// + /// Takes ownership of a C `nix_store_path`. It will be freed when the `StorePath` is dropped. pub fn new_raw(raw: *mut raw::StorePath) -> Self { StorePath { raw } } From 6b438e5398bdfa72b60a60ddcea9ad8774dac0ae Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 24 May 2024 11:45:39 +0200 Subject: [PATCH 058/306] dev: Check that we use ///-style doc comments in Rust (cherry picked from commit 395a4d7a46e8489ca98667fca3db8aebf998a9b6) --- flake.nix | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/flake.nix b/flake.nix index b231f62..237a4bf 100644 --- a/flake.nix +++ b/flake.nix @@ -35,6 +35,26 @@ # New configuration for rustfmt pre-commit.settings.settings.rust.cargoManifestPath = "./rust/Cargo.toml"; + # Check that we're using ///-style doc comments in Rust code. + # + # Unfortunately, rustfmt won't do this for us yet - at least not + # without nightly, and it might do too much. + pre-commit.settings.hooks.rust-doc-comments = { + enable = true; + files = ".*.rs$"; + entry = "${pkgs.writeScript "rust-doc-comments" '' + #!${pkgs.runtimeShell} + set -uxo pipefail + grep -n -C3 --color=always -F '/**' "$@" + r=$? + set -e + if [ $r -eq 0 ]; then + echo "Please replace /**-style comments by /// style comments in Rust code." + exit 1 + fi + ''}"; + }; + devShells.default = pkgs.mkShell { name = "nix-bindings-devshell"; strictDeps = true; From 65451efd4bfdb4f3b0d8c2ca9005e7643e71cadd Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 14 Jun 2024 15:02:13 +0200 Subject: [PATCH 059/306] dev: Improve comment check file name regex Co-authored-by: Taeer Bar-Yam (cherry picked from commit b401e52eba931374b782d83360411b197a8d59da) --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 237a4bf..ea9ae1f 100644 --- a/flake.nix +++ b/flake.nix @@ -41,7 +41,7 @@ # without nightly, and it might do too much. pre-commit.settings.hooks.rust-doc-comments = { enable = true; - files = ".*.rs$"; + files = "\\.rs$"; entry = "${pkgs.writeScript "rust-doc-comments" '' #!${pkgs.runtimeShell} set -uxo pipefail From de09abb0849afab2455a162e988397cca9c04372 Mon Sep 17 00:00:00 2001 From: Philipp Zander Date: Fri, 24 May 2024 00:16:10 +0200 Subject: [PATCH 060/306] support `nix_store_open`'s `params` argument (cherry picked from commit 3dd5137e33709559ffa152feb7ab508246adfb54) --- rust/nix-store/src/store.rs | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/rust/nix-store/src/store.rs b/rust/nix-store/src/store.rs index 98fc79d..7440ff2 100644 --- a/rust/nix-store/src/store.rs +++ b/rust/nix-store/src/store.rs @@ -4,7 +4,7 @@ use nix_c_raw as raw; use nix_util::context::Context; use nix_util::result_string_init; use nix_util::string_return::{callback_get_result_string, callback_get_result_string_data}; -use std::ffi::CString; +use std::ffi::{c_char, CString}; use std::ptr::null_mut; use std::ptr::NonNull; @@ -41,7 +41,10 @@ pub struct Store { context: Context, } impl Store { - pub fn open(url: &str) -> Result { + pub fn open<'a, 'b>( + url: &str, + params: impl IntoIterator, + ) -> Result { let x = INIT.as_ref(); match x { Ok(_) => {} @@ -54,13 +57,27 @@ impl Store { let context: Context = Context::new(); let uri_ptr = CString::new(url)?; - let store = unsafe { - raw::store_open( - context.ptr(), - uri_ptr.as_ptr(), - null_mut::<*mut *const i8>(), - ) - }; + + // this intermediate value must be here and must not be moved + // because it owns the data the `*const c_char` pointers point to. + let params: Vec<(CString, CString)> = params + .into_iter() + .map(|(k, v)| Ok((CString::new(k)?, CString::new(v)?))) // to do. context + .collect::>()?; + // this intermediate value owns the data the `*mut *const c_char` pointer points to. + let mut params: Vec<_> = params + .iter() + .map(|(k, v)| [k.as_ptr(), v.as_ptr()]) + .collect(); + // this intermediate value owns the data the `*mut *mut *const c_char` pointer points to. + let mut params: Vec<*mut *const c_char> = params + .iter_mut() + .map(|t| t.as_mut_ptr()) + .chain(std::iter::once(null_mut())) // signal the end of the array + .collect(); + + let store = + unsafe { raw::store_open(context.ptr(), uri_ptr.as_ptr(), params.as_mut_ptr()) }; context.check_err()?; if store.is_null() { panic!("nix_c_store_open returned a null pointer without an error"); From ec281768522e80304c455927a23fdab49726711c Mon Sep 17 00:00:00 2001 From: Philipp Zander Date: Fri, 24 May 2024 00:17:01 +0200 Subject: [PATCH 061/306] adjust all tests to `nix_store_open`'s new signature (cherry picked from commit da9bf071709bc9e48a0a02531e78b2a71a9b4eba) --- rust/nix-expr/src/eval_state.rs | 58 +++++++++++++++++---------------- rust/nix-store/src/store.rs | 10 +++--- 2 files changed, 36 insertions(+), 32 deletions(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 6232114..9e1a7c4 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -403,6 +403,8 @@ pub fn test_init() { #[cfg(test)] mod tests { + use std::collections::HashMap; + use ctor::ctor; use std::io::Write as _; @@ -417,7 +419,7 @@ mod tests { fn eval_state_new_and_drop() { gc_registering_current_thread(|| { // very basic test: make sure initialization doesn't crash - let store = Store::open("auto").unwrap(); + let store = Store::open("auto", HashMap::new()).unwrap(); let _e = EvalState::new(store, []).unwrap(); }) .unwrap(); @@ -433,11 +435,11 @@ mod tests { writeln!(test_file0, "{integer0}").unwrap(); writeln!(test_file1, "{integer1}").unwrap(); gc_registering_current_thread(|| { - let es = EvalState::new(Store::open("auto").unwrap(), []).unwrap(); + let es = EvalState::new(Store::open("auto", HashMap::new()).unwrap(), []).unwrap(); assert!(es.eval_from_string(import_expression, "").is_err()); let es = EvalState::new( - Store::open("auto").unwrap(), + Store::open("auto", HashMap::new()).unwrap(), [ format!("test_file0={}", test_file0.path().to_str().unwrap()).as_str(), format!("test_file1={}", test_file1.path().to_str().unwrap()).as_str(), @@ -457,7 +459,7 @@ mod tests { #[test] fn eval_state_eval_from_string() { gc_registering_current_thread(|| { - let store = Store::open("auto").unwrap(); + let store = Store::open("auto", HashMap::new()).unwrap(); let es = EvalState::new(store, []).unwrap(); let v = es.eval_from_string("1", "").unwrap(); let v2 = v.clone(); @@ -474,7 +476,7 @@ mod tests { #[test] fn eval_state_value_bool() { gc_registering_current_thread(|| { - let store = Store::open("auto").unwrap(); + let store = Store::open("auto", HashMap::new()).unwrap(); let es = EvalState::new(store, []).unwrap(); let v = es.eval_from_string("true", "").unwrap(); es.force(&v).unwrap(); @@ -487,7 +489,7 @@ mod tests { #[test] fn eval_state_value_int() { gc_registering_current_thread(|| { - let store = Store::open("auto").unwrap(); + let store = Store::open("auto", HashMap::new()).unwrap(); let es = EvalState::new(store, []).unwrap(); let v = es.eval_from_string("1", "").unwrap(); es.force(&v).unwrap(); @@ -502,7 +504,7 @@ mod tests { #[test] fn eval_state_value_attrs_names_empty() { gc_registering_current_thread(|| { - let store = Store::open("auto").unwrap(); + let store = Store::open("auto", HashMap::new()).unwrap(); let es = EvalState::new(store, []).unwrap(); let v = es.eval_from_string("{ }", "").unwrap(); es.force(&v).unwrap(); @@ -517,7 +519,7 @@ mod tests { #[test] fn eval_state_require_attrs_names_bad_type() { gc_registering_current_thread(|| { - let store = Store::open("auto").unwrap(); + let store = Store::open("auto", HashMap::new()).unwrap(); let es = EvalState::new(store, []).unwrap(); let v = es.eval_from_string("1", "").unwrap(); es.force(&v).unwrap(); @@ -534,7 +536,7 @@ mod tests { #[test] fn eval_state_value_attrs_names_example() { gc_registering_current_thread(|| { - let store = Store::open("auto").unwrap(); + let store = Store::open("auto", HashMap::new()).unwrap(); let es = EvalState::new(store, []).unwrap(); let expr = r#"{ a = throw "nope a"; b = throw "nope b"; }"#; let v = es.eval_from_string(expr, "").unwrap(); @@ -549,7 +551,7 @@ mod tests { #[test] fn eval_state_require_attrs_select() { gc_registering_current_thread(|| { - let store = Store::open("auto").unwrap(); + let store = Store::open("auto", HashMap::new()).unwrap(); let es = EvalState::new(store, []).unwrap(); let expr = r#"{ a = "aye"; b = "bee"; }"#; let v = es.eval_from_string(expr, "").unwrap(); @@ -564,7 +566,7 @@ mod tests { #[test] fn eval_state_require_attrs_select_error() { gc_registering_current_thread(|| { - let store = Store::open("auto").unwrap(); + let store = Store::open("auto", HashMap::new()).unwrap(); let es = EvalState::new(store, []).unwrap(); let expr = r#"{ a = throw "oh no the error"; }"#; let v = es.eval_from_string(expr, "").unwrap(); @@ -585,7 +587,7 @@ mod tests { #[test] fn eval_state_require_attrs_select_opt() { gc_registering_current_thread(|| { - let store = Store::open("auto").unwrap(); + let store = Store::open("auto", HashMap::new()).unwrap(); let es = EvalState::new(store, []).unwrap(); let expr = r#"{ a = "aye"; b = "bee"; }"#; let v = es.eval_from_string(expr, "").unwrap(); @@ -602,7 +604,7 @@ mod tests { #[test] fn eval_state_require_attrs_select_opt_error() { gc_registering_current_thread(|| { - let store = Store::open("auto").unwrap(); + let store = Store::open("auto", HashMap::new()).unwrap(); let es = EvalState::new(store, []).unwrap(); let expr = r#"{ a = throw "oh no the error"; }"#; let v = es.eval_from_string(expr, "").unwrap(); @@ -623,7 +625,7 @@ mod tests { #[test] fn eval_state_value_string() { gc_registering_current_thread(|| { - let store = Store::open("auto").unwrap(); + let store = Store::open("auto", HashMap::new()).unwrap(); let es = EvalState::new(store, []).unwrap(); let v = es.eval_from_string("\"hello\"", "").unwrap(); es.force(&v).unwrap(); @@ -638,7 +640,7 @@ mod tests { #[test] fn eval_state_value_string_unexpected_bool() { gc_registering_current_thread(|| { - let store = Store::open("auto").unwrap(); + let store = Store::open("auto", HashMap::new()).unwrap(); let es = EvalState::new(store, []).unwrap(); let v = es.eval_from_string("true", "").unwrap(); es.force(&v).unwrap(); @@ -656,7 +658,7 @@ mod tests { #[test] fn eval_state_value_string_unexpected_path_value() { gc_registering_current_thread(|| { - let store = Store::open("auto").unwrap(); + let store = Store::open("auto", HashMap::new()).unwrap(); let es = EvalState::new(store, []).unwrap(); let v = es.eval_from_string("/foo", "").unwrap(); es.force(&v).unwrap(); @@ -673,7 +675,7 @@ mod tests { #[test] fn eval_state_value_string_bad_utf() { gc_registering_current_thread(|| { - let store = Store::open("auto").unwrap(); + let store = Store::open("auto", HashMap::new()).unwrap(); let es = EvalState::new(store, []).unwrap(); let v = es .eval_from_string("builtins.substring 0 1 \"ü\"", "") @@ -694,7 +696,7 @@ mod tests { #[test] fn eval_state_value_string_unexpected_context() { gc_registering_current_thread(|| { - let store = Store::open("auto").unwrap(); + let store = Store::open("auto", HashMap::new()).unwrap(); let es = EvalState::new(store, []).unwrap(); let v = es .eval_from_string("(derivation { name = \"hello\"; system = \"dummy\"; builder = \"cmd.exe\"; }).outPath", "") @@ -713,7 +715,7 @@ mod tests { #[test] fn eval_state_new_string() { gc_registering_current_thread(|| { - let store = Store::open("auto").unwrap(); + let store = Store::open("auto", HashMap::new()).unwrap(); let es = EvalState::new(store, []).unwrap(); let v = es.new_value_str("hello").unwrap(); es.force(&v).unwrap(); @@ -728,7 +730,7 @@ mod tests { #[test] fn eval_state_new_string_empty() { gc_registering_current_thread(|| { - let store = Store::open("auto").unwrap(); + let store = Store::open("auto", HashMap::new()).unwrap(); let es = EvalState::new(store, []).unwrap(); let v = es.new_value_str("").unwrap(); es.force(&v).unwrap(); @@ -743,7 +745,7 @@ mod tests { #[test] fn eval_state_new_string_invalid() { gc_registering_current_thread(|| { - let store = Store::open("auto").unwrap(); + let store = Store::open("auto", HashMap::new()).unwrap(); let es = EvalState::new(store, []).unwrap(); let r = es.new_value_str("hell\0no"); match r { @@ -762,7 +764,7 @@ mod tests { #[test] fn eval_state_new_int() { gc_registering_current_thread(|| { - let store = Store::open("auto").unwrap(); + let store = Store::open("auto", HashMap::new()).unwrap(); let es = EvalState::new(store, []).unwrap(); let v = es.new_value_int(42).unwrap(); es.force(&v).unwrap(); @@ -777,7 +779,7 @@ mod tests { #[test] fn eval_state_value_attrset() { gc_registering_current_thread(|| { - let store = Store::open("auto").unwrap(); + let store = Store::open("auto", HashMap::new()).unwrap(); let es = EvalState::new(store, []).unwrap(); let v = es.eval_from_string("{ }", "").unwrap(); es.force(&v).unwrap(); @@ -790,7 +792,7 @@ mod tests { #[test] fn eval_state_value_list() { gc_registering_current_thread(|| { - let store = Store::open("auto").unwrap(); + let store = Store::open("auto", HashMap::new()).unwrap(); let es = EvalState::new(store, []).unwrap(); let v = es .eval_from_string("[ ]", "") @@ -805,7 +807,7 @@ mod tests { #[test] fn eval_state_realise_string() { gc_registering_current_thread(|| { - let store = Store::open("auto").unwrap(); + let store = Store::open("auto", HashMap::new()).unwrap(); let es = EvalState::new(store, []).unwrap(); let expr = r#" '' @@ -852,7 +854,7 @@ mod tests { #[test] fn eval_state_call() { gc_registering_current_thread(|| { - let store = Store::open("auto").unwrap(); + let store = Store::open("auto", HashMap::new()).unwrap(); let es = EvalState::new(store, []).unwrap(); let f = es.eval_from_string("x: x + 1", "").unwrap(); let a = es.eval_from_string("2", "").unwrap(); @@ -888,7 +890,7 @@ mod tests { #[test] fn eval_state_call_fail_body() { gc_registering_current_thread(|| { - let store = Store::open("auto").unwrap(); + let store = Store::open("auto", HashMap::new()).unwrap(); let es = EvalState::new(store, []).unwrap(); let f = es.eval_from_string("x: x + 1", "").unwrap(); let a = es.eval_from_string("true", "").unwrap(); @@ -933,7 +935,7 @@ mod tests { #[test] fn eval_state_call_fail_args() { gc_registering_current_thread(|| { - let store = Store::open("auto").unwrap(); + let store = Store::open("auto", HashMap::new()).unwrap(); let es = EvalState::new(store, []).unwrap(); let f = es.eval_from_string("{x}: x + 1", "").unwrap(); let a = es.eval_from_string("{}", "").unwrap(); diff --git a/rust/nix-store/src/store.rs b/rust/nix-store/src/store.rs index 7440ff2..ea0812b 100644 --- a/rust/nix-store/src/store.rs +++ b/rust/nix-store/src/store.rs @@ -112,23 +112,25 @@ impl Store { #[cfg(test)] mod tests { + use std::collections::HashMap; + use super::*; #[test] fn auto_works() { - let res = Store::open("auto"); + let res = Store::open("auto", HashMap::new()); res.unwrap(); } #[test] fn invalid_uri_fails() { - let res = Store::open("invalid://uri"); + let res = Store::open("invalid://uri", HashMap::new()); assert!(res.is_err()); } #[test] fn get_uri() { - let store = Store::open("auto").unwrap(); + let store = Store::open("auto", HashMap::new()).unwrap(); let uri = store.get_uri().unwrap(); assert!(!uri.is_empty()); // must be ascii @@ -140,7 +142,7 @@ mod tests { #[test] #[ignore] // Needs network access fn get_uri_nixos_cache() { - let store = Store::open("https://cache.nixos.org/").unwrap(); + let store = Store::open("https://cache.nixos.org/", HashMap::new()).unwrap(); let uri = store.get_uri().unwrap(); assert_eq!(uri, "https://cache.nixos.org"); } From fe4d62d083ffc90dc7033a90104fca39631ea1d0 Mon Sep 17 00:00:00 2001 From: Philipp Zander Date: Fri, 17 May 2024 01:56:16 +0200 Subject: [PATCH 062/306] test `nix_store_open`'s `params` argument (cherry picked from commit 2cebb64daacdc612e7ff916dc32b0fe5ed89dbfc) --- rust/nix-expr/src/eval_state.rs | 90 ++++++++++++++++++++++++++++++--- 1 file changed, 82 insertions(+), 8 deletions(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 9e1a7c4..8990cba 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -403,12 +403,11 @@ pub fn test_init() { #[cfg(test)] mod tests { - use std::collections::HashMap; - - use ctor::ctor; - use std::io::Write as _; - use super::*; + use ctor::ctor; + use std::collections::HashMap; + use std::fs::read_dir; + use std::io::Write as _; #[ctor] fn setup() { @@ -871,7 +870,7 @@ mod tests { #[test] fn eval_state_apply() { gc_registering_current_thread(|| { - let store = Store::open("auto").unwrap(); + let store = Store::open("auto", HashMap::new()).unwrap(); let es = EvalState::new(store, []).unwrap(); // This is a function that takes two arguments. let f = es.eval_from_string("x: x + 1", "").unwrap(); @@ -911,7 +910,7 @@ mod tests { #[test] fn eval_state_apply_fail_body() { gc_registering_current_thread(|| { - let store = Store::open("auto").unwrap(); + let store = Store::open("auto", HashMap::new()).unwrap(); let es = EvalState::new(store, []).unwrap(); let f = es.eval_from_string("x: x + 1", "").unwrap(); let a = es.eval_from_string("true", "").unwrap(); @@ -956,7 +955,7 @@ mod tests { #[test] fn eval_state_apply_fail_args() { gc_registering_current_thread(|| { - let store = Store::open("auto").unwrap(); + let store = Store::open("auto", HashMap::new()).unwrap(); let es = EvalState::new(store, []).unwrap(); let f = es.eval_from_string("{x}: x + 1", "").unwrap(); let a = es.eval_from_string("{}", "").unwrap(); @@ -976,4 +975,79 @@ mod tests { }) .unwrap(); } + + #[test] + fn store_open_params() { + gc_registering_current_thread(|| { + let store = tempfile::tempdir().unwrap(); + let store_path = store.path().to_str().unwrap(); + let state = tempfile::tempdir().unwrap(); + let state_path = state.path().to_str().unwrap(); + let log = tempfile::tempdir().unwrap(); + let log_path = log.path().to_str().unwrap(); + + let es = EvalState::new( + Store::open( + "local", + HashMap::from([ + ("store", store_path), + ("state", state_path), + ("log", log_path), + ]) + .iter() + .map(|(a, b)| (*a, *b)), + ) + .unwrap(), + [], + ) + .unwrap(); + + let expr = r#" + '' + a derivation output: ${ + derivation { name = "letsbuild"; + system = builtins.currentSystem; + builder = "/bin/sh"; + args = [ "-c" "echo foo > $out" ]; + }} + a path: ${builtins.toFile "just-a-file" "ooh file good"} + a derivation path by itself: ${ + builtins.unsafeDiscardOutputDependency + (derivation { + name = "not-actually-built-yet"; + system = builtins.currentSystem; + builder = "/bin/sh"; + args = [ "-c" "echo foo > $out" ]; + }).drvPath} + '' + "#; + let derivations: [&[u8]; 3] = [ + b"letsbuild.drv", + b"just-a-file", + b"not-actually-built-yet.drv", + ]; + let _ = es.eval_from_string(expr, "").unwrap(); + + // assert that all three `derivations` are inside the store and the `state` directory is not empty either. + let store_contents: Vec<_> = read_dir(store.path()) + .unwrap() + .map(|dir_entry| dir_entry.unwrap().file_name()) + .collect(); + for derivation in derivations { + assert!(store_contents + .iter() + .find(|f| f.as_encoded_bytes().ends_with(derivation)) + .is_some()); + } + assert!(!empty(read_dir(state.path()).unwrap())); + + store.close().unwrap(); + state.close().unwrap(); + log.close().unwrap(); + }) + .unwrap(); + } + fn empty(foldable: impl IntoIterator) -> bool { + foldable.into_iter().all(|_| false) + } } From d89fb1803f5385759039989b01e08b8a5d2d174a Mon Sep 17 00:00:00 2001 From: Philipp Zander Date: Wed, 29 May 2024 02:52:43 +0200 Subject: [PATCH 063/306] doc: `EvalState::eval_from_string` (cherry picked from commit b1de918abddc8166973c956ec77d597dae92f928) --- rust/nix-expr/src/eval_state.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 8990cba..633eb57 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -88,6 +88,25 @@ impl EvalState { pub fn store(&self) -> &Store { &self.store } + /// Parses and evaluates a Nix expression `expr`. + /// + /// Expressions can contain relative paths such as `./.` that are resolved relative to the given `path`. + /// + /// # Examples + /// + /// ``` + /// # use nix_expr::eval_state::EvalState; + /// use nix_store::store::Store; + /// use nix_expr::value::Value; + /// + /// # fn main() -> anyhow::Result<()> { + /// # let es = EvalState::new(Store::open("auto", [])?, [])?; + /// let v: Value = es.eval_from_string("42", ".")?; + /// assert_eq!(es.require_int(&v)?, 42); + /// # Ok(()) + /// # } + /// ``` + #[doc(alias = "nix_expr_eval_from_string")] pub fn eval_from_string(&self, expr: &str, path: &str) -> Result { let expr_ptr = CString::new(expr).with_context(|| "eval_from_string: expr contains null byte")?; From 673f80e2432bb3a5b4eeb4c950042df998ec60ca Mon Sep 17 00:00:00 2001 From: Philipp Zander Date: Mon, 3 Jun 2024 04:18:58 +0200 Subject: [PATCH 064/306] refact: remove `Thunk` from `ValueType` and introduce `ValueTypeOrThunk` to address #30 (cherry picked from commit 97c05175b633b364686790549b11e43cfb2f1d2c) --- rust/nix-expr/src/eval_state.rs | 31 +++++++++++++++++------------ rust/nix-expr/src/value.rs | 35 +++++++++++++++++++-------------- 2 files changed, 38 insertions(+), 28 deletions(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 633eb57..7510cfa 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -1,4 +1,4 @@ -use crate::value::{Int, Value, ValueType}; +use crate::value::{Int, Value, ValueType, ValueTypeOrThunk}; use anyhow::Context as _; use anyhow::{bail, Result}; use lazy_static::lazy_static; @@ -133,19 +133,24 @@ impl EvalState { } self.context.check_err() } - pub fn value_is_thunk(&self, value: &Value) -> bool { - let r = unsafe { - raw::get_type(self.context.ptr(), value.raw_ptr()) == raw::ValueType_NIX_TYPE_THUNK - }; - self.context.check_err().unwrap(); - r - } - pub fn value_type(&self, value: &Value) -> Result { - if self.value_is_thunk(value) { - self.force(value)?; - } + pub fn value_type_unforced(&self, value: &Value) -> ValueTypeOrThunk { let r = unsafe { raw::get_type(self.context.ptr(), value.raw_ptr()) }; - Ok(ValueType::from_raw(r)) + self.context.check_err().unwrap(); + ValueTypeOrThunk::from_raw(r) + } + pub fn value_type_forced(&self, value: &Value) -> Result { + match self.value_type_unforced(value) { + ValueTypeOrThunk::ValueType(a) => Ok(a), + ValueTypeOrThunk::Thunk => { + self.force(value)?; + match self.value_type_unforced(value) { + ValueTypeOrThunk::ValueType(a) => Ok(a), + ValueTypeOrThunk::Thunk => { + panic!("values should not be thunks after having been forced.") + } + } + } + } } pub fn require_int(&self, v: &Value) -> Result { let t = self.value_type(v).unwrap(); diff --git a/rust/nix-expr/src/value.rs b/rust/nix-expr/src/value.rs index 46371a2..dfc160e 100644 --- a/rust/nix-expr/src/value.rs +++ b/rust/nix-expr/src/value.rs @@ -19,26 +19,31 @@ pub enum ValueType { Null, Path, String, - Thunk, Unknown, } -impl ValueType { - pub(crate) fn from_raw(raw: raw::ValueType) -> ValueType { +#[derive(Eq, PartialEq, Debug)] +pub enum ValueTypeOrThunk { + ValueType(ValueType), + Thunk, +} + +impl ValueTypeOrThunk { + pub(crate) fn from_raw(raw: raw::ValueType) -> ValueTypeOrThunk { match raw { - raw::ValueType_NIX_TYPE_ATTRS => ValueType::AttrSet, - raw::ValueType_NIX_TYPE_BOOL => ValueType::Bool, - raw::ValueType_NIX_TYPE_EXTERNAL => ValueType::External, - raw::ValueType_NIX_TYPE_FLOAT => ValueType::Float, - raw::ValueType_NIX_TYPE_FUNCTION => ValueType::Function, - raw::ValueType_NIX_TYPE_INT => ValueType::Int, - raw::ValueType_NIX_TYPE_LIST => ValueType::List, - raw::ValueType_NIX_TYPE_NULL => ValueType::Null, - raw::ValueType_NIX_TYPE_PATH => ValueType::Path, - raw::ValueType_NIX_TYPE_STRING => ValueType::String, - raw::ValueType_NIX_TYPE_THUNK => ValueType::Thunk, + raw::ValueType_NIX_TYPE_ATTRS => ValueTypeOrThunk::ValueType(ValueType::AttrSet), + raw::ValueType_NIX_TYPE_BOOL => ValueTypeOrThunk::ValueType(ValueType::Bool), + raw::ValueType_NIX_TYPE_EXTERNAL => ValueTypeOrThunk::ValueType(ValueType::External), + raw::ValueType_NIX_TYPE_FLOAT => ValueTypeOrThunk::ValueType(ValueType::Float), + raw::ValueType_NIX_TYPE_FUNCTION => ValueTypeOrThunk::ValueType(ValueType::Function), + raw::ValueType_NIX_TYPE_INT => ValueTypeOrThunk::ValueType(ValueType::Int), + raw::ValueType_NIX_TYPE_LIST => ValueTypeOrThunk::ValueType(ValueType::List), + raw::ValueType_NIX_TYPE_NULL => ValueTypeOrThunk::ValueType(ValueType::Null), + raw::ValueType_NIX_TYPE_PATH => ValueTypeOrThunk::ValueType(ValueType::Path), + raw::ValueType_NIX_TYPE_STRING => ValueTypeOrThunk::ValueType(ValueType::String), + raw::ValueType_NIX_TYPE_THUNK => ValueTypeOrThunk::Thunk, // This would happen if a new type of value is added in Nix. - _ => ValueType::Unknown, + _ => ValueTypeOrThunk::ValueType(ValueType::Unknown), } } } From 627b31d6c524e9531b106500b3c8cff613fb73f2 Mon Sep 17 00:00:00 2001 From: Philipp Zander Date: Mon, 3 Jun 2024 04:20:17 +0200 Subject: [PATCH 065/306] refact: adjust `ValueType` use sites (cherry picked from commit 9d9f7d59c6df3db26f331e63c9b210f8fae19061) --- rust/nix-expr/src/eval_state.rs | 68 ++++++++++++++++----------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 7510cfa..10cc390 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -153,7 +153,7 @@ impl EvalState { } } pub fn require_int(&self, v: &Value) -> Result { - let t = self.value_type(v).unwrap(); + let t = self.value_type_forced(v).unwrap(); if t != ValueType::Int { bail!("expected an int, but got a {:?}", t); } @@ -163,7 +163,7 @@ impl EvalState { /// Evaluate, and require that the value is an attrset. /// Returns a list of the keys in the attrset. pub fn require_attrs_names(&self, v: &Value) -> Result> { - let t = self.value_type(v)?; + let t = self.value_type_forced(v)?; if t != ValueType::AttrSet { bail!("expected an attrset, but got a {:?}", t); } @@ -202,7 +202,7 @@ impl EvalState { /// Evaluate, require that the value is an attrset, and select an attribute by name. pub fn require_attrs_select_opt(&self, v: &Value, attr_name: &str) -> Result> { - let t = self.value_type(v)?; + let t = self.value_type_forced(v)?; if t != ValueType::AttrSet { bail!("expected an attrset, but got a {:?}", t); } @@ -264,7 +264,7 @@ impl EvalState { } /// NOTE: this will be replaced by two methods, one that also returns the context, and one that checks that the context is empty pub fn require_string(&self, value: &Value) -> Result { - let t = self.value_type(value)?; + let t = self.value_type_forced(value)?; if t != ValueType::String { bail!("expected a string, but got a {:?}", t); } @@ -275,7 +275,7 @@ impl EvalState { value: &Value, is_import_from_derivation: bool, ) -> Result { - let t = self.value_type(value)?; + let t = self.value_type_forced(value)?; if t != ValueType::String { bail!("expected a string, but got a {:?}", t); } @@ -487,10 +487,10 @@ mod tests { let v = es.eval_from_string("1", "").unwrap(); let v2 = v.clone(); es.force(&v).unwrap(); - let t = es.value_type(&v).unwrap(); - assert!(t == ValueType::Int); - let t2 = es.value_type(&v2).unwrap(); - assert!(t2 == ValueType::Int); + let t = es.value_type_unforced(&v); + assert!(t == ValueTypeOrThunk::ValueType(ValueType::Int)); + let t2 = es.value_type_unforced(&v2); + assert!(t2 == ValueTypeOrThunk::ValueType(ValueType::Int)); gc_now(); }) .unwrap(); @@ -503,8 +503,8 @@ mod tests { let es = EvalState::new(store, []).unwrap(); let v = es.eval_from_string("true", "").unwrap(); es.force(&v).unwrap(); - let t = es.value_type(&v).unwrap(); - assert!(t == ValueType::Bool); + let t = es.value_type_unforced(&v); + assert!(t == ValueTypeOrThunk::ValueType(ValueType::Bool)); }) .unwrap(); } @@ -531,8 +531,8 @@ mod tests { let es = EvalState::new(store, []).unwrap(); let v = es.eval_from_string("{ }", "").unwrap(); es.force(&v).unwrap(); - let t = es.value_type(&v).unwrap(); - assert!(t == ValueType::AttrSet); + let t = es.value_type_unforced(&v); + assert!(t == ValueTypeOrThunk::ValueType(ValueType::AttrSet)); let attrs = es.require_attrs_names(&v).unwrap(); assert_eq!(attrs.len(), 0); }) @@ -652,8 +652,8 @@ mod tests { let es = EvalState::new(store, []).unwrap(); let v = es.eval_from_string("\"hello\"", "").unwrap(); es.force(&v).unwrap(); - let t = es.value_type(&v).unwrap(); - assert!(t == ValueType::String); + let t = es.value_type_unforced(&v); + assert!(t == ValueTypeOrThunk::ValueType(ValueType::String)); let s = es.require_string(&v).unwrap(); assert!(s == "hello"); }) @@ -704,8 +704,8 @@ mod tests { .eval_from_string("builtins.substring 0 1 \"ü\"", "") .unwrap(); es.force(&v).unwrap(); - let t = es.value_type(&v).unwrap(); - assert!(t == ValueType::String); + let t = es.value_type_unforced(&v); + assert!(t == ValueTypeOrThunk::ValueType(ValueType::String)); let r = es.require_string(&v); assert!(r.is_err()); assert!(r @@ -725,8 +725,8 @@ mod tests { .eval_from_string("(derivation { name = \"hello\"; system = \"dummy\"; builder = \"cmd.exe\"; }).outPath", "") .unwrap(); es.force(&v).unwrap(); - let t = es.value_type(&v).unwrap(); - assert!(t == ValueType::String); + let t = es.value_type_unforced(&v); + assert!(t == ValueTypeOrThunk::ValueType(ValueType::String)); // TODO // let r = es.require_string_without_context(&v); // assert!(r.is_err()); @@ -742,8 +742,8 @@ mod tests { let es = EvalState::new(store, []).unwrap(); let v = es.new_value_str("hello").unwrap(); es.force(&v).unwrap(); - let t = es.value_type(&v).unwrap(); - assert!(t == ValueType::String); + let t = es.value_type_unforced(&v); + assert!(t == ValueTypeOrThunk::ValueType(ValueType::String)); let s = es.require_string(&v).unwrap(); assert!(s == "hello"); }) @@ -757,8 +757,8 @@ mod tests { let es = EvalState::new(store, []).unwrap(); let v = es.new_value_str("").unwrap(); es.force(&v).unwrap(); - let t = es.value_type(&v).unwrap(); - assert!(t == ValueType::String); + let t = es.value_type_unforced(&v); + assert!(t == ValueTypeOrThunk::ValueType(ValueType::String)); let s = es.require_string(&v).unwrap(); assert!(s == ""); }) @@ -791,8 +791,8 @@ mod tests { let es = EvalState::new(store, []).unwrap(); let v = es.new_value_int(42).unwrap(); es.force(&v).unwrap(); - let t = es.value_type(&v).unwrap(); - assert!(t == ValueType::Int); + let t = es.value_type_unforced(&v); + assert!(t == ValueTypeOrThunk::ValueType(ValueType::Int)); let i = es.require_int(&v).unwrap(); assert!(i == 42); }) @@ -806,8 +806,8 @@ mod tests { let es = EvalState::new(store, []).unwrap(); let v = es.eval_from_string("{ }", "").unwrap(); es.force(&v).unwrap(); - let t = es.value_type(&v).unwrap(); - assert!(t == ValueType::AttrSet); + let t = es.value_type_unforced(&v); + assert!(t == ValueTypeOrThunk::ValueType(ValueType::AttrSet)); }) .unwrap(); } @@ -821,8 +821,8 @@ mod tests { .eval_from_string("[ ]", "") .unwrap(); es.force(&v).unwrap(); - let t = es.value_type(&v).unwrap(); - assert!(t == ValueType::List); + let t = es.value_type_unforced(&v); + assert!(t == ValueTypeOrThunk::ValueType(ValueType::List)); }) .unwrap(); } @@ -883,8 +883,8 @@ mod tests { let a = es.eval_from_string("2", "").unwrap(); let v = es.call(f, a).unwrap(); es.force(&v).unwrap(); - let t = es.value_type(&v).unwrap(); - assert!(t == ValueType::Int); + let t = es.value_type_unforced(&v); + assert!(t == ValueTypeOrThunk::ValueType(ValueType::Int)); let i = es.require_int(&v).unwrap(); assert!(i == 3); }) @@ -900,10 +900,10 @@ mod tests { let f = es.eval_from_string("x: x + 1", "").unwrap(); let a = es.eval_from_string("2", "").unwrap(); let v = es.new_value_apply(&f, &a).unwrap(); - assert!(es.value_is_thunk(&v)); + assert!(es.value_type_unforced(&v) == ValueTypeOrThunk::Thunk); es.force(&v).unwrap(); - let t = es.value_type(&v).unwrap(); - assert!(t == ValueType::Int); + let t = es.value_type_unforced(&v); + assert!(t == ValueTypeOrThunk::ValueType(ValueType::Int)); let i = es.require_int(&v).unwrap(); assert!(i == 3); }) From 19361acb12d4f3f5016d3a177facb604eb8f2f44 Mon Sep 17 00:00:00 2001 From: Philipp Date: Fri, 14 Jun 2024 15:40:11 +0200 Subject: [PATCH 066/306] rename `value_type_forced` to `value_type` Co-authored-by: Robert Hensing (cherry picked from commit 3bf206a38e20109bbee9cd781e66bcd895ed95ce) --- rust/nix-expr/src/eval_state.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 10cc390..f9dd0ba 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -138,7 +138,7 @@ impl EvalState { self.context.check_err().unwrap(); ValueTypeOrThunk::from_raw(r) } - pub fn value_type_forced(&self, value: &Value) -> Result { + pub fn value_type(&self, value: &Value) -> Result { match self.value_type_unforced(value) { ValueTypeOrThunk::ValueType(a) => Ok(a), ValueTypeOrThunk::Thunk => { @@ -153,7 +153,7 @@ impl EvalState { } } pub fn require_int(&self, v: &Value) -> Result { - let t = self.value_type_forced(v).unwrap(); + let t = self.value_type(v).unwrap(); if t != ValueType::Int { bail!("expected an int, but got a {:?}", t); } @@ -163,7 +163,7 @@ impl EvalState { /// Evaluate, and require that the value is an attrset. /// Returns a list of the keys in the attrset. pub fn require_attrs_names(&self, v: &Value) -> Result> { - let t = self.value_type_forced(v)?; + let t = self.value_type(v)?; if t != ValueType::AttrSet { bail!("expected an attrset, but got a {:?}", t); } @@ -202,7 +202,7 @@ impl EvalState { /// Evaluate, require that the value is an attrset, and select an attribute by name. pub fn require_attrs_select_opt(&self, v: &Value, attr_name: &str) -> Result> { - let t = self.value_type_forced(v)?; + let t = self.value_type(v)?; if t != ValueType::AttrSet { bail!("expected an attrset, but got a {:?}", t); } @@ -264,7 +264,7 @@ impl EvalState { } /// NOTE: this will be replaced by two methods, one that also returns the context, and one that checks that the context is empty pub fn require_string(&self, value: &Value) -> Result { - let t = self.value_type_forced(value)?; + let t = self.value_type(value)?; if t != ValueType::String { bail!("expected a string, but got a {:?}", t); } @@ -275,7 +275,7 @@ impl EvalState { value: &Value, is_import_from_derivation: bool, ) -> Result { - let t = self.value_type_forced(value)?; + let t = self.value_type(value)?; if t != ValueType::String { bail!("expected a string, but got a {:?}", t); } From 22ffd20c5335f315adad324f8337576f5ad90b43 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 14 Jun 2024 18:42:23 +0200 Subject: [PATCH 067/306] refact: Use Option instead of custom ValueTypeOrThunk This loses the custom name for "thunk", but checking thunkness is a niche use case that I don't think we should spend much code on. (cherry picked from commit 7bdff525c13234ce6a32ea9346292d948b0840c1) --- rust/nix-expr/src/eval_state.rs | 46 ++++++++++++++++----------------- rust/nix-expr/src/value.rs | 41 +++++++++++++++-------------- 2 files changed, 44 insertions(+), 43 deletions(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index f9dd0ba..705d8a4 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -1,4 +1,4 @@ -use crate::value::{Int, Value, ValueType, ValueTypeOrThunk}; +use crate::value::{Int, Value, ValueType}; use anyhow::Context as _; use anyhow::{bail, Result}; use lazy_static::lazy_static; @@ -133,20 +133,20 @@ impl EvalState { } self.context.check_err() } - pub fn value_type_unforced(&self, value: &Value) -> ValueTypeOrThunk { + pub fn value_type_unforced(&self, value: &Value) -> Option { let r = unsafe { raw::get_type(self.context.ptr(), value.raw_ptr()) }; self.context.check_err().unwrap(); - ValueTypeOrThunk::from_raw(r) + ValueType::from_raw(r) } pub fn value_type(&self, value: &Value) -> Result { match self.value_type_unforced(value) { - ValueTypeOrThunk::ValueType(a) => Ok(a), - ValueTypeOrThunk::Thunk => { + Some(a) => Ok(a), + None => { self.force(value)?; match self.value_type_unforced(value) { - ValueTypeOrThunk::ValueType(a) => Ok(a), - ValueTypeOrThunk::Thunk => { - panic!("values should not be thunks after having been forced.") + Some(a) => Ok(a), + None => { + panic!("Nix value must not be thunk after being forced.") } } } @@ -488,9 +488,9 @@ mod tests { let v2 = v.clone(); es.force(&v).unwrap(); let t = es.value_type_unforced(&v); - assert!(t == ValueTypeOrThunk::ValueType(ValueType::Int)); + assert!(t == Some(ValueType::Int)); let t2 = es.value_type_unforced(&v2); - assert!(t2 == ValueTypeOrThunk::ValueType(ValueType::Int)); + assert!(t2 == Some(ValueType::Int)); gc_now(); }) .unwrap(); @@ -504,7 +504,7 @@ mod tests { let v = es.eval_from_string("true", "").unwrap(); es.force(&v).unwrap(); let t = es.value_type_unforced(&v); - assert!(t == ValueTypeOrThunk::ValueType(ValueType::Bool)); + assert!(t == Some(ValueType::Bool)); }) .unwrap(); } @@ -532,7 +532,7 @@ mod tests { let v = es.eval_from_string("{ }", "").unwrap(); es.force(&v).unwrap(); let t = es.value_type_unforced(&v); - assert!(t == ValueTypeOrThunk::ValueType(ValueType::AttrSet)); + assert!(t == Some(ValueType::AttrSet)); let attrs = es.require_attrs_names(&v).unwrap(); assert_eq!(attrs.len(), 0); }) @@ -653,7 +653,7 @@ mod tests { let v = es.eval_from_string("\"hello\"", "").unwrap(); es.force(&v).unwrap(); let t = es.value_type_unforced(&v); - assert!(t == ValueTypeOrThunk::ValueType(ValueType::String)); + assert!(t == Some(ValueType::String)); let s = es.require_string(&v).unwrap(); assert!(s == "hello"); }) @@ -705,7 +705,7 @@ mod tests { .unwrap(); es.force(&v).unwrap(); let t = es.value_type_unforced(&v); - assert!(t == ValueTypeOrThunk::ValueType(ValueType::String)); + assert!(t == Some(ValueType::String)); let r = es.require_string(&v); assert!(r.is_err()); assert!(r @@ -726,7 +726,7 @@ mod tests { .unwrap(); es.force(&v).unwrap(); let t = es.value_type_unforced(&v); - assert!(t == ValueTypeOrThunk::ValueType(ValueType::String)); + assert!(t == Some(ValueType::String)); // TODO // let r = es.require_string_without_context(&v); // assert!(r.is_err()); @@ -743,7 +743,7 @@ mod tests { let v = es.new_value_str("hello").unwrap(); es.force(&v).unwrap(); let t = es.value_type_unforced(&v); - assert!(t == ValueTypeOrThunk::ValueType(ValueType::String)); + assert!(t == Some(ValueType::String)); let s = es.require_string(&v).unwrap(); assert!(s == "hello"); }) @@ -758,7 +758,7 @@ mod tests { let v = es.new_value_str("").unwrap(); es.force(&v).unwrap(); let t = es.value_type_unforced(&v); - assert!(t == ValueTypeOrThunk::ValueType(ValueType::String)); + assert!(t == Some(ValueType::String)); let s = es.require_string(&v).unwrap(); assert!(s == ""); }) @@ -792,7 +792,7 @@ mod tests { let v = es.new_value_int(42).unwrap(); es.force(&v).unwrap(); let t = es.value_type_unforced(&v); - assert!(t == ValueTypeOrThunk::ValueType(ValueType::Int)); + assert!(t == Some(ValueType::Int)); let i = es.require_int(&v).unwrap(); assert!(i == 42); }) @@ -807,7 +807,7 @@ mod tests { let v = es.eval_from_string("{ }", "").unwrap(); es.force(&v).unwrap(); let t = es.value_type_unforced(&v); - assert!(t == ValueTypeOrThunk::ValueType(ValueType::AttrSet)); + assert!(t == Some(ValueType::AttrSet)); }) .unwrap(); } @@ -822,7 +822,7 @@ mod tests { .unwrap(); es.force(&v).unwrap(); let t = es.value_type_unforced(&v); - assert!(t == ValueTypeOrThunk::ValueType(ValueType::List)); + assert!(t == Some(ValueType::List)); }) .unwrap(); } @@ -884,7 +884,7 @@ mod tests { let v = es.call(f, a).unwrap(); es.force(&v).unwrap(); let t = es.value_type_unforced(&v); - assert!(t == ValueTypeOrThunk::ValueType(ValueType::Int)); + assert!(t == Some(ValueType::Int)); let i = es.require_int(&v).unwrap(); assert!(i == 3); }) @@ -900,10 +900,10 @@ mod tests { let f = es.eval_from_string("x: x + 1", "").unwrap(); let a = es.eval_from_string("2", "").unwrap(); let v = es.new_value_apply(&f, &a).unwrap(); - assert!(es.value_type_unforced(&v) == ValueTypeOrThunk::Thunk); + assert!(es.value_type_unforced(&v) == None); es.force(&v).unwrap(); let t = es.value_type_unforced(&v); - assert!(t == ValueTypeOrThunk::ValueType(ValueType::Int)); + assert!(t == Some(ValueType::Int)); let i = es.require_int(&v).unwrap(); assert!(i == 3); }) diff --git a/rust/nix-expr/src/value.rs b/rust/nix-expr/src/value.rs index dfc160e..da33c87 100644 --- a/rust/nix-expr/src/value.rs +++ b/rust/nix-expr/src/value.rs @@ -22,28 +22,29 @@ pub enum ValueType { Unknown, } -#[derive(Eq, PartialEq, Debug)] -pub enum ValueTypeOrThunk { - ValueType(ValueType), - Thunk, -} - -impl ValueTypeOrThunk { - pub(crate) fn from_raw(raw: raw::ValueType) -> ValueTypeOrThunk { +impl ValueType { + /// Convert a raw value type to a `ValueType`. + /// + /// Return `None` if the Value is still a thunk (i.e. not yet evaluated). + /// + /// Return `Some(ValueType::Unknown)` if the value type is not recognized. + pub(crate) fn from_raw(raw: raw::ValueType) -> Option { match raw { - raw::ValueType_NIX_TYPE_ATTRS => ValueTypeOrThunk::ValueType(ValueType::AttrSet), - raw::ValueType_NIX_TYPE_BOOL => ValueTypeOrThunk::ValueType(ValueType::Bool), - raw::ValueType_NIX_TYPE_EXTERNAL => ValueTypeOrThunk::ValueType(ValueType::External), - raw::ValueType_NIX_TYPE_FLOAT => ValueTypeOrThunk::ValueType(ValueType::Float), - raw::ValueType_NIX_TYPE_FUNCTION => ValueTypeOrThunk::ValueType(ValueType::Function), - raw::ValueType_NIX_TYPE_INT => ValueTypeOrThunk::ValueType(ValueType::Int), - raw::ValueType_NIX_TYPE_LIST => ValueTypeOrThunk::ValueType(ValueType::List), - raw::ValueType_NIX_TYPE_NULL => ValueTypeOrThunk::ValueType(ValueType::Null), - raw::ValueType_NIX_TYPE_PATH => ValueTypeOrThunk::ValueType(ValueType::Path), - raw::ValueType_NIX_TYPE_STRING => ValueTypeOrThunk::ValueType(ValueType::String), - raw::ValueType_NIX_TYPE_THUNK => ValueTypeOrThunk::Thunk, + raw::ValueType_NIX_TYPE_ATTRS => Some(ValueType::AttrSet), + raw::ValueType_NIX_TYPE_BOOL => Some(ValueType::Bool), + raw::ValueType_NIX_TYPE_EXTERNAL => Some(ValueType::External), + raw::ValueType_NIX_TYPE_FLOAT => Some(ValueType::Float), + raw::ValueType_NIX_TYPE_FUNCTION => Some(ValueType::Function), + raw::ValueType_NIX_TYPE_INT => Some(ValueType::Int), + raw::ValueType_NIX_TYPE_LIST => Some(ValueType::List), + raw::ValueType_NIX_TYPE_NULL => Some(ValueType::Null), + raw::ValueType_NIX_TYPE_PATH => Some(ValueType::Path), + raw::ValueType_NIX_TYPE_STRING => Some(ValueType::String), + + raw::ValueType_NIX_TYPE_THUNK => None, + // This would happen if a new type of value is added in Nix. - _ => ValueTypeOrThunk::ValueType(ValueType::Unknown), + _ => Some(ValueType::Unknown), } } } From c775792b632f2041c203513db3b973ad8e934ce2 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 14 Jun 2024 15:50:07 +0200 Subject: [PATCH 068/306] fix: Do not unwrap() in require_int (cherry picked from commit c7d2e3e02a977424634ee12015265906e18039a0) --- rust/nix-expr/src/eval_state.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 705d8a4..ce1de17 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -135,6 +135,7 @@ impl EvalState { } pub fn value_type_unforced(&self, value: &Value) -> Option { let r = unsafe { raw::get_type(self.context.ptr(), value.raw_ptr()) }; + // .unwrap(): no reason for this to fail, as it does not evaluate self.context.check_err().unwrap(); ValueType::from_raw(r) } @@ -153,7 +154,7 @@ impl EvalState { } } pub fn require_int(&self, v: &Value) -> Result { - let t = self.value_type(v).unwrap(); + let t = self.value_type(v)?; if t != ValueType::Int { bail!("expected an int, but got a {:?}", t); } From 61efb9b79fef13a9d2678b164b0f8b9ae2682231 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 14 Jun 2024 17:28:50 +0200 Subject: [PATCH 069/306] refact: Add Context::check_one_call(f) This makes it easier to stick to a regular pattern and not forget to check the context afterwards. (cherry picked from commit 966ef3769a23075f5d660a48cf5a7307df925dbf) --- rust/nix-expr/src/eval_state.rs | 178 +++++++++++++++----------------- rust/nix-util/src/context.rs | 25 +++++ 2 files changed, 110 insertions(+), 93 deletions(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index ce1de17..6e09439 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -17,12 +17,9 @@ lazy_static! { unsafe { raw::GC_allow_register_threads(); } - let context: Context = Context::new(); - unsafe { - raw::libexpr_init(context.ptr()); - } - context.check_err()?; - Ok(()) + Context::new().check_one_call(|ctx_ptr| unsafe { + raw::libexpr_init(ctx_ptr); + }) }; } pub fn init() -> Result<()> { @@ -70,14 +67,13 @@ impl EvalState { init()?; - let eval_state = - unsafe { raw::state_create(context.ptr(), lookup_path.as_mut_ptr(), store.raw_ptr()) }; - context.check_err()?; - if eval_state.is_null() { - panic!("nix_state_create returned a null pointer without an error"); - } + let eval_state = context.check_one_call(|ctx_ptr| unsafe { + raw::state_create(ctx_ptr, lookup_path.as_mut_ptr(), store.raw_ptr()) + })?; Ok(EvalState { - eval_state: NonNull::new(eval_state).unwrap(), + eval_state: NonNull::new(eval_state).unwrap_or_else(|| { + panic!("nix_state_create returned a null pointer without an error") + }), store, context, }) @@ -112,32 +108,35 @@ impl EvalState { CString::new(expr).with_context(|| "eval_from_string: expr contains null byte")?; let path_ptr = CString::new(path).with_context(|| "eval_from_string: path contains null byte")?; - let value = self.new_value_uninitialized(); unsafe { - let ctx_ptr = self.context.ptr(); - raw::expr_eval_from_string( - ctx_ptr, - self.raw_ptr(), - expr_ptr.as_ptr(), - path_ptr.as_ptr(), - value.raw_ptr(), - ); - }; - self.context.check_err()?; - Ok(value) + let value = self.new_value_uninitialized()?; + self.context.check_one_call(|ctx_ptr| { + raw::expr_eval_from_string( + ctx_ptr, + self.raw_ptr(), + expr_ptr.as_ptr(), + path_ptr.as_ptr(), + value.raw_ptr(), + ) + })?; + Ok(value) + } } /// Try turn any Value into a Value that isn't a Thunk. pub fn force(&self, v: &Value) -> Result<()> { unsafe { - raw::value_force(self.context.ptr(), self.raw_ptr(), v.raw_ptr()); + self.context.check_one_call(|ctx_ptr| { + raw::value_force(ctx_ptr, self.raw_ptr(), v.raw_ptr()); + })?; } - self.context.check_err() + Ok(()) } pub fn value_type_unforced(&self, value: &Value) -> Option { - let r = unsafe { raw::get_type(self.context.ptr(), value.raw_ptr()) }; + let r = self + .context + .check_one_call(|ctx_ptr| unsafe { raw::get_type(ctx_ptr, value.raw_ptr()) }); // .unwrap(): no reason for this to fail, as it does not evaluate - self.context.check_err().unwrap(); - ValueType::from_raw(r) + ValueType::from_raw(r.unwrap()) } pub fn value_type(&self, value: &Value) -> Result { match self.value_type_unforced(value) { @@ -158,8 +157,8 @@ impl EvalState { if t != ValueType::Int { bail!("expected an int, but got a {:?}", t); } - let i = unsafe { raw::get_int(self.context.ptr(), v.raw_ptr()) }; - Ok(i) + self.context + .check_one_call(|ctx_ptr| unsafe { raw::get_int(ctx_ptr, v.raw_ptr()) }) } /// Evaluate, and require that the value is an attrset. /// Returns a list of the keys in the attrset. @@ -168,28 +167,25 @@ impl EvalState { if t != ValueType::AttrSet { bail!("expected an attrset, but got a {:?}", t); } - let n = unsafe { raw::get_attrs_size(self.context.ptr(), v.raw_ptr()) as usize }; - self.context.check_err()?; + let n = self.context.check_one_call(|ctx_ptr| unsafe { + raw::get_attrs_size(ctx_ptr, v.raw_ptr()) as usize + })?; let mut attrs = Vec::with_capacity(n); - unsafe { - for i in 0..n { - let cstr_ptr: *const i8 = raw::get_attr_name_byidx( - self.context.ptr(), - v.raw_ptr(), - self.raw_ptr(), - i as c_uint, - ); - self.context.check_err()?; - let cstr = std::ffi::CStr::from_ptr(cstr_ptr); - let s = cstr.to_str().map_err(|e| { - anyhow::format_err!("Nix attrset key is not valid UTF-8: {}", e) - })?; - attrs.insert(i, s.to_owned()); - } + for i in 0..n { + let cstr_ptr: *const i8 = self.context.check_one_call(|ctx_ptr| unsafe { + raw::get_attr_name_byidx(ctx_ptr, v.raw_ptr(), self.raw_ptr(), i as c_uint) + })?; + let cstr = unsafe { std::ffi::CStr::from_ptr(cstr_ptr) }; + let s = cstr + .to_str() + .map_err(|e| anyhow::format_err!("Nix attrset key is not valid UTF-8: {}", e))?; + attrs.insert(i, s.to_owned()); } Ok(attrs) } + // TODO: make this the main implementation and move the error handling to require_attrs_select_opt + // that gets rid of the odd self.context.check_err() usage that relies on the context not being reset pub fn require_attrs_select(&self, v: &Value, attr_name: &str) -> Result { let r = self.require_attrs_select_opt(v, attr_name)?; match r { @@ -231,21 +227,21 @@ impl EvalState { pub fn new_value_str(&self, s: &str) -> Result { let s = CString::new(s).with_context(|| "new_value_str: contains null byte")?; let v = unsafe { - let value = self.new_value_uninitialized(); - raw::init_string(self.context.ptr(), value.raw_ptr(), s.as_ptr()); + let value = self.new_value_uninitialized()?; + self.context + .check_one_call(|ctx_ptr| raw::init_string(ctx_ptr, value.raw_ptr(), s.as_ptr()))?; value }; - self.context.check_err()?; Ok(v) } pub fn new_value_int(&self, i: Int) -> Result { let v = unsafe { - let value = self.new_value_uninitialized(); - raw::init_int(self.context.ptr(), value.raw_ptr(), i); + let value = self.new_value_uninitialized()?; + self.context + .check_one_call(|ctx_ptr| raw::init_int(ctx_ptr, value.raw_ptr(), i))?; value }; - self.context.check_err()?; Ok(v) } @@ -253,14 +249,15 @@ impl EvalState { fn get_string(&self, value: &Value) -> Result { let mut r = result_string_init!(); unsafe { - raw::get_string( - self.context.ptr(), - value.raw_ptr(), - Some(callback_get_result_string), - callback_get_result_string_data(&mut r), - ) + self.context.check_one_call(|ctx_ptr| { + raw::get_string( + ctx_ptr, + value.raw_ptr(), + Some(callback_get_result_string), + callback_get_result_string_data(&mut r), + ) + })?; }; - self.context.check_err()?; r } /// NOTE: this will be replaced by two methods, one that also returns the context, and one that checks that the context is empty @@ -281,15 +278,14 @@ impl EvalState { bail!("expected a string, but got a {:?}", t); } - let rs = unsafe { + let rs = self.context.check_one_call(|ctx_ptr| unsafe { raw::string_realise( - self.context.ptr(), + ctx_ptr, self.raw_ptr(), value.raw_ptr(), is_import_from_derivation, ) - }; - self.context.check_err()?; + })?; let s = unsafe { let start = raw::realised_string_get_buffer_start(rs) as *const u8; @@ -321,41 +317,37 @@ impl EvalState { /// /// For a lazy version, see [`new_value_apply`][`EvalState::new_value_apply`]. pub fn call(&self, f: Value, a: Value) -> Result { - let v = unsafe { - let value = self.new_value_uninitialized(); - raw::value_call( - self.context.ptr(), - self.raw_ptr(), - f.raw_ptr(), - a.raw_ptr(), - value.raw_ptr(), - ); - value - }; - self.context.check_err()?; - Ok(v) + unsafe { + let value = self.new_value_uninitialized()?; + self.context.check_one_call(|ctx_ptr| { + raw::value_call( + ctx_ptr, + self.raw_ptr(), + f.raw_ptr(), + a.raw_ptr(), + value.raw_ptr(), + ) + })?; + Ok(value) + } } /// Apply a function to an argument, but don't evaluate the result just yet. /// /// For an eager version, see [`call`][`EvalState::call`]. pub fn new_value_apply(&self, f: &Value, a: &Value) -> Result { - let value = self.new_value_uninitialized(); - unsafe { - raw::init_apply( - self.context.ptr(), - value.raw_ptr(), - f.raw_ptr(), - a.raw_ptr(), - ); - }; - self.context.check_err()?; - Ok(value) + let value = self.new_value_uninitialized()?; + self.context.check_one_call(|ctx_ptr| unsafe { + raw::init_apply(ctx_ptr, value.raw_ptr(), f.raw_ptr(), a.raw_ptr()); + value + }) } - fn new_value_uninitialized(&self) -> Value { - let value = unsafe { raw::alloc_value(self.context.ptr(), self.raw_ptr()) }; - Value::new(value) + fn new_value_uninitialized(&self) -> Result { + let value = self + .context + .check_one_call(|ctx_ptr| unsafe { raw::alloc_value(ctx_ptr, self.raw_ptr()) })?; + Ok(Value::new(value)) } } diff --git a/rust/nix-util/src/context.rs b/rust/nix-util/src/context.rs index 73540ca..a53e619 100644 --- a/rust/nix-util/src/context.rs +++ b/rust/nix-util/src/context.rs @@ -34,6 +34,31 @@ impl Context { } Ok(()) } + + pub fn check_err_and_clear(&self) -> Result<()> { + let r = self.check_err(); + if r.is_err() { + unsafe { + // TODO (https://github.com/NixOS/nix/pull/10910): raw::err_clear + raw::set_err_msg( + self.inner.as_ptr(), + raw::NIX_OK.try_into().unwrap(), + b"\0".as_ptr() as *const i8, + ); + } + } + r + } + + /// Run the function, and check the error, then reset the error. + /// Make at most one call to a Nix function in `f`. + /// Do not use if the context isn't fresh or cleared (e.g. with `check_err_and_clear`). + pub fn check_one_call T>(&self, f: F) -> Result { + let t = f(self.ptr()); + self.check_err_and_clear()?; + Ok(t) + } + /// NIX_ERR_KEY is returned when e.g. an attribute is missing. Return true if the error is of this type. pub fn is_key_error(&self) -> bool { unsafe { raw::err_code(self.inner.as_ptr()) == raw::NIX_ERR_KEY } From c16a9b0595e4b3ba539cba5266129b252f2d5674 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 14 Jun 2024 18:21:13 +0200 Subject: [PATCH 070/306] refact: Make require_attrs_select* error handling regular (cherry picked from commit de6c6cbd462202405bc787fed02dee249cf16973) --- rust/nix-expr/src/eval_state.rs | 56 +++++++++++++++++++-------------- rust/nix-util/src/context.rs | 32 ++++++++++++++----- 2 files changed, 56 insertions(+), 32 deletions(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 6e09439..8db152b 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -184,20 +184,27 @@ impl EvalState { Ok(attrs) } - // TODO: make this the main implementation and move the error handling to require_attrs_select_opt - // that gets rid of the odd self.context.check_err() usage that relies on the context not being reset + /// Evaluate, require that the value is an attrset, and select an attribute by name. pub fn require_attrs_select(&self, v: &Value, attr_name: &str) -> Result { - let r = self.require_attrs_select_opt(v, attr_name)?; - match r { - Some(v) => Ok(v), - None => self.context.check_err().and_then(|_| { - // should be unreachable - bail!("attribute not found: {}", attr_name) - }), + let t = self.value_type(v)?; + if t != ValueType::AttrSet { + bail!("expected an attrset, but got a {:?}", t); } + let attr_name = CString::new(attr_name) + .with_context(|| "require_attrs_select: attrName contains null byte")?; + let v2 = self.context.check_one_call(|ctx_ptr| unsafe { + raw::get_attr_byname(ctx_ptr, v.raw_ptr(), self.raw_ptr(), attr_name.as_ptr()) + })?; + Ok(Value::new(v2)) } /// Evaluate, require that the value is an attrset, and select an attribute by name. + /// + /// Return `Err(...)` if `v` is not an attrset, or if some other error occurred. + /// + /// Return `Ok(None)` if the attribute is not present. + /// + /// Return `Ok(Some(value))` if the attribute is present. pub fn require_attrs_select_opt(&self, v: &Value, attr_name: &str) -> Result> { let t = self.value_type(v)?; if t != ValueType::AttrSet { @@ -205,21 +212,10 @@ impl EvalState { } let attr_name = CString::new(attr_name) .with_context(|| "require_attrs_select_opt: attrName contains null byte")?; - // c_void should be Value; why was void generated? - let v = unsafe { - raw::get_attr_byname( - self.context.ptr(), - v.raw_ptr(), - self.raw_ptr(), - attr_name.as_ptr(), - ) - }; - if self.context.is_key_error() { - Ok(None) - } else { - self.context.check_err()?; - Ok(Some(Value::new(v))) - } + let v2 = self.context.check_one_call_or_key_none(|ctx_ptr| unsafe { + raw::get_attr_byname(ctx_ptr, v.raw_ptr(), self.raw_ptr(), attr_name.as_ptr()) + })?; + Ok(v2.map(Value::new)) } /// Create a new value containing the passed string. @@ -575,6 +571,18 @@ mod tests { let b = es.require_attrs_select(&v, "b").unwrap(); assert_eq!(es.require_string(&a).unwrap(), "aye"); assert_eq!(es.require_string(&b).unwrap(), "bee"); + let missing = es.require_attrs_select(&v, "c"); + match missing { + Ok(_) => panic!("expected an error"), + Err(e) => { + let s = format!("{e:#}"); + // TODO: bad error message from Nix + if !s.contains("missing attribute") { + eprintln!("unexpected error message: {}", s); + assert!(false); + } + } + } }) .unwrap() } diff --git a/rust/nix-util/src/context.rs b/rust/nix-util/src/context.rs index a53e619..1e8e6e1 100644 --- a/rust/nix-util/src/context.rs +++ b/rust/nix-util/src/context.rs @@ -35,17 +35,20 @@ impl Context { Ok(()) } + pub fn clear(&self) { + unsafe { + raw::set_err_msg( + self.inner.as_ptr(), + raw::NIX_OK.try_into().unwrap(), + b"\0".as_ptr() as *const i8, + ); + } + } + pub fn check_err_and_clear(&self) -> Result<()> { let r = self.check_err(); if r.is_err() { - unsafe { - // TODO (https://github.com/NixOS/nix/pull/10910): raw::err_clear - raw::set_err_msg( - self.inner.as_ptr(), - raw::NIX_OK.try_into().unwrap(), - b"\0".as_ptr() as *const i8, - ); - } + self.clear(); } r } @@ -59,6 +62,19 @@ impl Context { Ok(t) } + pub fn check_one_call_or_key_none T>( + &self, + f: F, + ) -> Result> { + let t = f(self.ptr()); + if self.is_key_error() { + self.clear(); + return Ok(None); + } + self.check_err_and_clear()?; + Ok(Some(t)) + } + /// NIX_ERR_KEY is returned when e.g. an attribute is missing. Return true if the error is of this type. pub fn is_key_error(&self) -> bool { unsafe { raw::err_code(self.inner.as_ptr()) == raw::NIX_ERR_KEY } From 878b7931617206b7284d0b740b3050fb0cc0d6fd Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 14 Jun 2024 19:17:35 +0200 Subject: [PATCH 071/306] test: eval_state_require_int_forces_thunk (cherry picked from commit 90de70936a726a8ae1aaf3e9aacb9a83b8d30b72) --- rust/nix-expr/src/eval_state.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 8db152b..f958ee4 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -513,6 +513,22 @@ mod tests { .unwrap(); } + #[test] + fn eval_state_require_int_forces_thunk() { + gc_registering_current_thread(|| { + let store = Store::open("auto", HashMap::new()).unwrap(); + let es = EvalState::new(store, []).unwrap(); + let f = es.eval_from_string("x: x + 1", "").unwrap(); + let a = es.eval_from_string("2", "").unwrap(); + let v = es.new_value_apply(&f, &a).unwrap(); + let t = es.value_type_unforced(&v); + assert!(t == None); + let i = es.require_int(&v).unwrap(); + assert!(i == 3); + }) + .unwrap(); + } + #[test] fn eval_state_value_attrs_names_empty() { gc_registering_current_thread(|| { From b04d429f19b405763f9f2e565f34d90900bfab94 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 14 Jun 2024 19:29:33 +0200 Subject: [PATCH 072/306] test: Make sure require_* functions force thunks (cherry picked from commit 69b661db22e4b16fb08d322f5fede480438e2b71) --- rust/nix-expr/src/eval_state.rs | 78 +++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index f958ee4..592abb1 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -529,6 +529,29 @@ mod tests { .unwrap(); } + /// A helper that turns an expression into a thunk. + fn make_thunk(es: &EvalState, expr: &str) -> Value { + // This would be silly in real code, but it works for the current Nix implementation. + // A Nix implementation that applies the identity function eagerly would be a valid + // Nix implementation, but annoying because we'll have to change this helper to do + // something more complicated that isn't optimized away. + let f = es.eval_from_string("x: x", "").unwrap(); + let v = es.eval_from_string(expr, "").unwrap(); + es.new_value_apply(&f, &v).unwrap() + } + + #[test] + fn make_thunk_helper_works() { + gc_registering_current_thread(|| { + let store = Store::open("auto", HashMap::new()).unwrap(); + let es = EvalState::new(store, []).unwrap(); + let v = make_thunk(&es, "1"); + let t = es.value_type_unforced(&v); + assert!(t == None); + }) + .unwrap(); + } + #[test] fn eval_state_value_attrs_names_empty() { gc_registering_current_thread(|| { @@ -544,6 +567,20 @@ mod tests { .unwrap() } + #[test] + fn eval_state_require_attrs_names_forces_thunk() { + gc_registering_current_thread(|| { + let store = Store::open("auto", HashMap::new()).unwrap(); + let es = EvalState::new(store, []).unwrap(); + let v = make_thunk(&es, "{ a = 1; b = 2; }"); + let t = es.value_type_unforced(&v); + assert!(t == None); + let attrs = es.require_attrs_names(&v).unwrap(); + assert_eq!(attrs.len(), 2); + }) + .unwrap() + } + #[test] fn eval_state_require_attrs_names_bad_type() { gc_registering_current_thread(|| { @@ -603,6 +640,20 @@ mod tests { .unwrap() } + #[test] + fn eval_state_require_attrs_select_forces_thunk() { + gc_registering_current_thread(|| { + let store = Store::open("auto", HashMap::new()).unwrap(); + let es = EvalState::new(store, []).unwrap(); + let expr = r#"{ a = "aye"; b = "bee"; }"#; + let v = make_thunk(&es, expr); + assert!(es.value_type_unforced(&v).is_none()); + let r = es.require_attrs_select(&v, "a"); + assert!(r.is_ok()); + }) + .unwrap() + } + #[test] fn eval_state_require_attrs_select_error() { gc_registering_current_thread(|| { @@ -641,6 +692,20 @@ mod tests { .unwrap() } + #[test] + fn eval_state_require_attrs_select_opt_forces_thunk() { + gc_registering_current_thread(|| { + let store = Store::open("auto", HashMap::new()).unwrap(); + let es = EvalState::new(store, []).unwrap(); + let expr = r#"{ a = "aye"; b = "bee"; }"#; + let v = make_thunk(&es, expr); + assert!(es.value_type_unforced(&v).is_none()); + let r = es.require_attrs_select_opt(&v, "a"); + assert!(r.is_ok()); + }) + .unwrap() + } + #[test] fn eval_state_require_attrs_select_opt_error() { gc_registering_current_thread(|| { @@ -677,6 +742,19 @@ mod tests { .unwrap(); } + #[test] + fn eval_state_value_string_forces_thunk() { + gc_registering_current_thread(|| { + let store = Store::open("auto", HashMap::new()).unwrap(); + let es = EvalState::new(store, []).unwrap(); + let v = make_thunk(&es, "\"hello\""); + assert!(es.value_type_unforced(&v).is_none()); + let s = es.require_string(&v).unwrap(); + assert!(s == "hello"); + }) + .unwrap(); + } + #[test] fn eval_state_value_string_unexpected_bool() { gc_registering_current_thread(|| { From 93a2db836a140cf18750ed20766580d7774acc0e Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 14 Jun 2024 19:32:56 +0200 Subject: [PATCH 073/306] doc: Clarify why we test call and apply and how they relate (cherry picked from commit 17c7a8d30d1c4d99eeb84a6203ae33d2ba781edb) --- rust/nix-expr/src/eval_state.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 592abb1..1e21e19 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -1050,6 +1050,7 @@ mod tests { .unwrap(); } + /// This tests the behavior of `call`, which is strict, unlike `new_value_apply`. #[test] fn eval_state_call_fail_args() { gc_registering_current_thread(|| { @@ -1071,8 +1072,9 @@ mod tests { .unwrap(); } + /// This tests the behavior of `new_value_apply`, which is lazy, unlike `call`. #[test] - fn eval_state_apply_fail_args() { + fn eval_state_apply_fail_args_lazy() { gc_registering_current_thread(|| { let store = Store::open("auto", HashMap::new()).unwrap(); let es = EvalState::new(store, []).unwrap(); From 2f3a5fb03956d660d8c4a80546d2e5dfb4c47f66 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 14 Jun 2024 19:58:44 +0200 Subject: [PATCH 074/306] refact: Clean up Context interface to be safer The mutation-based methods had some pitfalls, and we don't really need them. We could re-add them if we need to. (cherry picked from commit ca92b8491d87cebf54dd2468599168fc7a16c07f) --- rust/nix-expr/src/value.rs | 5 +++-- rust/nix-store/src/store.rs | 21 +++++++++------------ rust/nix-util/src/context.rs | 25 ++++++++++++++++--------- rust/nix-util/src/settings.rs | 22 ++++++++++------------ 4 files changed, 38 insertions(+), 35 deletions(-) diff --git a/rust/nix-expr/src/value.rs b/rust/nix-expr/src/value.rs index da33c87..83aa26e 100644 --- a/rust/nix-expr/src/value.rs +++ b/rust/nix-expr/src/value.rs @@ -74,9 +74,10 @@ impl Drop for Value { impl Clone for Value { fn clone(&self) -> Self { let context = Context::new(); - unsafe { raw::gc_incref(context.ptr(), self.inner.as_ptr()) }; + context + .check_one_call(|ctx_ptr| unsafe { raw::gc_incref(ctx_ptr, self.inner.as_ptr()) }) + .unwrap(); // can't return an error here, but we don't want to ignore the error either as it means we could use-after-free - context.check_err().unwrap(); Value { inner: self.inner } } } diff --git a/rust/nix-store/src/store.rs b/rust/nix-store/src/store.rs index ea0812b..f05bd07 100644 --- a/rust/nix-store/src/store.rs +++ b/rust/nix-store/src/store.rs @@ -11,11 +11,9 @@ use std::ptr::NonNull; /* TODO make Nix itself thread safe */ lazy_static! { static ref INIT: Result<()> = { - unsafe { - let context: Context = Context::new(); - raw::libstore_init(context.ptr()); - context.check_err() - } + Context::new().check_one_call(|ctx_ptr| unsafe { + raw::libstore_init(ctx_ptr); + }) }; } @@ -76,9 +74,9 @@ impl Store { .chain(std::iter::once(null_mut())) // signal the end of the array .collect(); - let store = - unsafe { raw::store_open(context.ptr(), uri_ptr.as_ptr(), params.as_mut_ptr()) }; - context.check_err()?; + let store = context.check_one_call(|ctx_ptr| unsafe { + raw::store_open(ctx_ptr, uri_ptr.as_ptr(), params.as_mut_ptr()) + })?; if store.is_null() { panic!("nix_c_store_open returned a null pointer without an error"); } @@ -97,15 +95,14 @@ impl Store { pub fn get_uri(&self) -> Result { let mut r = result_string_init!(); - unsafe { + self.context.check_one_call(|ctx_ptr| unsafe { raw::store_get_uri( - self.context.ptr(), + ctx_ptr, self.inner.ptr(), Some(callback_get_result_string), callback_get_result_string_data(&mut r), ) - }; - self.context.check_err()?; + })?; r } } diff --git a/rust/nix-util/src/context.rs b/rust/nix-util/src/context.rs index 1e8e6e1..657e85a 100644 --- a/rust/nix-util/src/context.rs +++ b/rust/nix-util/src/context.rs @@ -20,10 +20,22 @@ impl Context { }; ctx } - pub fn ptr(&self) -> *mut raw::c_context { + + /// This is currently private because of the switch to `check_one_call`, which is more ergonomic. + /// The pattern its callers use make it hard to forget to use this function. + /// + /// If we have a good use case for `check_err`, we can expose it again. + fn ptr(&self) -> *mut raw::c_context { self.inner.as_ptr() } - pub fn check_err(&self) -> Result<()> { + + /// Check the error code and return an error if it's not `NIX_OK`. + /// + /// This is currently private because of the switch to `check_one_call`, which is more ergonomic. + /// The pattern its callers use make it hard to forget to use this function. + /// + /// If we have a good use case for `check_err`, we can expose it again. + fn check_err(&self) -> Result<()> { let err = unsafe { raw::err_code(self.inner.as_ptr()) }; if err != raw::NIX_OK.try_into().unwrap() { // msgp is a borrowed pointer (pointing into the context), so we don't need to free it @@ -35,7 +47,7 @@ impl Context { Ok(()) } - pub fn clear(&self) { + fn clear(&self) { unsafe { raw::set_err_msg( self.inner.as_ptr(), @@ -67,18 +79,13 @@ impl Context { f: F, ) -> Result> { let t = f(self.ptr()); - if self.is_key_error() { + if unsafe { raw::err_code(self.inner.as_ptr()) == raw::NIX_ERR_KEY } { self.clear(); return Ok(None); } self.check_err_and_clear()?; Ok(Some(t)) } - - /// NIX_ERR_KEY is returned when e.g. an attribute is missing. Return true if the error is of this type. - pub fn is_key_error(&self) -> bool { - unsafe { raw::err_code(self.inner.as_ptr()) == raw::NIX_ERR_KEY } - } } impl Drop for Context { diff --git a/rust/nix-util/src/settings.rs b/rust/nix-util/src/settings.rs index 36964ad..6b4d0f4 100644 --- a/rust/nix-util/src/settings.rs +++ b/rust/nix-util/src/settings.rs @@ -10,25 +10,23 @@ pub fn set(key: &str, value: &str) -> Result<()> { let ctx = context::Context::new(); let key = std::ffi::CString::new(key)?; let value = std::ffi::CString::new(value)?; - unsafe { - raw::setting_set(ctx.ptr(), key.as_ptr(), value.as_ptr()); - }; - ctx.check_err() + ctx.check_one_call(|ctx_ptr| unsafe { + raw::setting_set(ctx_ptr, key.as_ptr(), value.as_ptr()); + }) } pub fn get(key: &str) -> Result { let ctx = context::Context::new(); let key = std::ffi::CString::new(key)?; let mut r: Result = result_string_init!(); - unsafe { + ctx.check_one_call(|ctx_ptr| unsafe { raw::setting_get( - ctx.ptr(), + ctx_ptr, key.as_ptr(), Some(callback_get_result_string), callback_get_result_string_data(&mut r), ) - }; - ctx.check_err()?; + })?; r } @@ -39,10 +37,10 @@ mod tests { #[ctor::ctor] fn setup() { let ctx = context::Context::new(); - unsafe { - nix_c_raw::libstore_init(ctx.ptr()); - }; - ctx.check_err().unwrap(); + ctx.check_one_call(|ctx_ptr| unsafe { + nix_c_raw::libstore_init(ctx_ptr); + }) + .unwrap(); } #[test] From 226639939f9624a1bc81fdd8a8fc198af10734df Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Sat, 15 Jun 2024 12:09:11 +0200 Subject: [PATCH 075/306] feat: nix_util::context::check_call!(func[...]) > I couldn't figure out a way to use () for the function call, but I didn't try that hard. I (Robert) have also given it a shot, briefly, unsuccessfully. While I was critical of over-engineering this, it turns out that when we start to use `mut`, a macro is much more practical, because it doesn't create a new scope where we need a copy of a mutable reference, which of course is not allowed. (cherry picked from commit a2acc93d13991da4b14a99065acd589477334d07) --- rust/nix-util/src/context.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/rust/nix-util/src/context.rs b/rust/nix-util/src/context.rs index 657e85a..75c4b1c 100644 --- a/rust/nix-util/src/context.rs +++ b/rust/nix-util/src/context.rs @@ -96,6 +96,24 @@ impl Drop for Context { } } +#[macro_export] +macro_rules! check_call { + ($f:path[$ctx:expr $(, $arg:expr)*]) => { + { + let ret = $f($ctx.ptr() $(, $arg)*); + match $ctx.check_err() { + Ok(_) => Ok(ret), + Err(e) => { + $ctx.clear(); + Err(e) + } + } + } + } +} + +pub use check_call; + #[cfg(test)] mod tests { use super::*; From a6dbf177786c9ae535a93e607628affcea31e767 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 15 Jun 2024 12:40:45 +0200 Subject: [PATCH 076/306] fix: Require mutable Context, as it should be This spreads out transitively to many places and requires that we use `check_call!` instead of `check_one_call` in a number of places. (cherry picked from commit 6bc31dcf206518a7be7f0ac6e773d5dfe25531ea) --- rust/nix-expr/src/eval_state.rs | 239 +++++++++++++++++--------------- rust/nix-expr/src/value.rs | 2 +- rust/nix-store/src/store.rs | 8 +- rust/nix-util/src/context.rs | 8 +- rust/nix-util/src/settings.rs | 15 +- 5 files changed, 143 insertions(+), 129 deletions(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 1e21e19..60465bb 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -6,8 +6,8 @@ use nix_c_raw as raw; use nix_store::path::StorePath; use nix_store::store::Store; use nix_util::context::Context; -use nix_util::result_string_init; use nix_util::string_return::{callback_get_result_string, callback_get_result_string_data}; +use nix_util::{check_call, check_call_opt_key, result_string_init}; use std::ffi::{c_char, CString}; use std::os::raw::c_uint; use std::ptr::{null, null_mut, NonNull}; @@ -45,7 +45,7 @@ pub struct EvalState { } impl EvalState { pub fn new<'a>(store: Store, lookup_path: impl IntoIterator) -> Result { - let context = Context::new(); + let mut context = Context::new(); // this intermediate value must be here and must not be moved // because it owns the data the `*const c_char` pointers point to. @@ -96,49 +96,43 @@ impl EvalState { /// use nix_expr::value::Value; /// /// # fn main() -> anyhow::Result<()> { - /// # let es = EvalState::new(Store::open("auto", [])?, [])?; + /// # let mut es = EvalState::new(Store::open("auto", [])?, [])?; /// let v: Value = es.eval_from_string("42", ".")?; /// assert_eq!(es.require_int(&v)?, 42); /// # Ok(()) /// # } /// ``` #[doc(alias = "nix_expr_eval_from_string")] - pub fn eval_from_string(&self, expr: &str, path: &str) -> Result { + pub fn eval_from_string(&mut self, expr: &str, path: &str) -> Result { let expr_ptr = CString::new(expr).with_context(|| "eval_from_string: expr contains null byte")?; let path_ptr = CString::new(path).with_context(|| "eval_from_string: path contains null byte")?; unsafe { let value = self.new_value_uninitialized()?; - self.context.check_one_call(|ctx_ptr| { - raw::expr_eval_from_string( - ctx_ptr, - self.raw_ptr(), - expr_ptr.as_ptr(), - path_ptr.as_ptr(), - value.raw_ptr(), - ) - })?; + check_call!(raw::expr_eval_from_string[ + self.context, + self.raw_ptr(), + expr_ptr.as_ptr(), + path_ptr.as_ptr(), + value.raw_ptr() + ])?; Ok(value) } } /// Try turn any Value into a Value that isn't a Thunk. - pub fn force(&self, v: &Value) -> Result<()> { - unsafe { - self.context.check_one_call(|ctx_ptr| { - raw::value_force(ctx_ptr, self.raw_ptr(), v.raw_ptr()); - })?; - } + pub fn force(&mut self, v: &Value) -> Result<()> { + unsafe { check_call!(raw::value_force[self.context, self.raw_ptr(), v.raw_ptr()]) }?; Ok(()) } - pub fn value_type_unforced(&self, value: &Value) -> Option { + pub fn value_type_unforced(&mut self, value: &Value) -> Option { let r = self .context .check_one_call(|ctx_ptr| unsafe { raw::get_type(ctx_ptr, value.raw_ptr()) }); // .unwrap(): no reason for this to fail, as it does not evaluate ValueType::from_raw(r.unwrap()) } - pub fn value_type(&self, value: &Value) -> Result { + pub fn value_type(&mut self, value: &Value) -> Result { match self.value_type_unforced(value) { Some(a) => Ok(a), None => { @@ -152,7 +146,7 @@ impl EvalState { } } } - pub fn require_int(&self, v: &Value) -> Result { + pub fn require_int(&mut self, v: &Value) -> Result { let t = self.value_type(v)?; if t != ValueType::Int { bail!("expected an int, but got a {:?}", t); @@ -162,7 +156,7 @@ impl EvalState { } /// Evaluate, and require that the value is an attrset. /// Returns a list of the keys in the attrset. - pub fn require_attrs_names(&self, v: &Value) -> Result> { + pub fn require_attrs_names(&mut self, v: &Value) -> Result> { let t = self.value_type(v)?; if t != ValueType::AttrSet { bail!("expected an attrset, but got a {:?}", t); @@ -172,9 +166,14 @@ impl EvalState { })?; let mut attrs = Vec::with_capacity(n); for i in 0..n { - let cstr_ptr: *const i8 = self.context.check_one_call(|ctx_ptr| unsafe { - raw::get_attr_name_byidx(ctx_ptr, v.raw_ptr(), self.raw_ptr(), i as c_uint) - })?; + let cstr_ptr: *const i8 = unsafe { + check_call!(raw::get_attr_name_byidx[ + self.context, + v.raw_ptr(), + self.raw_ptr(), + i as c_uint + ]) + }?; let cstr = unsafe { std::ffi::CStr::from_ptr(cstr_ptr) }; let s = cstr .to_str() @@ -185,16 +184,21 @@ impl EvalState { } /// Evaluate, require that the value is an attrset, and select an attribute by name. - pub fn require_attrs_select(&self, v: &Value, attr_name: &str) -> Result { + pub fn require_attrs_select(&mut self, v: &Value, attr_name: &str) -> Result { let t = self.value_type(v)?; if t != ValueType::AttrSet { bail!("expected an attrset, but got a {:?}", t); } let attr_name = CString::new(attr_name) .with_context(|| "require_attrs_select: attrName contains null byte")?; - let v2 = self.context.check_one_call(|ctx_ptr| unsafe { - raw::get_attr_byname(ctx_ptr, v.raw_ptr(), self.raw_ptr(), attr_name.as_ptr()) - })?; + let v2 = unsafe { + check_call!(raw::get_attr_byname[ + self.context, + v.raw_ptr(), + self.raw_ptr(), + attr_name.as_ptr() + ]) + }?; Ok(Value::new(v2)) } @@ -205,22 +209,31 @@ impl EvalState { /// Return `Ok(None)` if the attribute is not present. /// /// Return `Ok(Some(value))` if the attribute is present. - pub fn require_attrs_select_opt(&self, v: &Value, attr_name: &str) -> Result> { + pub fn require_attrs_select_opt( + &mut self, + v: &Value, + attr_name: &str, + ) -> Result> { let t = self.value_type(v)?; if t != ValueType::AttrSet { bail!("expected an attrset, but got a {:?}", t); } let attr_name = CString::new(attr_name) .with_context(|| "require_attrs_select_opt: attrName contains null byte")?; - let v2 = self.context.check_one_call_or_key_none(|ctx_ptr| unsafe { - raw::get_attr_byname(ctx_ptr, v.raw_ptr(), self.raw_ptr(), attr_name.as_ptr()) - })?; + let v2 = unsafe { + check_call_opt_key!(raw::get_attr_byname[ + self.context, + v.raw_ptr(), + self.raw_ptr(), + attr_name.as_ptr() + ]) + }?; Ok(v2.map(Value::new)) } /// Create a new value containing the passed string. /// Returns a string value without any string context. - pub fn new_value_str(&self, s: &str) -> Result { + pub fn new_value_str(&mut self, s: &str) -> Result { let s = CString::new(s).with_context(|| "new_value_str: contains null byte")?; let v = unsafe { let value = self.new_value_uninitialized()?; @@ -231,7 +244,7 @@ impl EvalState { Ok(v) } - pub fn new_value_int(&self, i: Int) -> Result { + pub fn new_value_int(&mut self, i: Int) -> Result { let v = unsafe { let value = self.new_value_uninitialized()?; self.context @@ -242,7 +255,7 @@ impl EvalState { } /// Not exposed, because the caller must always explicitly handle the context or not accept one at all. - fn get_string(&self, value: &Value) -> Result { + fn get_string(&mut self, value: &Value) -> Result { let mut r = result_string_init!(); unsafe { self.context.check_one_call(|ctx_ptr| { @@ -257,7 +270,7 @@ impl EvalState { r } /// NOTE: this will be replaced by two methods, one that also returns the context, and one that checks that the context is empty - pub fn require_string(&self, value: &Value) -> Result { + pub fn require_string(&mut self, value: &Value) -> Result { let t = self.value_type(value)?; if t != ValueType::String { bail!("expected a string, but got a {:?}", t); @@ -265,7 +278,7 @@ impl EvalState { self.get_string(value) } pub fn realise_string( - &self, + &mut self, value: &Value, is_import_from_derivation: bool, ) -> Result { @@ -274,14 +287,14 @@ impl EvalState { bail!("expected a string, but got a {:?}", t); } - let rs = self.context.check_one_call(|ctx_ptr| unsafe { - raw::string_realise( - ctx_ptr, + let rs = unsafe { + check_call!(raw::string_realise[ + self.context, self.raw_ptr(), value.raw_ptr(), - is_import_from_derivation, - ) - })?; + is_import_from_derivation + ]) + }?; let s = unsafe { let start = raw::realised_string_get_buffer_start(rs) as *const u8; @@ -312,37 +325,38 @@ impl EvalState { /// Eagerly apply a function to an argument. /// /// For a lazy version, see [`new_value_apply`][`EvalState::new_value_apply`]. - pub fn call(&self, f: Value, a: Value) -> Result { + pub fn call(&mut self, f: Value, a: Value) -> Result { + let value = self.new_value_uninitialized()?; unsafe { - let value = self.new_value_uninitialized()?; - self.context.check_one_call(|ctx_ptr| { - raw::value_call( - ctx_ptr, - self.raw_ptr(), - f.raw_ptr(), - a.raw_ptr(), - value.raw_ptr(), - ) - })?; - Ok(value) - } + check_call!(raw::value_call[ + self.context, + self.raw_ptr(), + f.raw_ptr(), + a.raw_ptr(), + value.raw_ptr() + ]) + }?; + Ok(value) } /// Apply a function to an argument, but don't evaluate the result just yet. /// /// For an eager version, see [`call`][`EvalState::call`]. - pub fn new_value_apply(&self, f: &Value, a: &Value) -> Result { + pub fn new_value_apply(&mut self, f: &Value, a: &Value) -> Result { let value = self.new_value_uninitialized()?; - self.context.check_one_call(|ctx_ptr| unsafe { - raw::init_apply(ctx_ptr, value.raw_ptr(), f.raw_ptr(), a.raw_ptr()); - value - }) + unsafe { + check_call!(raw::init_apply[ + self.context, + value.raw_ptr(), + f.raw_ptr(), + a.raw_ptr() + ]) + }?; + Ok(value) } - fn new_value_uninitialized(&self) -> Result { - let value = self - .context - .check_one_call(|ctx_ptr| unsafe { raw::alloc_value(ctx_ptr, self.raw_ptr()) })?; + fn new_value_uninitialized(&mut self) -> Result { + let value = unsafe { check_call!(raw::alloc_value[self.context, self.raw_ptr()]) }?; Ok(Value::new(value)) } } @@ -447,10 +461,10 @@ mod tests { writeln!(test_file0, "{integer0}").unwrap(); writeln!(test_file1, "{integer1}").unwrap(); gc_registering_current_thread(|| { - let es = EvalState::new(Store::open("auto", HashMap::new()).unwrap(), []).unwrap(); + let mut es = EvalState::new(Store::open("auto", HashMap::new()).unwrap(), []).unwrap(); assert!(es.eval_from_string(import_expression, "").is_err()); - let es = EvalState::new( + let mut es = EvalState::new( Store::open("auto", HashMap::new()).unwrap(), [ format!("test_file0={}", test_file0.path().to_str().unwrap()).as_str(), @@ -458,9 +472,8 @@ mod tests { ], ) .unwrap(); - let v = es - .require_int(&es.eval_from_string(import_expression, "").unwrap()) - .unwrap(); + let ie = &es.eval_from_string(import_expression, "").unwrap(); + let v = es.require_int(ie).unwrap(); assert_eq!(v, integer0 + integer1); }) .unwrap(); @@ -472,7 +485,7 @@ mod tests { fn eval_state_eval_from_string() { gc_registering_current_thread(|| { let store = Store::open("auto", HashMap::new()).unwrap(); - let es = EvalState::new(store, []).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); let v = es.eval_from_string("1", "").unwrap(); let v2 = v.clone(); es.force(&v).unwrap(); @@ -489,7 +502,7 @@ mod tests { fn eval_state_value_bool() { gc_registering_current_thread(|| { let store = Store::open("auto", HashMap::new()).unwrap(); - let es = EvalState::new(store, []).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); let v = es.eval_from_string("true", "").unwrap(); es.force(&v).unwrap(); let t = es.value_type_unforced(&v); @@ -502,7 +515,7 @@ mod tests { fn eval_state_value_int() { gc_registering_current_thread(|| { let store = Store::open("auto", HashMap::new()).unwrap(); - let es = EvalState::new(store, []).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); let v = es.eval_from_string("1", "").unwrap(); es.force(&v).unwrap(); let t = es.value_type(&v).unwrap(); @@ -517,7 +530,7 @@ mod tests { fn eval_state_require_int_forces_thunk() { gc_registering_current_thread(|| { let store = Store::open("auto", HashMap::new()).unwrap(); - let es = EvalState::new(store, []).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); let f = es.eval_from_string("x: x + 1", "").unwrap(); let a = es.eval_from_string("2", "").unwrap(); let v = es.new_value_apply(&f, &a).unwrap(); @@ -530,7 +543,7 @@ mod tests { } /// A helper that turns an expression into a thunk. - fn make_thunk(es: &EvalState, expr: &str) -> Value { + fn make_thunk(es: &mut EvalState, expr: &str) -> Value { // This would be silly in real code, but it works for the current Nix implementation. // A Nix implementation that applies the identity function eagerly would be a valid // Nix implementation, but annoying because we'll have to change this helper to do @@ -544,8 +557,8 @@ mod tests { fn make_thunk_helper_works() { gc_registering_current_thread(|| { let store = Store::open("auto", HashMap::new()).unwrap(); - let es = EvalState::new(store, []).unwrap(); - let v = make_thunk(&es, "1"); + let mut es = EvalState::new(store, []).unwrap(); + let v = make_thunk(&mut es, "1"); let t = es.value_type_unforced(&v); assert!(t == None); }) @@ -556,7 +569,7 @@ mod tests { fn eval_state_value_attrs_names_empty() { gc_registering_current_thread(|| { let store = Store::open("auto", HashMap::new()).unwrap(); - let es = EvalState::new(store, []).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); let v = es.eval_from_string("{ }", "").unwrap(); es.force(&v).unwrap(); let t = es.value_type_unforced(&v); @@ -571,8 +584,8 @@ mod tests { fn eval_state_require_attrs_names_forces_thunk() { gc_registering_current_thread(|| { let store = Store::open("auto", HashMap::new()).unwrap(); - let es = EvalState::new(store, []).unwrap(); - let v = make_thunk(&es, "{ a = 1; b = 2; }"); + let mut es = EvalState::new(store, []).unwrap(); + let v = make_thunk(&mut es, "{ a = 1; b = 2; }"); let t = es.value_type_unforced(&v); assert!(t == None); let attrs = es.require_attrs_names(&v).unwrap(); @@ -585,7 +598,7 @@ mod tests { fn eval_state_require_attrs_names_bad_type() { gc_registering_current_thread(|| { let store = Store::open("auto", HashMap::new()).unwrap(); - let es = EvalState::new(store, []).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); let v = es.eval_from_string("1", "").unwrap(); es.force(&v).unwrap(); let r = es.require_attrs_names(&v); @@ -602,7 +615,7 @@ mod tests { fn eval_state_value_attrs_names_example() { gc_registering_current_thread(|| { let store = Store::open("auto", HashMap::new()).unwrap(); - let es = EvalState::new(store, []).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); let expr = r#"{ a = throw "nope a"; b = throw "nope b"; }"#; let v = es.eval_from_string(expr, "").unwrap(); let attrs = es.require_attrs_names(&v).unwrap(); @@ -617,7 +630,7 @@ mod tests { fn eval_state_require_attrs_select() { gc_registering_current_thread(|| { let store = Store::open("auto", HashMap::new()).unwrap(); - let es = EvalState::new(store, []).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); let expr = r#"{ a = "aye"; b = "bee"; }"#; let v = es.eval_from_string(expr, "").unwrap(); let a = es.require_attrs_select(&v, "a").unwrap(); @@ -644,9 +657,9 @@ mod tests { fn eval_state_require_attrs_select_forces_thunk() { gc_registering_current_thread(|| { let store = Store::open("auto", HashMap::new()).unwrap(); - let es = EvalState::new(store, []).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); let expr = r#"{ a = "aye"; b = "bee"; }"#; - let v = make_thunk(&es, expr); + let v = make_thunk(&mut es, expr); assert!(es.value_type_unforced(&v).is_none()); let r = es.require_attrs_select(&v, "a"); assert!(r.is_ok()); @@ -658,7 +671,7 @@ mod tests { fn eval_state_require_attrs_select_error() { gc_registering_current_thread(|| { let store = Store::open("auto", HashMap::new()).unwrap(); - let es = EvalState::new(store, []).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); let expr = r#"{ a = throw "oh no the error"; }"#; let v = es.eval_from_string(expr, "").unwrap(); let r = es.require_attrs_select(&v, "a"); @@ -679,7 +692,7 @@ mod tests { fn eval_state_require_attrs_select_opt() { gc_registering_current_thread(|| { let store = Store::open("auto", HashMap::new()).unwrap(); - let es = EvalState::new(store, []).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); let expr = r#"{ a = "aye"; b = "bee"; }"#; let v = es.eval_from_string(expr, "").unwrap(); let a = es.require_attrs_select_opt(&v, "a").unwrap().unwrap(); @@ -696,9 +709,9 @@ mod tests { fn eval_state_require_attrs_select_opt_forces_thunk() { gc_registering_current_thread(|| { let store = Store::open("auto", HashMap::new()).unwrap(); - let es = EvalState::new(store, []).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); let expr = r#"{ a = "aye"; b = "bee"; }"#; - let v = make_thunk(&es, expr); + let v = make_thunk(&mut es, expr); assert!(es.value_type_unforced(&v).is_none()); let r = es.require_attrs_select_opt(&v, "a"); assert!(r.is_ok()); @@ -710,7 +723,7 @@ mod tests { fn eval_state_require_attrs_select_opt_error() { gc_registering_current_thread(|| { let store = Store::open("auto", HashMap::new()).unwrap(); - let es = EvalState::new(store, []).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); let expr = r#"{ a = throw "oh no the error"; }"#; let v = es.eval_from_string(expr, "").unwrap(); let r = es.require_attrs_select_opt(&v, "a"); @@ -731,7 +744,7 @@ mod tests { fn eval_state_value_string() { gc_registering_current_thread(|| { let store = Store::open("auto", HashMap::new()).unwrap(); - let es = EvalState::new(store, []).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); let v = es.eval_from_string("\"hello\"", "").unwrap(); es.force(&v).unwrap(); let t = es.value_type_unforced(&v); @@ -746,8 +759,8 @@ mod tests { fn eval_state_value_string_forces_thunk() { gc_registering_current_thread(|| { let store = Store::open("auto", HashMap::new()).unwrap(); - let es = EvalState::new(store, []).unwrap(); - let v = make_thunk(&es, "\"hello\""); + let mut es = EvalState::new(store, []).unwrap(); + let v = make_thunk(&mut es, "\"hello\""); assert!(es.value_type_unforced(&v).is_none()); let s = es.require_string(&v).unwrap(); assert!(s == "hello"); @@ -759,7 +772,7 @@ mod tests { fn eval_state_value_string_unexpected_bool() { gc_registering_current_thread(|| { let store = Store::open("auto", HashMap::new()).unwrap(); - let es = EvalState::new(store, []).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); let v = es.eval_from_string("true", "").unwrap(); es.force(&v).unwrap(); let r = es.require_string(&v); @@ -777,7 +790,7 @@ mod tests { fn eval_state_value_string_unexpected_path_value() { gc_registering_current_thread(|| { let store = Store::open("auto", HashMap::new()).unwrap(); - let es = EvalState::new(store, []).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); let v = es.eval_from_string("/foo", "").unwrap(); es.force(&v).unwrap(); let r = es.require_string(&v); @@ -794,7 +807,7 @@ mod tests { fn eval_state_value_string_bad_utf() { gc_registering_current_thread(|| { let store = Store::open("auto", HashMap::new()).unwrap(); - let es = EvalState::new(store, []).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); let v = es .eval_from_string("builtins.substring 0 1 \"ü\"", "") .unwrap(); @@ -815,7 +828,7 @@ mod tests { fn eval_state_value_string_unexpected_context() { gc_registering_current_thread(|| { let store = Store::open("auto", HashMap::new()).unwrap(); - let es = EvalState::new(store, []).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); let v = es .eval_from_string("(derivation { name = \"hello\"; system = \"dummy\"; builder = \"cmd.exe\"; }).outPath", "") .unwrap(); @@ -834,7 +847,7 @@ mod tests { fn eval_state_new_string() { gc_registering_current_thread(|| { let store = Store::open("auto", HashMap::new()).unwrap(); - let es = EvalState::new(store, []).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); let v = es.new_value_str("hello").unwrap(); es.force(&v).unwrap(); let t = es.value_type_unforced(&v); @@ -849,7 +862,7 @@ mod tests { fn eval_state_new_string_empty() { gc_registering_current_thread(|| { let store = Store::open("auto", HashMap::new()).unwrap(); - let es = EvalState::new(store, []).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); let v = es.new_value_str("").unwrap(); es.force(&v).unwrap(); let t = es.value_type_unforced(&v); @@ -864,7 +877,7 @@ mod tests { fn eval_state_new_string_invalid() { gc_registering_current_thread(|| { let store = Store::open("auto", HashMap::new()).unwrap(); - let es = EvalState::new(store, []).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); let r = es.new_value_str("hell\0no"); match r { Ok(_) => panic!("expected an error"), @@ -883,7 +896,7 @@ mod tests { fn eval_state_new_int() { gc_registering_current_thread(|| { let store = Store::open("auto", HashMap::new()).unwrap(); - let es = EvalState::new(store, []).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); let v = es.new_value_int(42).unwrap(); es.force(&v).unwrap(); let t = es.value_type_unforced(&v); @@ -898,7 +911,7 @@ mod tests { fn eval_state_value_attrset() { gc_registering_current_thread(|| { let store = Store::open("auto", HashMap::new()).unwrap(); - let es = EvalState::new(store, []).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); let v = es.eval_from_string("{ }", "").unwrap(); es.force(&v).unwrap(); let t = es.value_type_unforced(&v); @@ -911,7 +924,7 @@ mod tests { fn eval_state_value_list() { gc_registering_current_thread(|| { let store = Store::open("auto", HashMap::new()).unwrap(); - let es = EvalState::new(store, []).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); let v = es .eval_from_string("[ ]", "") .unwrap(); @@ -926,7 +939,7 @@ mod tests { fn eval_state_realise_string() { gc_registering_current_thread(|| { let store = Store::open("auto", HashMap::new()).unwrap(); - let es = EvalState::new(store, []).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); let expr = r#" '' a derivation output: ${ @@ -973,7 +986,7 @@ mod tests { fn eval_state_call() { gc_registering_current_thread(|| { let store = Store::open("auto", HashMap::new()).unwrap(); - let es = EvalState::new(store, []).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); let f = es.eval_from_string("x: x + 1", "").unwrap(); let a = es.eval_from_string("2", "").unwrap(); let v = es.call(f, a).unwrap(); @@ -990,7 +1003,7 @@ mod tests { fn eval_state_apply() { gc_registering_current_thread(|| { let store = Store::open("auto", HashMap::new()).unwrap(); - let es = EvalState::new(store, []).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); // This is a function that takes two arguments. let f = es.eval_from_string("x: x + 1", "").unwrap(); let a = es.eval_from_string("2", "").unwrap(); @@ -1009,7 +1022,7 @@ mod tests { fn eval_state_call_fail_body() { gc_registering_current_thread(|| { let store = Store::open("auto", HashMap::new()).unwrap(); - let es = EvalState::new(store, []).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); let f = es.eval_from_string("x: x + 1", "").unwrap(); let a = es.eval_from_string("true", "").unwrap(); let r = es.call(f, a); @@ -1030,7 +1043,7 @@ mod tests { fn eval_state_apply_fail_body() { gc_registering_current_thread(|| { let store = Store::open("auto", HashMap::new()).unwrap(); - let es = EvalState::new(store, []).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); let f = es.eval_from_string("x: x + 1", "").unwrap(); let a = es.eval_from_string("true", "").unwrap(); // Lazy => no error @@ -1055,7 +1068,7 @@ mod tests { fn eval_state_call_fail_args() { gc_registering_current_thread(|| { let store = Store::open("auto", HashMap::new()).unwrap(); - let es = EvalState::new(store, []).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); let f = es.eval_from_string("{x}: x + 1", "").unwrap(); let a = es.eval_from_string("{}", "").unwrap(); let r = es.call(f, a); @@ -1077,7 +1090,7 @@ mod tests { fn eval_state_apply_fail_args_lazy() { gc_registering_current_thread(|| { let store = Store::open("auto", HashMap::new()).unwrap(); - let es = EvalState::new(store, []).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); let f = es.eval_from_string("{x}: x + 1", "").unwrap(); let a = es.eval_from_string("{}", "").unwrap(); // Lazy => no error @@ -1107,7 +1120,7 @@ mod tests { let log = tempfile::tempdir().unwrap(); let log_path = log.path().to_str().unwrap(); - let es = EvalState::new( + let mut es = EvalState::new( Store::open( "local", HashMap::from([ diff --git a/rust/nix-expr/src/value.rs b/rust/nix-expr/src/value.rs index 83aa26e..b25e449 100644 --- a/rust/nix-expr/src/value.rs +++ b/rust/nix-expr/src/value.rs @@ -73,7 +73,7 @@ impl Drop for Value { } impl Clone for Value { fn clone(&self) -> Self { - let context = Context::new(); + let mut context = Context::new(); context .check_one_call(|ctx_ptr| unsafe { raw::gc_incref(ctx_ptr, self.inner.as_ptr()) }) .unwrap(); diff --git a/rust/nix-store/src/store.rs b/rust/nix-store/src/store.rs index f05bd07..1e692c0 100644 --- a/rust/nix-store/src/store.rs +++ b/rust/nix-store/src/store.rs @@ -52,7 +52,7 @@ impl Store { } } - let context: Context = Context::new(); + let mut context: Context = Context::new(); let uri_ptr = CString::new(url)?; @@ -93,7 +93,7 @@ impl Store { self.inner.ptr() } - pub fn get_uri(&self) -> Result { + pub fn get_uri(&mut self) -> Result { let mut r = result_string_init!(); self.context.check_one_call(|ctx_ptr| unsafe { raw::store_get_uri( @@ -127,7 +127,7 @@ mod tests { #[test] fn get_uri() { - let store = Store::open("auto", HashMap::new()).unwrap(); + let mut store = Store::open("auto", HashMap::new()).unwrap(); let uri = store.get_uri().unwrap(); assert!(!uri.is_empty()); // must be ascii @@ -139,7 +139,7 @@ mod tests { #[test] #[ignore] // Needs network access fn get_uri_nixos_cache() { - let store = Store::open("https://cache.nixos.org/", HashMap::new()).unwrap(); + let mut store = Store::open("https://cache.nixos.org/", HashMap::new()).unwrap(); let uri = store.get_uri().unwrap(); assert_eq!(uri, "https://cache.nixos.org"); } diff --git a/rust/nix-util/src/context.rs b/rust/nix-util/src/context.rs index 75c4b1c..dbb7b23 100644 --- a/rust/nix-util/src/context.rs +++ b/rust/nix-util/src/context.rs @@ -47,7 +47,7 @@ impl Context { Ok(()) } - fn clear(&self) { + pub fn clear(&mut self) { unsafe { raw::set_err_msg( self.inner.as_ptr(), @@ -57,7 +57,7 @@ impl Context { } } - pub fn check_err_and_clear(&self) -> Result<()> { + pub fn check_err_and_clear(&mut self) -> Result<()> { let r = self.check_err(); if r.is_err() { self.clear(); @@ -68,14 +68,14 @@ impl Context { /// Run the function, and check the error, then reset the error. /// Make at most one call to a Nix function in `f`. /// Do not use if the context isn't fresh or cleared (e.g. with `check_err_and_clear`). - pub fn check_one_call T>(&self, f: F) -> Result { + pub fn check_one_call T>(&mut self, f: F) -> Result { let t = f(self.ptr()); self.check_err_and_clear()?; Ok(t) } pub fn check_one_call_or_key_none T>( - &self, + &mut self, f: F, ) -> Result> { let t = f(self.ptr()); diff --git a/rust/nix-util/src/settings.rs b/rust/nix-util/src/settings.rs index 6b4d0f4..3cce59c 100644 --- a/rust/nix-util/src/settings.rs +++ b/rust/nix-util/src/settings.rs @@ -7,7 +7,7 @@ use crate::{ }; pub fn set(key: &str, value: &str) -> Result<()> { - let ctx = context::Context::new(); + let mut ctx = context::Context::new(); let key = std::ffi::CString::new(key)?; let value = std::ffi::CString::new(value)?; ctx.check_one_call(|ctx_ptr| unsafe { @@ -16,7 +16,7 @@ pub fn set(key: &str, value: &str) -> Result<()> { } pub fn get(key: &str) -> Result { - let ctx = context::Context::new(); + let mut ctx = context::Context::new(); let key = std::ffi::CString::new(key)?; let mut r: Result = result_string_init!(); ctx.check_one_call(|ctx_ptr| unsafe { @@ -32,15 +32,16 @@ pub fn get(key: &str) -> Result { #[cfg(test)] mod tests { + use crate::check_call; + use super::*; #[ctor::ctor] fn setup() { - let ctx = context::Context::new(); - ctx.check_one_call(|ctx_ptr| unsafe { - nix_c_raw::libstore_init(ctx_ptr); - }) - .unwrap(); + let mut ctx = context::Context::new(); + unsafe { + check_call!(raw::libstore_init[ctx]).unwrap(); + } } #[test] From 870c03ee4d058bacd5e15e6acbc9f5dc21fdbf6c Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 15 Jun 2024 12:45:23 +0200 Subject: [PATCH 077/306] fix: Revert hiding of Context::ptr, add check_call_opt_key (cherry picked from commit 5fba79143c85177d1649aa97d70ece067bade0fd) --- rust/nix-util/src/context.rs | 37 +++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/rust/nix-util/src/context.rs b/rust/nix-util/src/context.rs index dbb7b23..3b9cf36 100644 --- a/rust/nix-util/src/context.rs +++ b/rust/nix-util/src/context.rs @@ -21,21 +21,17 @@ impl Context { ctx } - /// This is currently private because of the switch to `check_one_call`, which is more ergonomic. - /// The pattern its callers use make it hard to forget to use this function. + /// Access the C context pointer. /// - /// If we have a good use case for `check_err`, we can expose it again. - fn ptr(&self) -> *mut raw::c_context { + /// We recommend to use `check_call!` if possible. + pub fn ptr(&mut self) -> *mut raw::c_context { self.inner.as_ptr() } /// Check the error code and return an error if it's not `NIX_OK`. /// - /// This is currently private because of the switch to `check_one_call`, which is more ergonomic. - /// The pattern its callers use make it hard to forget to use this function. - /// - /// If we have a good use case for `check_err`, we can expose it again. - fn check_err(&self) -> Result<()> { + /// We recommend to use `check_call!` if possible. + pub fn check_err(&self) -> Result<()> { let err = unsafe { raw::err_code(self.inner.as_ptr()) }; if err != raw::NIX_OK.try_into().unwrap() { // msgp is a borrowed pointer (pointing into the context), so we don't need to free it @@ -114,6 +110,29 @@ macro_rules! check_call { pub use check_call; +// TODO: Generalize this macro to work with any error code or any error handling logic +#[macro_export] +macro_rules! check_call_opt_key { + ($f:path[$ctx:expr, $($arg:expr),*]) => { + { + let ret = $f($ctx.ptr(), $($arg,)*); + if unsafe { raw::err_code($ctx.ptr()) == raw::NIX_ERR_KEY } { + $ctx.clear(); + return Ok(None); + } + match $ctx.check_err() { + Ok(_) => Ok(Some(ret)), + Err(e) => { + $ctx.clear(); + Err(e) + } + } + } + } +} + +pub use check_call_opt_key; + #[cfg(test)] mod tests { use super::*; From bf6dbd3f1e56454a96d624cc56f23ea7e6261720 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 15 Jun 2024 14:34:30 +0200 Subject: [PATCH 078/306] fix: Do not duplicate ctx expr in check_call! (cherry picked from commit 9b6f0c1e66d80ec2e92ce079152edb22c05e9b1e) --- rust/nix-expr/src/eval_state.rs | 29 ++++++++++++++++------------- rust/nix-util/src/context.rs | 24 +++++++++++++++++++++--- rust/nix-util/src/settings.rs | 2 +- 3 files changed, 38 insertions(+), 17 deletions(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 60465bb..b9656c2 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -111,8 +111,8 @@ impl EvalState { unsafe { let value = self.new_value_uninitialized()?; check_call!(raw::expr_eval_from_string[ - self.context, - self.raw_ptr(), + &mut self.context, + self.eval_state.as_ptr(), expr_ptr.as_ptr(), path_ptr.as_ptr(), value.raw_ptr() @@ -122,7 +122,9 @@ impl EvalState { } /// Try turn any Value into a Value that isn't a Thunk. pub fn force(&mut self, v: &Value) -> Result<()> { - unsafe { check_call!(raw::value_force[self.context, self.raw_ptr(), v.raw_ptr()]) }?; + unsafe { + check_call!(raw::value_force[&mut self.context, self.eval_state.as_ptr(), v.raw_ptr()]) + }?; Ok(()) } pub fn value_type_unforced(&mut self, value: &Value) -> Option { @@ -168,9 +170,9 @@ impl EvalState { for i in 0..n { let cstr_ptr: *const i8 = unsafe { check_call!(raw::get_attr_name_byidx[ - self.context, + &mut self.context, v.raw_ptr(), - self.raw_ptr(), + self.eval_state.as_ptr(), i as c_uint ]) }?; @@ -193,9 +195,9 @@ impl EvalState { .with_context(|| "require_attrs_select: attrName contains null byte")?; let v2 = unsafe { check_call!(raw::get_attr_byname[ - self.context, + &mut self.context, v.raw_ptr(), - self.raw_ptr(), + self.eval_state.as_ptr(), attr_name.as_ptr() ]) }?; @@ -289,8 +291,8 @@ impl EvalState { let rs = unsafe { check_call!(raw::string_realise[ - self.context, - self.raw_ptr(), + &mut self.context, + self.eval_state.as_ptr(), value.raw_ptr(), is_import_from_derivation ]) @@ -329,8 +331,8 @@ impl EvalState { let value = self.new_value_uninitialized()?; unsafe { check_call!(raw::value_call[ - self.context, - self.raw_ptr(), + &mut self.context, + self.eval_state.as_ptr(), f.raw_ptr(), a.raw_ptr(), value.raw_ptr() @@ -346,7 +348,7 @@ impl EvalState { let value = self.new_value_uninitialized()?; unsafe { check_call!(raw::init_apply[ - self.context, + &mut self.context, value.raw_ptr(), f.raw_ptr(), a.raw_ptr() @@ -356,7 +358,8 @@ impl EvalState { } fn new_value_uninitialized(&mut self) -> Result { - let value = unsafe { check_call!(raw::alloc_value[self.context, self.raw_ptr()]) }?; + let value = + unsafe { check_call!(raw::alloc_value[&mut self.context, self.eval_state.as_ptr()]) }?; Ok(Value::new(value)) } } diff --git a/rust/nix-util/src/context.rs b/rust/nix-util/src/context.rs index 3b9cf36..5a47ac6 100644 --- a/rust/nix-util/src/context.rs +++ b/rust/nix-util/src/context.rs @@ -96,11 +96,12 @@ impl Drop for Context { macro_rules! check_call { ($f:path[$ctx:expr $(, $arg:expr)*]) => { { - let ret = $f($ctx.ptr() $(, $arg)*); - match $ctx.check_err() { + let ctx : &mut $crate::context::Context = $ctx; + let ret = $f(ctx.ptr() $(, $arg)*); + match ctx.check_err() { Ok(_) => Ok(ret), Err(e) => { - $ctx.clear(); + ctx.clear(); Err(e) } } @@ -142,4 +143,21 @@ mod tests { // don't crash let _c = Context::new(); } + + fn set_dummy_err(ctx_ptr: *mut raw::c_context) { + unsafe { + raw::set_err_msg( + ctx_ptr, + raw::NIX_ERR_UNKNOWN.try_into().unwrap(), + b"dummy error message\0".as_ptr() as *const i8, + ); + } + } + + #[test] + fn check_call_dynamic_context() { + let r = check_call!(set_dummy_err[&mut Context::new()]); + assert!(r.is_err()); + assert_eq!(r.unwrap_err().to_string(), "dummy error message"); + } } diff --git a/rust/nix-util/src/settings.rs b/rust/nix-util/src/settings.rs index 3cce59c..138ffe6 100644 --- a/rust/nix-util/src/settings.rs +++ b/rust/nix-util/src/settings.rs @@ -40,7 +40,7 @@ mod tests { fn setup() { let mut ctx = context::Context::new(); unsafe { - check_call!(raw::libstore_init[ctx]).unwrap(); + check_call!(raw::libstore_init[&mut ctx]).unwrap(); } } From 35803f4a30cdbaad30d7e9ffbd2ebf322d51aff6 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 15 Jun 2024 14:51:26 +0200 Subject: [PATCH 079/306] refactor: Remove check_one_call (cherry picked from commit 065f880e52c6d6cb44e4b857272176ebe2464eea) --- rust/nix-expr/src/eval_state.rs | 51 +++++++++++++++------------------ rust/nix-expr/src/value.rs | 12 ++++---- rust/nix-store/src/store.rs | 26 +++++++---------- rust/nix-util/src/context.rs | 9 ------ rust/nix-util/src/settings.rs | 20 ++++++------- 5 files changed, 48 insertions(+), 70 deletions(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index b9656c2..a45c75f 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -16,10 +16,9 @@ lazy_static! { static ref INIT: Result<()> = { unsafe { raw::GC_allow_register_threads(); + check_call!(raw::libexpr_init[&mut Context::new()])?; + Ok(()) } - Context::new().check_one_call(|ctx_ptr| unsafe { - raw::libexpr_init(ctx_ptr); - }) }; } pub fn init() -> Result<()> { @@ -67,9 +66,9 @@ impl EvalState { init()?; - let eval_state = context.check_one_call(|ctx_ptr| unsafe { - raw::state_create(ctx_ptr, lookup_path.as_mut_ptr(), store.raw_ptr()) - })?; + let eval_state = unsafe { + check_call!(raw::state_create[&mut context, lookup_path.as_mut_ptr(), store.raw_ptr()]) + }?; Ok(EvalState { eval_state: NonNull::new(eval_state).unwrap_or_else(|| { panic!("nix_state_create returned a null pointer without an error") @@ -128,9 +127,12 @@ impl EvalState { Ok(()) } pub fn value_type_unforced(&mut self, value: &Value) -> Option { - let r = self - .context - .check_one_call(|ctx_ptr| unsafe { raw::get_type(ctx_ptr, value.raw_ptr()) }); + let r = unsafe { + check_call!(raw::get_type[ + &mut self.context, + value.raw_ptr() + ]) + }; // .unwrap(): no reason for this to fail, as it does not evaluate ValueType::from_raw(r.unwrap()) } @@ -153,8 +155,7 @@ impl EvalState { if t != ValueType::Int { bail!("expected an int, but got a {:?}", t); } - self.context - .check_one_call(|ctx_ptr| unsafe { raw::get_int(ctx_ptr, v.raw_ptr()) }) + unsafe { check_call!(raw::get_int[&mut self.context, v.raw_ptr()]) } } /// Evaluate, and require that the value is an attrset. /// Returns a list of the keys in the attrset. @@ -163,10 +164,8 @@ impl EvalState { if t != ValueType::AttrSet { bail!("expected an attrset, but got a {:?}", t); } - let n = self.context.check_one_call(|ctx_ptr| unsafe { - raw::get_attrs_size(ctx_ptr, v.raw_ptr()) as usize - })?; - let mut attrs = Vec::with_capacity(n); + let n = unsafe { check_call!(raw::get_attrs_size[&mut self.context, v.raw_ptr()]) }?; + let mut attrs = Vec::with_capacity(n as usize); for i in 0..n { let cstr_ptr: *const i8 = unsafe { check_call!(raw::get_attr_name_byidx[ @@ -180,7 +179,7 @@ impl EvalState { let s = cstr .to_str() .map_err(|e| anyhow::format_err!("Nix attrset key is not valid UTF-8: {}", e))?; - attrs.insert(i, s.to_owned()); + attrs.insert(i as usize, s.to_owned()); } Ok(attrs) } @@ -239,8 +238,7 @@ impl EvalState { let s = CString::new(s).with_context(|| "new_value_str: contains null byte")?; let v = unsafe { let value = self.new_value_uninitialized()?; - self.context - .check_one_call(|ctx_ptr| raw::init_string(ctx_ptr, value.raw_ptr(), s.as_ptr()))?; + check_call!(raw::init_string[&mut self.context, value.raw_ptr(), s.as_ptr()])?; value }; Ok(v) @@ -249,8 +247,7 @@ impl EvalState { pub fn new_value_int(&mut self, i: Int) -> Result { let v = unsafe { let value = self.new_value_uninitialized()?; - self.context - .check_one_call(|ctx_ptr| raw::init_int(ctx_ptr, value.raw_ptr(), i))?; + check_call!(raw::init_int[&mut self.context, value.raw_ptr(), i])?; value }; Ok(v) @@ -260,14 +257,12 @@ impl EvalState { fn get_string(&mut self, value: &Value) -> Result { let mut r = result_string_init!(); unsafe { - self.context.check_one_call(|ctx_ptr| { - raw::get_string( - ctx_ptr, - value.raw_ptr(), - Some(callback_get_result_string), - callback_get_result_string_data(&mut r), - ) - })?; + check_call!(raw::get_string[ + &mut self.context, + value.raw_ptr(), + Some(callback_get_result_string), + callback_get_result_string_data(&mut r) + ])?; }; r } diff --git a/rust/nix-expr/src/value.rs b/rust/nix-expr/src/value.rs index b25e449..e7ff7c7 100644 --- a/rust/nix-expr/src/value.rs +++ b/rust/nix-expr/src/value.rs @@ -1,5 +1,5 @@ use nix_c_raw as raw; -use nix_util::context::Context; +use nix_util::{check_call, context::Context}; use std::ptr::{null_mut, NonNull}; // TODO: test: cloning a thunk does not duplicate the evaluation. @@ -73,10 +73,12 @@ impl Drop for Value { } impl Clone for Value { fn clone(&self) -> Self { - let mut context = Context::new(); - context - .check_one_call(|ctx_ptr| unsafe { raw::gc_incref(ctx_ptr, self.inner.as_ptr()) }) - .unwrap(); + // TODO: Is it worth allocating a new Context here? Ideally cloning is cheap. + // this is very unlikely to error, and it is not recoverable + // Maybe try without, and try again with context to report details? + unsafe { + check_call!(raw::gc_incref[&mut Context::new(), self.inner.as_ptr()]).unwrap(); + } // can't return an error here, but we don't want to ignore the error either as it means we could use-after-free Value { inner: self.inner } } diff --git a/rust/nix-store/src/store.rs b/rust/nix-store/src/store.rs index 1e692c0..d827153 100644 --- a/rust/nix-store/src/store.rs +++ b/rust/nix-store/src/store.rs @@ -2,18 +2,17 @@ use anyhow::{bail, Result}; use lazy_static::lazy_static; use nix_c_raw as raw; use nix_util::context::Context; -use nix_util::result_string_init; use nix_util::string_return::{callback_get_result_string, callback_get_result_string_data}; +use nix_util::{check_call, result_string_init}; use std::ffi::{c_char, CString}; use std::ptr::null_mut; use std::ptr::NonNull; /* TODO make Nix itself thread safe */ lazy_static! { - static ref INIT: Result<()> = { - Context::new().check_one_call(|ctx_ptr| unsafe { - raw::libstore_init(ctx_ptr); - }) + static ref INIT: Result<()> = unsafe { + check_call!(raw::libstore_init[&mut Context::new()])?; + Ok(()) }; } @@ -74,9 +73,9 @@ impl Store { .chain(std::iter::once(null_mut())) // signal the end of the array .collect(); - let store = context.check_one_call(|ctx_ptr| unsafe { - raw::store_open(ctx_ptr, uri_ptr.as_ptr(), params.as_mut_ptr()) - })?; + let store = unsafe { + check_call!(raw::store_open[&mut context, uri_ptr.as_ptr(), params.as_mut_ptr()]) + }?; if store.is_null() { panic!("nix_c_store_open returned a null pointer without an error"); } @@ -95,14 +94,9 @@ impl Store { pub fn get_uri(&mut self) -> Result { let mut r = result_string_init!(); - self.context.check_one_call(|ctx_ptr| unsafe { - raw::store_get_uri( - ctx_ptr, - self.inner.ptr(), - Some(callback_get_result_string), - callback_get_result_string_data(&mut r), - ) - })?; + unsafe { + check_call!(raw::store_get_uri[&mut self.context, self.inner.ptr(), Some(callback_get_result_string), callback_get_result_string_data(&mut r)]) + }?; r } } diff --git a/rust/nix-util/src/context.rs b/rust/nix-util/src/context.rs index 5a47ac6..cec4063 100644 --- a/rust/nix-util/src/context.rs +++ b/rust/nix-util/src/context.rs @@ -61,15 +61,6 @@ impl Context { r } - /// Run the function, and check the error, then reset the error. - /// Make at most one call to a Nix function in `f`. - /// Do not use if the context isn't fresh or cleared (e.g. with `check_err_and_clear`). - pub fn check_one_call T>(&mut self, f: F) -> Result { - let t = f(self.ptr()); - self.check_err_and_clear()?; - Ok(t) - } - pub fn check_one_call_or_key_none T>( &mut self, f: F, diff --git a/rust/nix-util/src/settings.rs b/rust/nix-util/src/settings.rs index 138ffe6..218e3b5 100644 --- a/rust/nix-util/src/settings.rs +++ b/rust/nix-util/src/settings.rs @@ -2,7 +2,7 @@ use anyhow::Result; use nix_c_raw as raw; use crate::{ - context, result_string_init, + check_call, context, result_string_init, string_return::{callback_get_result_string, callback_get_result_string_data}, }; @@ -10,23 +10,19 @@ pub fn set(key: &str, value: &str) -> Result<()> { let mut ctx = context::Context::new(); let key = std::ffi::CString::new(key)?; let value = std::ffi::CString::new(value)?; - ctx.check_one_call(|ctx_ptr| unsafe { - raw::setting_set(ctx_ptr, key.as_ptr(), value.as_ptr()); - }) + unsafe { + check_call!(raw::setting_set[&mut ctx, key.as_ptr(), value.as_ptr()])?; + } + Ok(()) } pub fn get(key: &str) -> Result { let mut ctx = context::Context::new(); let key = std::ffi::CString::new(key)?; let mut r: Result = result_string_init!(); - ctx.check_one_call(|ctx_ptr| unsafe { - raw::setting_get( - ctx_ptr, - key.as_ptr(), - Some(callback_get_result_string), - callback_get_result_string_data(&mut r), - ) - })?; + unsafe { + check_call!(raw::setting_get[&mut ctx, key.as_ptr(), Some(callback_get_result_string), callback_get_result_string_data(&mut r)])?; + } r } From da26721beaed12b30faaee62d7f9a26f33fdab7f Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 15 Jun 2024 14:55:23 +0200 Subject: [PATCH 080/306] fix: Do not duplicate ctx expr in check_call_opt_key! (cherry picked from commit 86ddc63a573cd08ec19008448ec2fca33a84159e) --- rust/nix-expr/src/eval_state.rs | 4 ++-- rust/nix-util/src/context.rs | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index a45c75f..833a366 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -223,9 +223,9 @@ impl EvalState { .with_context(|| "require_attrs_select_opt: attrName contains null byte")?; let v2 = unsafe { check_call_opt_key!(raw::get_attr_byname[ - self.context, + &mut self.context, v.raw_ptr(), - self.raw_ptr(), + self.eval_state.as_ptr(), attr_name.as_ptr() ]) }?; diff --git a/rust/nix-util/src/context.rs b/rust/nix-util/src/context.rs index cec4063..e838cc6 100644 --- a/rust/nix-util/src/context.rs +++ b/rust/nix-util/src/context.rs @@ -107,15 +107,16 @@ pub use check_call; macro_rules! check_call_opt_key { ($f:path[$ctx:expr, $($arg:expr),*]) => { { - let ret = $f($ctx.ptr(), $($arg,)*); - if unsafe { raw::err_code($ctx.ptr()) == raw::NIX_ERR_KEY } { - $ctx.clear(); + let ctx : &mut $crate::context::Context = $ctx; + let ret = $f(ctx.ptr(), $($arg,)*); + if unsafe { raw::err_code(ctx.ptr()) == raw::NIX_ERR_KEY } { + ctx.clear(); return Ok(None); } - match $ctx.check_err() { + match ctx.check_err() { Ok(_) => Ok(Some(ret)), Err(e) => { - $ctx.clear(); + ctx.clear(); Err(e) } } From cc2e640e7fb01e35c10b2739f2562ffa0b1ed32d Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Tue, 2 Jul 2024 16:35:32 -0400 Subject: [PATCH 081/306] check_call!() macro to use () not [] (cherry picked from commit 66d255af0a5d331782dc24c89bb45d3434f3c109) --- rust/nix-expr/src/eval_state.rs | 69 +++++++++++++++++++-------------- rust/nix-expr/src/value.rs | 2 +- rust/nix-store/src/store.rs | 15 +++++-- rust/nix-util/src/context.rs | 6 +-- rust/nix-util/src/settings.rs | 11 ++++-- 5 files changed, 64 insertions(+), 39 deletions(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 833a366..d99f50d 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -16,7 +16,7 @@ lazy_static! { static ref INIT: Result<()> = { unsafe { raw::GC_allow_register_threads(); - check_call!(raw::libexpr_init[&mut Context::new()])?; + check_call!(raw::libexpr_init(&mut Context::new()))?; Ok(()) } }; @@ -67,7 +67,11 @@ impl EvalState { init()?; let eval_state = unsafe { - check_call!(raw::state_create[&mut context, lookup_path.as_mut_ptr(), store.raw_ptr()]) + check_call!(raw::state_create( + &mut context, + lookup_path.as_mut_ptr(), + store.raw_ptr() + )) }?; Ok(EvalState { eval_state: NonNull::new(eval_state).unwrap_or_else(|| { @@ -109,30 +113,29 @@ impl EvalState { CString::new(path).with_context(|| "eval_from_string: path contains null byte")?; unsafe { let value = self.new_value_uninitialized()?; - check_call!(raw::expr_eval_from_string[ + check_call!(raw::expr_eval_from_string( &mut self.context, self.eval_state.as_ptr(), expr_ptr.as_ptr(), path_ptr.as_ptr(), value.raw_ptr() - ])?; + ))?; Ok(value) } } /// Try turn any Value into a Value that isn't a Thunk. pub fn force(&mut self, v: &Value) -> Result<()> { unsafe { - check_call!(raw::value_force[&mut self.context, self.eval_state.as_ptr(), v.raw_ptr()]) + check_call!(raw::value_force( + &mut self.context, + self.eval_state.as_ptr(), + v.raw_ptr() + )) }?; Ok(()) } pub fn value_type_unforced(&mut self, value: &Value) -> Option { - let r = unsafe { - check_call!(raw::get_type[ - &mut self.context, - value.raw_ptr() - ]) - }; + let r = unsafe { check_call!(raw::get_type(&mut self.context, value.raw_ptr())) }; // .unwrap(): no reason for this to fail, as it does not evaluate ValueType::from_raw(r.unwrap()) } @@ -155,7 +158,7 @@ impl EvalState { if t != ValueType::Int { bail!("expected an int, but got a {:?}", t); } - unsafe { check_call!(raw::get_int[&mut self.context, v.raw_ptr()]) } + unsafe { check_call!(raw::get_int(&mut self.context, v.raw_ptr())) } } /// Evaluate, and require that the value is an attrset. /// Returns a list of the keys in the attrset. @@ -164,16 +167,16 @@ impl EvalState { if t != ValueType::AttrSet { bail!("expected an attrset, but got a {:?}", t); } - let n = unsafe { check_call!(raw::get_attrs_size[&mut self.context, v.raw_ptr()]) }?; + let n = unsafe { check_call!(raw::get_attrs_size(&mut self.context, v.raw_ptr())) }?; let mut attrs = Vec::with_capacity(n as usize); for i in 0..n { let cstr_ptr: *const i8 = unsafe { - check_call!(raw::get_attr_name_byidx[ + check_call!(raw::get_attr_name_byidx( &mut self.context, v.raw_ptr(), self.eval_state.as_ptr(), i as c_uint - ]) + )) }?; let cstr = unsafe { std::ffi::CStr::from_ptr(cstr_ptr) }; let s = cstr @@ -193,12 +196,12 @@ impl EvalState { let attr_name = CString::new(attr_name) .with_context(|| "require_attrs_select: attrName contains null byte")?; let v2 = unsafe { - check_call!(raw::get_attr_byname[ + check_call!(raw::get_attr_byname( &mut self.context, v.raw_ptr(), self.eval_state.as_ptr(), attr_name.as_ptr() - ]) + )) }?; Ok(Value::new(v2)) } @@ -238,7 +241,11 @@ impl EvalState { let s = CString::new(s).with_context(|| "new_value_str: contains null byte")?; let v = unsafe { let value = self.new_value_uninitialized()?; - check_call!(raw::init_string[&mut self.context, value.raw_ptr(), s.as_ptr()])?; + check_call!(raw::init_string( + &mut self.context, + value.raw_ptr(), + s.as_ptr() + ))?; value }; Ok(v) @@ -247,7 +254,7 @@ impl EvalState { pub fn new_value_int(&mut self, i: Int) -> Result { let v = unsafe { let value = self.new_value_uninitialized()?; - check_call!(raw::init_int[&mut self.context, value.raw_ptr(), i])?; + check_call!(raw::init_int(&mut self.context, value.raw_ptr(), i))?; value }; Ok(v) @@ -257,12 +264,12 @@ impl EvalState { fn get_string(&mut self, value: &Value) -> Result { let mut r = result_string_init!(); unsafe { - check_call!(raw::get_string[ + check_call!(raw::get_string( &mut self.context, value.raw_ptr(), Some(callback_get_result_string), callback_get_result_string_data(&mut r) - ])?; + ))?; }; r } @@ -285,12 +292,12 @@ impl EvalState { } let rs = unsafe { - check_call!(raw::string_realise[ + check_call!(raw::string_realise( &mut self.context, self.eval_state.as_ptr(), value.raw_ptr(), is_import_from_derivation - ]) + )) }?; let s = unsafe { @@ -325,13 +332,13 @@ impl EvalState { pub fn call(&mut self, f: Value, a: Value) -> Result { let value = self.new_value_uninitialized()?; unsafe { - check_call!(raw::value_call[ + check_call!(raw::value_call( &mut self.context, self.eval_state.as_ptr(), f.raw_ptr(), a.raw_ptr(), value.raw_ptr() - ]) + )) }?; Ok(value) } @@ -342,19 +349,23 @@ impl EvalState { pub fn new_value_apply(&mut self, f: &Value, a: &Value) -> Result { let value = self.new_value_uninitialized()?; unsafe { - check_call!(raw::init_apply[ + check_call!(raw::init_apply( &mut self.context, value.raw_ptr(), f.raw_ptr(), a.raw_ptr() - ]) + )) }?; Ok(value) } fn new_value_uninitialized(&mut self) -> Result { - let value = - unsafe { check_call!(raw::alloc_value[&mut self.context, self.eval_state.as_ptr()]) }?; + let value = unsafe { + check_call!(raw::alloc_value( + &mut self.context, + self.eval_state.as_ptr() + )) + }?; Ok(Value::new(value)) } } diff --git a/rust/nix-expr/src/value.rs b/rust/nix-expr/src/value.rs index e7ff7c7..4fb9b62 100644 --- a/rust/nix-expr/src/value.rs +++ b/rust/nix-expr/src/value.rs @@ -77,7 +77,7 @@ impl Clone for Value { // this is very unlikely to error, and it is not recoverable // Maybe try without, and try again with context to report details? unsafe { - check_call!(raw::gc_incref[&mut Context::new(), self.inner.as_ptr()]).unwrap(); + check_call!(raw::gc_incref(&mut Context::new(), self.inner.as_ptr())).unwrap(); } // can't return an error here, but we don't want to ignore the error either as it means we could use-after-free Value { inner: self.inner } diff --git a/rust/nix-store/src/store.rs b/rust/nix-store/src/store.rs index d827153..af820a1 100644 --- a/rust/nix-store/src/store.rs +++ b/rust/nix-store/src/store.rs @@ -11,7 +11,7 @@ use std::ptr::NonNull; /* TODO make Nix itself thread safe */ lazy_static! { static ref INIT: Result<()> = unsafe { - check_call!(raw::libstore_init[&mut Context::new()])?; + check_call!(raw::libstore_init(&mut Context::new()))?; Ok(()) }; } @@ -74,7 +74,11 @@ impl Store { .collect(); let store = unsafe { - check_call!(raw::store_open[&mut context, uri_ptr.as_ptr(), params.as_mut_ptr()]) + check_call!(raw::store_open( + &mut context, + uri_ptr.as_ptr(), + params.as_mut_ptr() + )) }?; if store.is_null() { panic!("nix_c_store_open returned a null pointer without an error"); @@ -95,7 +99,12 @@ impl Store { pub fn get_uri(&mut self) -> Result { let mut r = result_string_init!(); unsafe { - check_call!(raw::store_get_uri[&mut self.context, self.inner.ptr(), Some(callback_get_result_string), callback_get_result_string_data(&mut r)]) + check_call!(raw::store_get_uri( + &mut self.context, + self.inner.ptr(), + Some(callback_get_result_string), + callback_get_result_string_data(&mut r) + )) }?; r } diff --git a/rust/nix-util/src/context.rs b/rust/nix-util/src/context.rs index e838cc6..1314360 100644 --- a/rust/nix-util/src/context.rs +++ b/rust/nix-util/src/context.rs @@ -85,10 +85,10 @@ impl Drop for Context { #[macro_export] macro_rules! check_call { - ($f:path[$ctx:expr $(, $arg:expr)*]) => { + ($($f:ident)::+($ctx:expr $(, $arg:expr)*)) => { { let ctx : &mut $crate::context::Context = $ctx; - let ret = $f(ctx.ptr() $(, $arg)*); + let ret = $($f)::*(ctx.ptr() $(, $arg)*); match ctx.check_err() { Ok(_) => Ok(ret), Err(e) => { @@ -148,7 +148,7 @@ mod tests { #[test] fn check_call_dynamic_context() { - let r = check_call!(set_dummy_err[&mut Context::new()]); + let r = check_call!(set_dummy_err(&mut Context::new())); assert!(r.is_err()); assert_eq!(r.unwrap_err().to_string(), "dummy error message"); } diff --git a/rust/nix-util/src/settings.rs b/rust/nix-util/src/settings.rs index 218e3b5..fc29810 100644 --- a/rust/nix-util/src/settings.rs +++ b/rust/nix-util/src/settings.rs @@ -11,7 +11,7 @@ pub fn set(key: &str, value: &str) -> Result<()> { let key = std::ffi::CString::new(key)?; let value = std::ffi::CString::new(value)?; unsafe { - check_call!(raw::setting_set[&mut ctx, key.as_ptr(), value.as_ptr()])?; + check_call!(raw::setting_set(&mut ctx, key.as_ptr(), value.as_ptr()))?; } Ok(()) } @@ -21,7 +21,12 @@ pub fn get(key: &str) -> Result { let key = std::ffi::CString::new(key)?; let mut r: Result = result_string_init!(); unsafe { - check_call!(raw::setting_get[&mut ctx, key.as_ptr(), Some(callback_get_result_string), callback_get_result_string_data(&mut r)])?; + check_call!(raw::setting_get( + &mut ctx, + key.as_ptr(), + Some(callback_get_result_string), + callback_get_result_string_data(&mut r) + ))?; } r } @@ -36,7 +41,7 @@ mod tests { fn setup() { let mut ctx = context::Context::new(); unsafe { - check_call!(raw::libstore_init[&mut ctx]).unwrap(); + check_call!(raw::libstore_init(&mut ctx)).unwrap(); } } From c235b3286880ac0b6f9c3908c89c4821676506a4 Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Tue, 2 Jul 2024 16:44:04 -0400 Subject: [PATCH 082/306] check_call_opt_key: [] -> () (cherry picked from commit 76966c24dcf6413a4ebdfe0ddba00e3d1d7e5fff) --- rust/nix-expr/src/eval_state.rs | 4 ++-- rust/nix-util/src/context.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index d99f50d..7ba139a 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -225,12 +225,12 @@ impl EvalState { let attr_name = CString::new(attr_name) .with_context(|| "require_attrs_select_opt: attrName contains null byte")?; let v2 = unsafe { - check_call_opt_key!(raw::get_attr_byname[ + check_call_opt_key!(raw::get_attr_byname( &mut self.context, v.raw_ptr(), self.eval_state.as_ptr(), attr_name.as_ptr() - ]) + )) }?; Ok(v2.map(Value::new)) } diff --git a/rust/nix-util/src/context.rs b/rust/nix-util/src/context.rs index 1314360..f5db84d 100644 --- a/rust/nix-util/src/context.rs +++ b/rust/nix-util/src/context.rs @@ -105,10 +105,10 @@ pub use check_call; // TODO: Generalize this macro to work with any error code or any error handling logic #[macro_export] macro_rules! check_call_opt_key { - ($f:path[$ctx:expr, $($arg:expr),*]) => { + ($($f:ident)::+($ctx:expr, $($arg:expr),*)) => { { let ctx : &mut $crate::context::Context = $ctx; - let ret = $f(ctx.ptr(), $($arg,)*); + let ret = $($f)::*(ctx.ptr(), $($arg,)*); if unsafe { raw::err_code(ctx.ptr()) == raw::NIX_ERR_KEY } { ctx.clear(); return Ok(None); From 6b98acb3823f48032225e7d29172992a51a445a0 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 23 Aug 2024 21:02:00 +0200 Subject: [PATCH 083/306] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nix': 'github:NixOS/nix/c90a763273f94ec5e82f21bfb2b359d04af86383?narHash=sha256-Lsd3e8xDtAfo/RgpzYgi1P2Wkp1DqTP3n8TI1BK1JPY%3D' (2024-05-24) → 'github:NixOS/nix/85f1aa6b3df5c5fcc924a74e2a9cc8acea9ba0e1?narHash=sha256-3%2BUgAktTtkGUNpxMxr%2Bq%2BR%2Bz3r026L3PwJzG6RD2IXM%3D' (2024-08-23) • Updated input 'nix/flake-compat': 'github:edolstra/flake-compat/35bb57c0c8d8b62bbfd284272c928ceb64ddbde9?narHash=sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm%2B504Ch3sNKLd8%3D' (2023-01-17) → 'github:edolstra/flake-compat/0f9255e01c2351cc7d116c072cb317785dd33b33?narHash=sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U%3D' (2023-10-04) • Updated input 'nix/flake-parts': 'github:hercules-ci/flake-parts/9126214d0a59633752a136528f5f3b9aa8565b7d?narHash=sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm%2BGpZNw%3D' (2024-04-01) → 'github:hercules-ci/flake-parts/9227223f6d922fee3c7b190b2cc238a99527bbb7?narHash=sha256-pQMhCCHyQGRzdfAkdJ4cIWiw%2BJNuWsTX7f0ZYSyz0VY%3D' (2024-07-03) • Added input 'nix/git-hooks-nix': 'github:cachix/git-hooks.nix/f451c19376071a90d8c58ab1a953c6e9840527fd?narHash=sha256-6FPUl7HVtvRHCCBQne7Ylp4p%2BdpP3P/OYuzjztZ4s70%3D' (2024-07-15) • Added input 'nix/git-hooks-nix/flake-compat': follows 'nix' • Added input 'nix/git-hooks-nix/gitignore': follows 'nix' • Added input 'nix/git-hooks-nix/nixpkgs': follows 'nix/nixpkgs' • Added input 'nix/git-hooks-nix/nixpkgs-stable': follows 'nix/nixpkgs' • Updated input 'nix/libgit2': 'github:libgit2/libgit2/45fd9ed7ae1a9b74b957ef4f337bc3c8b3df01b5?narHash=sha256-oX4Z3S9WtJlwvj0uH9HlYcWv%2Bx1hqp8mhXl7HsLu2f0%3D' (2023-10-18) → 'github:libgit2/libgit2/36f7e21ad757a3dacc58cf7944329da6bc1d6e96?narHash=sha256-J2rCxTecyLbbDdsyBWn9w7r3pbKRMkI9E7RvRgAqBdY%3D' (2024-05-16) • Added input 'nix/nixpkgs-23-11': 'github:NixOS/nixpkgs/a62e6edd6d5e1fa0329b8653c801147986f8d446?narHash=sha256-oamiKNfr2MS6yH64rUn99mIZjc45nGJlj9eGth/3Xuw%3D' (2024-05-31) • Removed input 'nix/pre-commit-hooks' • Removed input 'nix/pre-commit-hooks/flake-compat' • Removed input 'nix/pre-commit-hooks/flake-utils' • Removed input 'nix/pre-commit-hooks/flake-utils/systems' • Removed input 'nix/pre-commit-hooks/gitignore' • Removed input 'nix/pre-commit-hooks/nixpkgs' • Removed input 'nix/pre-commit-hooks/nixpkgs-stable' • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/faf912b086576fd1a15fca610166c98d47bc667e?narHash=sha256-ZbHsm%2BmGk/izkWtT4xwwqz38fdlwu7nUUKXTOmm4SyE%3D' (2024-02-05) → 'github:NixOS/nixpkgs/c374d94f1536013ca8e92341b540eba4c22f9c62?narHash=sha256-Z/ELQhrSd7bMzTO8r7NZgi9g5emh%2BaRKoCdaAv5fiO0%3D' (2024-08-21) (cherry picked from commit 76b1b22ca32d1540ee300836e632d5e4fa1f82ad) --- flake.lock | 150 ++++++++++++++++++++++++----------------------------- 1 file changed, 67 insertions(+), 83 deletions(-) diff --git a/flake.lock b/flake.lock index 70d82fa..911ffd7 100644 --- a/flake.lock +++ b/flake.lock @@ -43,11 +43,11 @@ "flake-compat": { "flake": false, "locked": { - "lastModified": 1673956053, - "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", "owner": "edolstra", "repo": "flake-compat", - "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", "type": "github" }, "original": { @@ -100,11 +100,11 @@ ] }, "locked": { - "lastModified": 1712014858, - "narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=", + "lastModified": 1719994518, + "narHash": "sha256-pQMhCCHyQGRzdfAkdJ4cIWiw+JNuWsTX7f0ZYSyz0VY=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "9126214d0a59633752a136528f5f3b9aa8565b7d", + "rev": "9227223f6d922fee3c7b190b2cc238a99527bbb7", "type": "github" }, "original": { @@ -117,24 +117,6 @@ "inputs": { "systems": "systems" }, - "locked": { - "lastModified": 1710146030, - "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "flake-utils_2": { - "inputs": { - "systems": "systems_2" - }, "locked": { "lastModified": 1701680307, "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", @@ -149,6 +131,37 @@ "type": "github" } }, + "git-hooks-nix": { + "inputs": { + "flake-compat": [ + "nix" + ], + "gitignore": [ + "nix" + ], + "nixpkgs": [ + "nix", + "nixpkgs" + ], + "nixpkgs-stable": [ + "nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1721042469, + "narHash": "sha256-6FPUl7HVtvRHCCBQne7Ylp4p+dpP3P/OYuzjztZ4s70=", + "owner": "cachix", + "repo": "git-hooks.nix", + "rev": "f451c19376071a90d8c58ab1a953c6e9840527fd", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "git-hooks.nix", + "type": "github" + } + }, "gitignore": { "inputs": { "nixpkgs": [ @@ -173,15 +186,16 @@ "libgit2": { "flake": false, "locked": { - "lastModified": 1697646580, - "narHash": "sha256-oX4Z3S9WtJlwvj0uH9HlYcWv+x1hqp8mhXl7HsLu2f0=", + "lastModified": 1715853528, + "narHash": "sha256-J2rCxTecyLbbDdsyBWn9w7r3pbKRMkI9E7RvRgAqBdY=", "owner": "libgit2", "repo": "libgit2", - "rev": "45fd9ed7ae1a9b74b957ef4f337bc3c8b3df01b5", + "rev": "36f7e21ad757a3dacc58cf7944329da6bc1d6e96", "type": "github" }, "original": { "owner": "libgit2", + "ref": "v1.8.1", "repo": "libgit2", "type": "github" } @@ -206,19 +220,20 @@ "inputs": { "flake-compat": "flake-compat", "flake-parts": "flake-parts_2", + "git-hooks-nix": "git-hooks-nix", "libgit2": "libgit2", "nixpkgs": [ "nixpkgs" ], - "nixpkgs-regression": "nixpkgs-regression", - "pre-commit-hooks": "pre-commit-hooks" + "nixpkgs-23-11": "nixpkgs-23-11", + "nixpkgs-regression": "nixpkgs-regression" }, "locked": { - "lastModified": 1716532322, - "narHash": "sha256-Lsd3e8xDtAfo/RgpzYgi1P2Wkp1DqTP3n8TI1BK1JPY=", + "lastModified": 1724439434, + "narHash": "sha256-3+UgAktTtkGUNpxMxr+q+R+z3r026L3PwJzG6RD2IXM=", "owner": "NixOS", "repo": "nix", - "rev": "c90a763273f94ec5e82f21bfb2b359d04af86383", + "rev": "85f1aa6b3df5c5fcc924a74e2a9cc8acea9ba0e1", "type": "github" }, "original": { @@ -255,11 +270,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1707092692, - "narHash": "sha256-ZbHsm+mGk/izkWtT4xwwqz38fdlwu7nUUKXTOmm4SyE=", + "lastModified": 1724224976, + "narHash": "sha256-Z/ELQhrSd7bMzTO8r7NZgi9g5emh+aRKoCdaAv5fiO0=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "faf912b086576fd1a15fca610166c98d47bc667e", + "rev": "c374d94f1536013ca8e92341b540eba4c22f9c62", "type": "github" }, "original": { @@ -269,6 +284,22 @@ "type": "github" } }, + "nixpkgs-23-11": { + "locked": { + "lastModified": 1717159533, + "narHash": "sha256-oamiKNfr2MS6yH64rUn99mIZjc45nGJlj9eGth/3Xuw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "a62e6edd6d5e1fa0329b8653c801147986f8d446", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "a62e6edd6d5e1fa0329b8653c801147986f8d446", + "type": "github" + } + }, "nixpkgs-regression": { "locked": { "lastModified": 1643052045, @@ -322,42 +353,10 @@ "type": "github" } }, - "pre-commit-hooks": { - "inputs": { - "flake-compat": [ - "nix" - ], - "flake-utils": "flake-utils", - "gitignore": [ - "nix" - ], - "nixpkgs": [ - "nix", - "nixpkgs" - ], - "nixpkgs-stable": [ - "nix", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1713775815, - "narHash": "sha256-Wu9cdYTnGQQwtT20QQMg7jzkANKQjwBD9iccfGKkfls=", - "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "rev": "2ac4dcbf55ed43f3be0bae15e181f08a57af24a4", - "type": "github" - }, - "original": { - "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "type": "github" - } - }, "pre-commit-hooks-nix": { "inputs": { "flake-compat": "flake-compat_2", - "flake-utils": "flake-utils_2", + "flake-utils": "flake-utils", "gitignore": "gitignore", "nixpkgs": [ "nixpkgs" @@ -481,21 +480,6 @@ "type": "github" } }, - "systems_2": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, "treefmt": { "inputs": { "nixpkgs": [ From 1ed50f009ae0314e923b98a95d84e6416d1dcd92 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 23 Aug 2024 21:12:00 +0200 Subject: [PATCH 084/306] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'flake-parts': 'github:hercules-ci/flake-parts/b253292d9c0a5ead9bc98c4e9a26c6312e27d69f?narHash=sha256-a0NYyp%2Bh9hlb7ddVz4LUn1vT/PLwqfrWYcHMvFB1xYg%3D' (2024-02-01) → 'github:hercules-ci/flake-parts/8471fe90ad337a8074e957b69ca4d0089218391d?narHash=sha256-XOQkdLafnb/p9ij77byFQjDf5m5QYl9b2REiVClC%2Bx4%3D' (2024-08-01) • Updated input 'nix-cargo-integration': 'github:yusdacra/nix-cargo-integration/eddee765f67966646c487f554fc843e8513aad28?narHash=sha256-VixqzHPc4VGd3TVcdTSufrpZ6wequ0a8BGSQOuxaFps%3D' (2024-02-08) → 'github:yusdacra/nix-cargo-integration/3a8e3bb661db28522aa2d4a55f1fccf9f95ec33e?narHash=sha256-fjwO6Pv3d35F6UErY42hc7zXJr6ek0LhSZlgEu%2BeI04%3D' (2024-08-23) • Updated input 'nix-cargo-integration/dream2nix': 'github:nix-community/dream2nix/936208ae7d88a178a0bcf7e6ac21bb6b87f6c8ea?narHash=sha256-tDmoPA3gukJPfjR/pKl6hNcRP7cuEGw6ruoguHS4szo%3D' (2024-02-06) → 'github:nix-community/dream2nix/3fd4c14d3683baac8d1f94286ae14fe160888b51?narHash=sha256-fFS8aDnfK9Qfm2FLnQ8pqWk8FzvFEv5LvTuZTZLREnc%3D' (2024-08-01) • Updated input 'nix-cargo-integration/parts': 'github:hercules-ci/flake-parts/b253292d9c0a5ead9bc98c4e9a26c6312e27d69f?narHash=sha256-a0NYyp%2Bh9hlb7ddVz4LUn1vT/PLwqfrWYcHMvFB1xYg%3D' (2024-02-01) → 'github:hercules-ci/flake-parts/8471fe90ad337a8074e957b69ca4d0089218391d?narHash=sha256-XOQkdLafnb/p9ij77byFQjDf5m5QYl9b2REiVClC%2Bx4%3D' (2024-08-01) • Updated input 'nix-cargo-integration/rust-overlay': 'github:oxalica/rust-overlay/dd917bb1b67fc049fd56fe6de70266a9ab74a4aa?narHash=sha256-Nuhi8KEJ2e%2B2nTimSyEIPqN5eh7ECVWd%2BAnPXG6L%2BSY%3D' (2024-02-08) → 'github:oxalica/rust-overlay/a18034322c7703fcfe5d7352a77981ba4a936a61?narHash=sha256-%2BCFDh1FUgyY7q0FiWhKJpHS7LlD3KbiqN5Z4Z%2B4bGmc%3D' (2024-08-23) • Updated input 'nix-cargo-integration/treefmt': 'github:numtide/treefmt-nix/ac599dab59a66304eb511af07b3883114f061b9d?narHash=sha256-qQF0fEkHlnxHcrKIMRzOETnRBksUK048MXkX0SOmxvA%3D' (2024-02-07) → 'github:numtide/treefmt-nix/070f834771efa715f3e74cd8ab93ecc96fabc951?narHash=sha256-kKJtaiU5Ou%2Be/0Qs7SICXF22DLx4V/WhG1P6%2Bk4yeOE%3D' (2024-08-22) • Updated input 'pre-commit-hooks-nix': 'github:cachix/pre-commit-hooks.nix/e611897ddfdde3ed3eaac4758635d7177ff78673?narHash=sha256-6hOpUiuxuwpXXc/xfJsBUJeqqgGI%2BJMJuLo45aG3cKc%3D' (2024-03-20) → 'github:cachix/pre-commit-hooks.nix/6cedaa7c1b4f82a266e5d30f212273e60d62cb0d?narHash=sha256-TuSaYdhOxeaaE9885mFO1lZHHax33GD5A9dczJrGUjw%3D' (2024-08-21) • Removed input 'pre-commit-hooks-nix/flake-utils' • Removed input 'pre-commit-hooks-nix/flake-utils/systems' • Updated input 'pre-commit-hooks-nix/gitignore': 'github:hercules-ci/gitignore.nix/43e1aa1308018f37118e34d3a9cb4f5e75dc11d5?narHash=sha256-gGPa9qWNc6eCXT/%2BZ5/zMkyYOuRZqeFZBDbopNZQkuY%3D' (2023-12-29) → 'github:hercules-ci/gitignore.nix/637db329424fd7e46cf4185293b9cc8c88c95394?narHash=sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs%3D' (2024-02-28) • Updated input 'pre-commit-hooks-nix/nixpkgs-stable': 'github:NixOS/nixpkgs/3dc440faeee9e889fe2d1b4d25ad0f430d449356?narHash=sha256-YWuCrtsty5vVZvu%2B7BchAxmcYzTMfolSPP5io8%2BWYCg%3D' (2024-01-10) → 'github:NixOS/nixpkgs/194846768975b7ad2c4988bdb82572c00222c0d7?narHash=sha256-NGKVY4PjzwAa4upkGtAMz1npHGoRzWotlSnVlqI40mo%3D' (2024-07-07) (cherry picked from commit 9621053e6c5af58af8ba5cc9ead42fe987f298b0) --- flake.lock | 90 +++++++++++++++++------------------------------------- 1 file changed, 28 insertions(+), 62 deletions(-) diff --git a/flake.lock b/flake.lock index 911ffd7..40ec8c3 100644 --- a/flake.lock +++ b/flake.lock @@ -27,11 +27,11 @@ "pyproject-nix": "pyproject-nix" }, "locked": { - "lastModified": 1707223475, - "narHash": "sha256-tDmoPA3gukJPfjR/pKl6hNcRP7cuEGw6ruoguHS4szo=", + "lastModified": 1722526955, + "narHash": "sha256-fFS8aDnfK9Qfm2FLnQ8pqWk8FzvFEv5LvTuZTZLREnc=", "owner": "nix-community", "repo": "dream2nix", - "rev": "936208ae7d88a178a0bcf7e6ac21bb6b87f6c8ea", + "rev": "3fd4c14d3683baac8d1f94286ae14fe160888b51", "type": "github" }, "original": { @@ -79,11 +79,11 @@ ] }, "locked": { - "lastModified": 1706830856, - "narHash": "sha256-a0NYyp+h9hlb7ddVz4LUn1vT/PLwqfrWYcHMvFB1xYg=", + "lastModified": 1722555600, + "narHash": "sha256-XOQkdLafnb/p9ij77byFQjDf5m5QYl9b2REiVClC+x4=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "b253292d9c0a5ead9bc98c4e9a26c6312e27d69f", + "rev": "8471fe90ad337a8074e957b69ca4d0089218391d", "type": "github" }, "original": { @@ -113,24 +113,6 @@ "type": "github" } }, - "flake-utils": { - "inputs": { - "systems": "systems" - }, - "locked": { - "lastModified": 1701680307, - "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, "git-hooks-nix": { "inputs": { "flake-compat": [ @@ -170,11 +152,11 @@ ] }, "locked": { - "lastModified": 1703887061, - "narHash": "sha256-gGPa9qWNc6eCXT/+Z5/zMkyYOuRZqeFZBDbopNZQkuY=", + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", "owner": "hercules-ci", "repo": "gitignore.nix", - "rev": "43e1aa1308018f37118e34d3a9cb4f5e75dc11d5", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", "type": "github" }, "original": { @@ -255,11 +237,11 @@ "treefmt": "treefmt" }, "locked": { - "lastModified": 1707372711, - "narHash": "sha256-VixqzHPc4VGd3TVcdTSufrpZ6wequ0a8BGSQOuxaFps=", + "lastModified": 1724393640, + "narHash": "sha256-fjwO6Pv3d35F6UErY42hc7zXJr6ek0LhSZlgEu+eI04=", "owner": "yusdacra", "repo": "nix-cargo-integration", - "rev": "eddee765f67966646c487f554fc843e8513aad28", + "rev": "3a8e3bb661db28522aa2d4a55f1fccf9f95ec33e", "type": "github" }, "original": { @@ -318,16 +300,16 @@ }, "nixpkgs-stable": { "locked": { - "lastModified": 1704874635, - "narHash": "sha256-YWuCrtsty5vVZvu+7BchAxmcYzTMfolSPP5io8+WYCg=", + "lastModified": 1720386169, + "narHash": "sha256-NGKVY4PjzwAa4upkGtAMz1npHGoRzWotlSnVlqI40mo=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "3dc440faeee9e889fe2d1b4d25ad0f430d449356", + "rev": "194846768975b7ad2c4988bdb82572c00222c0d7", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-23.11", + "ref": "nixos-24.05", "repo": "nixpkgs", "type": "github" } @@ -340,11 +322,11 @@ ] }, "locked": { - "lastModified": 1706830856, - "narHash": "sha256-a0NYyp+h9hlb7ddVz4LUn1vT/PLwqfrWYcHMvFB1xYg=", + "lastModified": 1722555600, + "narHash": "sha256-XOQkdLafnb/p9ij77byFQjDf5m5QYl9b2REiVClC+x4=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "b253292d9c0a5ead9bc98c4e9a26c6312e27d69f", + "rev": "8471fe90ad337a8074e957b69ca4d0089218391d", "type": "github" }, "original": { @@ -356,7 +338,6 @@ "pre-commit-hooks-nix": { "inputs": { "flake-compat": "flake-compat_2", - "flake-utils": "flake-utils", "gitignore": "gitignore", "nixpkgs": [ "nixpkgs" @@ -364,11 +345,11 @@ "nixpkgs-stable": "nixpkgs-stable" }, "locked": { - "lastModified": 1710923068, - "narHash": "sha256-6hOpUiuxuwpXXc/xfJsBUJeqqgGI+JMJuLo45aG3cKc=", + "lastModified": 1724227338, + "narHash": "sha256-TuSaYdhOxeaaE9885mFO1lZHHax33GD5A9dczJrGUjw=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "e611897ddfdde3ed3eaac4758635d7177ff78673", + "rev": "6cedaa7c1b4f82a266e5d30f212273e60d62cb0d", "type": "github" }, "original": { @@ -429,11 +410,11 @@ "rust-overlay": { "flake": false, "locked": { - "lastModified": 1707358215, - "narHash": "sha256-Nuhi8KEJ2e+2nTimSyEIPqN5eh7ECVWd+AnPXG6L+SY=", + "lastModified": 1724379657, + "narHash": "sha256-+CFDh1FUgyY7q0FiWhKJpHS7LlD3KbiqN5Z4Z+4bGmc=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "dd917bb1b67fc049fd56fe6de70266a9ab74a4aa", + "rev": "a18034322c7703fcfe5d7352a77981ba4a936a61", "type": "github" }, "original": { @@ -465,21 +446,6 @@ "type": "github" } }, - "systems": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, "treefmt": { "inputs": { "nixpkgs": [ @@ -488,11 +454,11 @@ ] }, "locked": { - "lastModified": 1707300477, - "narHash": "sha256-qQF0fEkHlnxHcrKIMRzOETnRBksUK048MXkX0SOmxvA=", + "lastModified": 1724338379, + "narHash": "sha256-kKJtaiU5Ou+e/0Qs7SICXF22DLx4V/WhG1P6+k4yeOE=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "ac599dab59a66304eb511af07b3883114f061b9d", + "rev": "070f834771efa715f3e74cd8ab93ecc96fabc951", "type": "github" }, "original": { From cf6bedde14fddeca96502143f5b00504d8678c7f Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 23 Aug 2024 21:12:56 +0200 Subject: [PATCH 085/306] flake.nix: Unfollow nixpkgs in flake-parts This may speed up autocompletion since the default lib is a small path. (It shouldn't matter, but Nix isn't as lazy as it will be yet, so here we go.) (cherry picked from commit 90709ff3c690275dfb6b8fce50cb0282a52e1971) --- flake.lock | 16 +++++++++++++--- flake.nix | 1 - 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/flake.lock b/flake.lock index 40ec8c3..f95a706 100644 --- a/flake.lock +++ b/flake.lock @@ -74,9 +74,7 @@ }, "flake-parts": { "inputs": { - "nixpkgs-lib": [ - "nixpkgs" - ] + "nixpkgs-lib": "nixpkgs-lib" }, "locked": { "lastModified": 1722555600, @@ -282,6 +280,18 @@ "type": "github" } }, + "nixpkgs-lib": { + "locked": { + "lastModified": 1724440177, + "narHash": "sha256-uFf2QeW7eAHlYXuDktm9c25OxOyCoUOQmh5SZ9amE5Q=", + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/a5d394176e64ab29c852d03346c1fc9b0b7d33eb.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/a5d394176e64ab29c852d03346c1fc9b0b7d33eb.tar.gz" + } + }, "nixpkgs-regression": { "locked": { "lastModified": 1643052045, diff --git a/flake.nix b/flake.nix index ea9ae1f..ff3c267 100644 --- a/flake.nix +++ b/flake.nix @@ -3,7 +3,6 @@ inputs = { flake-parts.url = "github:hercules-ci/flake-parts"; - flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs"; nix.url = "github:NixOS/nix"; nix.inputs.nixpkgs.follows = "nixpkgs"; nix-cargo-integration.url = "github:yusdacra/nix-cargo-integration"; From ee1d591e1a6ffcc792a1431edb458d1440dbf24d Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 29 Aug 2024 15:52:16 +0200 Subject: [PATCH 086/306] fix: Update for more recent Nix API (cherry picked from commit 55355f03ab86dfa15972681d9a51d333739f6d10) --- rust/nix-expr/src/value.rs | 4 ++-- rust/nix-util/src/context.rs | 10 +++++----- rust/nix-util/src/string_return.rs | 9 +++++++++ 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/rust/nix-expr/src/value.rs b/rust/nix-expr/src/value.rs index 4fb9b62..77ef048 100644 --- a/rust/nix-expr/src/value.rs +++ b/rust/nix-expr/src/value.rs @@ -67,7 +67,7 @@ impl Drop for Value { fn drop(&mut self) { unsafe { // ignoring error because the only failure mode is leaking memory - raw::gc_decref(null_mut(), self.inner.as_ptr()); + raw::value_decref(null_mut(), self.inner.as_ptr()); } } } @@ -77,7 +77,7 @@ impl Clone for Value { // this is very unlikely to error, and it is not recoverable // Maybe try without, and try again with context to report details? unsafe { - check_call!(raw::gc_incref(&mut Context::new(), self.inner.as_ptr())).unwrap(); + check_call!(raw::value_incref(&mut Context::new(), self.inner.as_ptr())).unwrap(); } // can't return an error here, but we don't want to ignore the error either as it means we could use-after-free Value { inner: self.inner } diff --git a/rust/nix-util/src/context.rs b/rust/nix-util/src/context.rs index f5db84d..55f0ab4 100644 --- a/rust/nix-util/src/context.rs +++ b/rust/nix-util/src/context.rs @@ -33,7 +33,7 @@ impl Context { /// We recommend to use `check_call!` if possible. pub fn check_err(&self) -> Result<()> { let err = unsafe { raw::err_code(self.inner.as_ptr()) }; - if err != raw::NIX_OK.try_into().unwrap() { + if err != raw::err_NIX_OK.try_into().unwrap() { // msgp is a borrowed pointer (pointing into the context), so we don't need to free it let msgp = unsafe { raw::err_msg(null_mut(), self.inner.as_ptr(), null_mut()) }; // Turn the i8 pointer into a Rust string by copying @@ -47,7 +47,7 @@ impl Context { unsafe { raw::set_err_msg( self.inner.as_ptr(), - raw::NIX_OK.try_into().unwrap(), + raw::err_NIX_OK.try_into().unwrap(), b"\0".as_ptr() as *const i8, ); } @@ -66,7 +66,7 @@ impl Context { f: F, ) -> Result> { let t = f(self.ptr()); - if unsafe { raw::err_code(self.inner.as_ptr()) == raw::NIX_ERR_KEY } { + if unsafe { raw::err_code(self.inner.as_ptr()) == raw::err_NIX_ERR_KEY } { self.clear(); return Ok(None); } @@ -109,7 +109,7 @@ macro_rules! check_call_opt_key { { let ctx : &mut $crate::context::Context = $ctx; let ret = $($f)::*(ctx.ptr(), $($arg,)*); - if unsafe { raw::err_code(ctx.ptr()) == raw::NIX_ERR_KEY } { + if unsafe { raw::err_code(ctx.ptr()) == raw::err_NIX_ERR_KEY } { ctx.clear(); return Ok(None); } @@ -140,7 +140,7 @@ mod tests { unsafe { raw::set_err_msg( ctx_ptr, - raw::NIX_ERR_UNKNOWN.try_into().unwrap(), + raw::err_NIX_ERR_UNKNOWN.try_into().unwrap(), b"dummy error message\0".as_ptr() as *const i8, ); } diff --git a/rust/nix-util/src/string_return.rs b/rust/nix-util/src/string_return.rs index 004f1ae..f06fa32 100644 --- a/rust/nix-util/src/string_return.rs +++ b/rust/nix-util/src/string_return.rs @@ -11,6 +11,15 @@ pub unsafe extern "C" fn callback_get_result_string( user_data: *mut std::os::raw::c_void, ) { let ret = user_data as *mut Result; + + if start == std::ptr::null() { + if n != 0 { + panic!("callback_get_result_string: start is null but n is not zero"); + } + *ret = Ok(String::new()); + return; + } + let slice = std::slice::from_raw_parts(start as *const u8, n as usize); if !(*ret).is_err() { From 271cf09450749a1bceb14b87a9790f89f72358fd Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 30 Aug 2024 15:38:55 +0200 Subject: [PATCH 087/306] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'flake-parts': 'github:hercules-ci/flake-parts/8471fe90ad337a8074e957b69ca4d0089218391d?narHash=sha256-XOQkdLafnb/p9ij77byFQjDf5m5QYl9b2REiVClC%2Bx4%3D' (2024-08-01) → 'github:hercules-ci/flake-parts/af510d4a62d071ea13925ce41c95e3dec816c01d?narHash=sha256-ODYRm8zHfLTH3soTFWE452ydPYz2iTvr9T8ftDMUQ3E%3D' (2024-08-30) • Updated input 'flake-parts/nixpkgs-lib': 'https://github.com/NixOS/nixpkgs/archive/a5d394176e64ab29c852d03346c1fc9b0b7d33eb.tar.gz?narHash=sha256-uFf2QeW7eAHlYXuDktm9c25OxOyCoUOQmh5SZ9amE5Q%3D' (2024-08-23) → 'https://github.com/NixOS/nixpkgs/archive/a5d394176e64ab29c852d03346c1fc9b0b7d33eb.tar.gz?narHash=sha256-uFf2QeW7eAHlYXuDktm9c25OxOyCoUOQmh5SZ9amE5Q%3D' (2024-08-01) (cherry picked from commit 07998ef498cabbfd3c18b223da0f38f3d82a7e9a) --- flake.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/flake.lock b/flake.lock index f95a706..dac7ea2 100644 --- a/flake.lock +++ b/flake.lock @@ -77,11 +77,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1722555600, - "narHash": "sha256-XOQkdLafnb/p9ij77byFQjDf5m5QYl9b2REiVClC+x4=", + "lastModified": 1725024810, + "narHash": "sha256-ODYRm8zHfLTH3soTFWE452ydPYz2iTvr9T8ftDMUQ3E=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "8471fe90ad337a8074e957b69ca4d0089218391d", + "rev": "af510d4a62d071ea13925ce41c95e3dec816c01d", "type": "github" }, "original": { @@ -282,7 +282,7 @@ }, "nixpkgs-lib": { "locked": { - "lastModified": 1724440177, + "lastModified": 1722555339, "narHash": "sha256-uFf2QeW7eAHlYXuDktm9c25OxOyCoUOQmh5SZ9amE5Q=", "type": "tarball", "url": "https://github.com/NixOS/nixpkgs/archive/a5d394176e64ab29c852d03346c1fc9b0b7d33eb.tar.gz" From 7918175c246840e7aa98360c87f03092e32c94d2 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 29 Aug 2024 16:58:18 +0200 Subject: [PATCH 088/306] refact: Move dev-only dependencies into separate lock file (cherry picked from commit ddf306bb9564a43fb42ab4321b390e766254f1c4) --- .claude/settings.local.json | 12 ++++++ dev/flake-module.nix | 74 ++++++++++++++++++++++++++++++++ dev/flake.lock | 85 +++++++++++++++++++++++++++++++++++++ dev/flake.nix | 8 ++++ flake.lock | 79 +--------------------------------- flake.nix | 83 ++++-------------------------------- 6 files changed, 188 insertions(+), 153 deletions(-) create mode 100644 .claude/settings.local.json create mode 100644 dev/flake-module.nix create mode 100644 dev/flake.lock create mode 100644 dev/flake.nix diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..1b694a3 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://json.schemastore.org/claude-code-settings.json", + "permissions": { + "allow": [ + "Bash(nix flake check:*)", + "Bash(git grep:*)", + "Bash(git cherry-pick:*)", + "Bash(sed:*)" + ], + "deny": [] + } +} \ No newline at end of file diff --git a/dev/flake-module.nix b/dev/flake-module.nix new file mode 100644 index 0000000..8869c87 --- /dev/null +++ b/dev/flake-module.nix @@ -0,0 +1,74 @@ +{ inputs, ... }: { + imports = [ inputs.pre-commit-hooks-nix.flakeModule ]; + perSystem = { config, pkgs, ... }: { + + pre-commit.settings.hooks.nixpkgs-fmt.enable = true; + # Temporarily disable rustfmt due to configuration issues + # pre-commit.settings.hooks.rustfmt.enable = true; + pre-commit.settings.settings.rust.cargoManifestPath = "./rust/Cargo.toml"; + + # Check that we're using ///-style doc comments in Rust code. + # + # Unfortunately, rustfmt won't do this for us yet - at least not + # without nightly, and it might do too much. + pre-commit.settings.hooks.rust-doc-comments = { + enable = true; + files = "\\.rs$"; + entry = "${pkgs.writeScript "rust-doc-comments" '' + #!${pkgs.runtimeShell} + set -uxo pipefail + grep -n -C3 --color=always -F '/**' "$@" + r=$? + set -e + if [ $r -eq 0 ]; then + echo "Please replace /**-style comments by /// style comments in Rust code." + exit 1 + fi + ''}"; + }; + + devShells.default = pkgs.mkShell { + name = "nix-bindings-devshell"; + strictDeps = true; + inputsFrom = [ config.nci.outputs.nix-bindings.devShell ]; + inherit (config.nci.outputs.nix-bindings.devShell.env) + LIBCLANG_PATH + BINDGEN_EXTRA_CLANG_ARGS + ; + NIX_DEBUG_INFO_DIRS = + let + # TODO: add to Nixpkgs lib + getDebug = pkg: + if pkg?debug then pkg.debug + else if pkg?lib then pkg.lib + else pkg; + in + "${getDebug config.packages.nix}/lib/debug"; + buildInputs = [ + config.packages.nix + ]; + nativeBuildInputs = [ + pkgs.rust-analyzer + pkgs.nixpkgs-fmt + pkgs.rustfmt + pkgs.pkg-config + pkgs.clang-tools # clangd + pkgs.valgrind + pkgs.gdb + # TODO: set up cargo-valgrind in shell and build + # currently both this and `cargo install cargo-valgrind` + # produce a binary that says ENOENT. + # pkgs.cargo-valgrind + ]; + shellHook = '' + ${config.pre-commit.installationScript} + echo 1>&2 "Welcome to the development shell!" + ''; + # rust-analyzer needs a NIX_PATH for some reason + NIX_PATH = "nixpkgs=${inputs.nixpkgs}"; + }; + }; + flake = { + herculesCI.ciSystems = [ "x86_64-linux" ]; + }; +} diff --git a/dev/flake.lock b/dev/flake.lock new file mode 100644 index 0000000..3647d2e --- /dev/null +++ b/dev/flake.lock @@ -0,0 +1,85 @@ +{ + "nodes": { + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "pre-commit-hooks-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1720386169, + "narHash": "sha256-NGKVY4PjzwAa4upkGtAMz1npHGoRzWotlSnVlqI40mo=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "194846768975b7ad2c4988bdb82572c00222c0d7", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-24.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "pre-commit-hooks-nix": { + "inputs": { + "flake-compat": "flake-compat", + "gitignore": "gitignore", + "nixpkgs": [], + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1724857454, + "narHash": "sha256-Qyl9Q4QMTLZnnBb/8OuQ9LSkzWjBU1T5l5zIzTxkkhk=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "4509ca64f1084e73bc7a721b20c669a8d4c5ebe6", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, + "root": { + "inputs": { + "pre-commit-hooks-nix": "pre-commit-hooks-nix" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/dev/flake.nix b/dev/flake.nix new file mode 100644 index 0000000..ff27954 --- /dev/null +++ b/dev/flake.nix @@ -0,0 +1,8 @@ +{ + description = "dependencies only"; + inputs = { + pre-commit-hooks-nix.url = "github:cachix/pre-commit-hooks.nix"; + pre-commit-hooks-nix.inputs.nixpkgs.follows = ""; + }; + outputs = { ... }: { }; +} diff --git a/flake.lock b/flake.lock index dac7ea2..737d9dd 100644 --- a/flake.lock +++ b/flake.lock @@ -56,22 +56,6 @@ "type": "github" } }, - "flake-compat_2": { - "flake": false, - "locked": { - "lastModified": 1696426674, - "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", - "owner": "edolstra", - "repo": "flake-compat", - "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", - "type": "github" - }, - "original": { - "owner": "edolstra", - "repo": "flake-compat", - "type": "github" - } - }, "flake-parts": { "inputs": { "nixpkgs-lib": "nixpkgs-lib" @@ -142,27 +126,6 @@ "type": "github" } }, - "gitignore": { - "inputs": { - "nixpkgs": [ - "pre-commit-hooks-nix", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1709087332, - "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", - "owner": "hercules-ci", - "repo": "gitignore.nix", - "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "gitignore.nix", - "type": "github" - } - }, "libgit2": { "flake": false, "locked": { @@ -308,22 +271,6 @@ "type": "github" } }, - "nixpkgs-stable": { - "locked": { - "lastModified": 1720386169, - "narHash": "sha256-NGKVY4PjzwAa4upkGtAMz1npHGoRzWotlSnVlqI40mo=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "194846768975b7ad2c4988bdb82572c00222c0d7", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixos-24.05", - "repo": "nixpkgs", - "type": "github" - } - }, "parts": { "inputs": { "nixpkgs-lib": [ @@ -345,29 +292,6 @@ "type": "github" } }, - "pre-commit-hooks-nix": { - "inputs": { - "flake-compat": "flake-compat_2", - "gitignore": "gitignore", - "nixpkgs": [ - "nixpkgs" - ], - "nixpkgs-stable": "nixpkgs-stable" - }, - "locked": { - "lastModified": 1724227338, - "narHash": "sha256-TuSaYdhOxeaaE9885mFO1lZHHax33GD5A9dczJrGUjw=", - "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "rev": "6cedaa7c1b4f82a266e5d30f212273e60d62cb0d", - "type": "github" - }, - "original": { - "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "type": "github" - } - }, "purescript-overlay": { "inputs": { "nixpkgs": [ @@ -413,8 +337,7 @@ "flake-parts": "flake-parts", "nix": "nix", "nix-cargo-integration": "nix-cargo-integration", - "nixpkgs": "nixpkgs", - "pre-commit-hooks-nix": "pre-commit-hooks-nix" + "nixpkgs": "nixpkgs" } }, "rust-overlay": { diff --git a/flake.nix b/flake.nix index ff3c267..c67d7f1 100644 --- a/flake.nix +++ b/flake.nix @@ -8,8 +8,6 @@ nix-cargo-integration.url = "github:yusdacra/nix-cargo-integration"; nix-cargo-integration.inputs.nixpkgs.follows = "nixpkgs"; nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; - pre-commit-hooks-nix.url = "github:cachix/pre-commit-hooks.nix"; - pre-commit-hooks-nix.inputs.nixpkgs.follows = "nixpkgs"; }; outputs = inputs@{ self, flake-parts, ... }: @@ -17,86 +15,21 @@ { inherit inputs; } ({ lib, ... }: { imports = [ - inputs.pre-commit-hooks-nix.flakeModule inputs.nix-cargo-integration.flakeModule + inputs.flake-parts.flakeModules.partitions ./rust/nci.nix ]; systems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ]; perSystem = { config, self', inputs', pkgs, ... }: { - - - packages.nix = inputs'.nix.packages.nix; - - pre-commit.settings.hooks.nixpkgs-fmt.enable = true; - # Temporarily disable rustfmt due to configuration issues - # pre-commit.settings.hooks.rustfmt.enable = true; - # New configuration for rustfmt - pre-commit.settings.settings.rust.cargoManifestPath = "./rust/Cargo.toml"; - - # Check that we're using ///-style doc comments in Rust code. - # - # Unfortunately, rustfmt won't do this for us yet - at least not - # without nightly, and it might do too much. - pre-commit.settings.hooks.rust-doc-comments = { - enable = true; - files = "\\.rs$"; - entry = "${pkgs.writeScript "rust-doc-comments" '' - #!${pkgs.runtimeShell} - set -uxo pipefail - grep -n -C3 --color=always -F '/**' "$@" - r=$? - set -e - if [ $r -eq 0 ]; then - echo "Please replace /**-style comments by /// style comments in Rust code." - exit 1 - fi - ''}"; - }; - - devShells.default = pkgs.mkShell { - name = "nix-bindings-devshell"; - strictDeps = true; - inputsFrom = [ config.nci.outputs.nix-bindings.devShell ]; - inherit (config.nci.outputs.nix-bindings.devShell.env) - LIBCLANG_PATH - BINDGEN_EXTRA_CLANG_ARGS - ; - NIX_DEBUG_INFO_DIRS = - let - # TODO: add to Nixpkgs lib - getDebug = pkg: - if pkg?debug then pkg.debug - else if pkg?lib then pkg.lib - else pkg; - in - "${getDebug config.packages.nix}/lib/debug"; - buildInputs = [ - config.packages.nix - ]; - nativeBuildInputs = [ - pkgs.rust-analyzer - pkgs.nixpkgs-fmt - pkgs.rustfmt - pkgs.pkg-config - pkgs.clang-tools # clangd - pkgs.valgrind - pkgs.gdb - # TODO: set up cargo-valgrind in shell and build - # currently both this and `cargo install cargo-valgrind` - # produce a binary that says ENOENT. - # pkgs.cargo-valgrind - ]; - shellHook = '' - ${config.pre-commit.installationScript} - echo 1>&2 "Welcome to the development shell!" - ''; - # rust-analyzer needs a NIX_PATH for some reason - NIX_PATH = "nixpkgs=${inputs.nixpkgs}"; - }; }; - flake = { - herculesCI.ciSystems = [ "x86_64-linux" ]; + + partitionedAttrs.devShells = "dev"; + partitionedAttrs.checks = "dev"; + partitionedAttrs.herculesCI = "dev"; + partitions.dev.extraInputsFlake = ./dev; + partitions.dev.module = { + imports = [ ./dev/flake-module.nix ]; }; }); } From bd8ebcbb013d01ddb0c686b93d6c29cb54238828 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 29 Aug 2024 17:13:47 +0200 Subject: [PATCH 089/306] dev: Add hercules-ci-effects dev input (cherry picked from commit 85f10d0410a061f151722042924afc8523869aa7) --- dev/flake-module.nix | 13 ++++++---- dev/flake.lock | 56 ++++++++++++++++++++++++++++++++++++++++++++ dev/flake.nix | 1 + 3 files changed, 66 insertions(+), 4 deletions(-) diff --git a/dev/flake-module.nix b/dev/flake-module.nix index 8869c87..1f07ecb 100644 --- a/dev/flake-module.nix +++ b/dev/flake-module.nix @@ -1,5 +1,8 @@ -{ inputs, ... }: { - imports = [ inputs.pre-commit-hooks-nix.flakeModule ]; +{ lib, inputs, withSystem, ... }: { + imports = [ + inputs.pre-commit-hooks-nix.flakeModule + inputs.hercules-ci-effects.flakeModule + ]; perSystem = { config, pkgs, ... }: { pre-commit.settings.hooks.nixpkgs-fmt.enable = true; @@ -55,6 +58,7 @@ pkgs.clang-tools # clangd pkgs.valgrind pkgs.gdb + pkgs.hci # TODO: set up cargo-valgrind in shell and build # currently both this and `cargo install cargo-valgrind` # produce a binary that says ENOENT. @@ -68,7 +72,8 @@ NIX_PATH = "nixpkgs=${inputs.nixpkgs}"; }; }; - flake = { - herculesCI.ciSystems = [ "x86_64-linux" ]; + herculesCI = hci@{ config, ... }: { + ciSystems = [ "x86_64-linux" ]; }; + flake = { }; } diff --git a/dev/flake.lock b/dev/flake.lock index 3647d2e..f8b1bc3 100644 --- a/dev/flake.lock +++ b/dev/flake.lock @@ -16,6 +16,26 @@ "type": "github" } }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": [ + "hercules-ci-effects", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1712014858, + "narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "9126214d0a59633752a136528f5f3b9aa8565b7d", + "type": "github" + }, + "original": { + "id": "flake-parts", + "type": "indirect" + } + }, "gitignore": { "inputs": { "nixpkgs": [ @@ -37,6 +57,41 @@ "type": "github" } }, + "hercules-ci-effects": { + "inputs": { + "flake-parts": "flake-parts", + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1719226092, + "narHash": "sha256-YNkUMcCUCpnULp40g+svYsaH1RbSEj6s4WdZY/SHe38=", + "owner": "hercules-ci", + "repo": "hercules-ci-effects", + "rev": "11e4b8dc112e2f485d7c97e1cee77f9958f498f5", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "hercules-ci-effects", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1713714899, + "narHash": "sha256-+z/XjO3QJs5rLE5UOf015gdVauVRQd2vZtsFkaXBq2Y=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "6143fc5eeb9c4f00163267708e26191d1e918932", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, "nixpkgs-stable": { "locked": { "lastModified": 1720386169, @@ -76,6 +131,7 @@ }, "root": { "inputs": { + "hercules-ci-effects": "hercules-ci-effects", "pre-commit-hooks-nix": "pre-commit-hooks-nix" } } diff --git a/dev/flake.nix b/dev/flake.nix index ff27954..062e27a 100644 --- a/dev/flake.nix +++ b/dev/flake.nix @@ -3,6 +3,7 @@ inputs = { pre-commit-hooks-nix.url = "github:cachix/pre-commit-hooks.nix"; pre-commit-hooks-nix.inputs.nixpkgs.follows = ""; + hercules-ci-effects.url = "github:hercules-ci/hercules-ci-effects"; }; outputs = { ... }: { }; } From 590a748e822def0e605d6a8768f114d6ad61aee7 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 23 Aug 2024 19:41:17 +0200 Subject: [PATCH 090/306] maint: Update nixpkgs was: feat: Empty manual (cherry picked from commit a7de1171105d4609b500e69d42fa1ade5cd6f1f1) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 737d9dd..bb36bb7 100644 --- a/flake.lock +++ b/flake.lock @@ -213,11 +213,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1724224976, - "narHash": "sha256-Z/ELQhrSd7bMzTO8r7NZgi9g5emh+aRKoCdaAv5fiO0=", + "lastModified": 1724819573, + "narHash": "sha256-GnR7/ibgIH1vhoy8cYdmXE6iyZqKqFxQSVkFgosBh6w=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "c374d94f1536013ca8e92341b540eba4c22f9c62", + "rev": "71e91c409d1e654808b2621f28a327acfdad8dc2", "type": "github" }, "original": { From 9cebf1d13114a1e00f5e5716ae7efc02e33fd9c7 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 24 May 2024 09:09:41 +0200 Subject: [PATCH 091/306] feat: Value::new_borrowed (cherry picked from commit 86e3828250d4de15f9cf49d19222eb382c77f7e2) --- rust/nix-expr/src/value.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/rust/nix-expr/src/value.rs b/rust/nix-expr/src/value.rs index 77ef048..e15d5b9 100644 --- a/rust/nix-expr/src/value.rs +++ b/rust/nix-expr/src/value.rs @@ -54,11 +54,24 @@ pub struct Value { inner: NonNull, } impl Value { + /// Take ownership of a new Value. + /// + /// This does not call `nix_gc_incref`, but does call `nix_gc_decref` when dropped. pub(crate) fn new(inner: *mut raw::Value) -> Self { Value { inner: NonNull::new(inner).unwrap(), } } + + /// Borrow a reference to a Value. + /// + /// This calls `nix_gc_incref`, and the returned Value will call `nix_gc_decref` when dropped. + pub(crate) fn new_borrowed(inner: *mut raw::Value) -> Self { + let v = Value::new(inner); + unsafe { raw::value_incref(null_mut(), inner) }; + v + } + pub(crate) fn raw_ptr(&self) -> *mut raw::Value { self.inner.as_ptr() } From 03f6c63373b57ee817b267a3b8f8eb7f61a378ce Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 26 Jun 2024 21:00:22 +0200 Subject: [PATCH 092/306] feat: impl Clone for Store (cherry picked from commit 90750c3c82ab0a5973de634661c5284c74fc89a8) --- rust/nix-store/src/store.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/rust/nix-store/src/store.rs b/rust/nix-store/src/store.rs index af820a1..d6cca3d 100644 --- a/rust/nix-store/src/store.rs +++ b/rust/nix-store/src/store.rs @@ -7,6 +7,7 @@ use nix_util::{check_call, result_string_init}; use std::ffi::{c_char, CString}; use std::ptr::null_mut; use std::ptr::NonNull; +use std::sync::Arc; /* TODO make Nix itself thread safe */ lazy_static! { @@ -33,7 +34,7 @@ impl Drop for StoreRef { } pub struct Store { - inner: StoreRef, + inner: Arc, /* An error context to reuse. This way we don't have to allocate them for each store operation. */ context: Context, } @@ -84,9 +85,9 @@ impl Store { panic!("nix_c_store_open returned a null pointer without an error"); } let store = Store { - inner: StoreRef { + inner: Arc::new(StoreRef { inner: NonNull::new(store).unwrap(), - }, + }), context, }; Ok(store) @@ -110,6 +111,15 @@ impl Store { } } +impl Clone for Store { + fn clone(&self) -> Self { + Store { + inner: self.inner.clone(), + context: Context::new(), + } + } +} + #[cfg(test)] mod tests { use std::collections::HashMap; From 3d5c64c4a628e02215e27055d1fca3ab312eaa79 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 26 Jun 2024 21:00:54 +0200 Subject: [PATCH 093/306] feat: impl Clone for EvalState (cherry picked from commit f22d9ba23564107480f627a5901a52f044034c98) --- rust/nix-expr/src/eval_state.rs | 35 ++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 7ba139a..8596dc5 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -11,6 +11,7 @@ use nix_util::{check_call, check_call_opt_key, result_string_init}; use std::ffi::{c_char, CString}; use std::os::raw::c_uint; use std::ptr::{null, null_mut, NonNull}; +use std::sync::Arc; lazy_static! { static ref INIT: Result<()> = { @@ -37,8 +38,24 @@ pub struct RealisedString { pub paths: Vec, } -pub struct EvalState { +struct EvalStateRef { eval_state: NonNull, +} +impl EvalStateRef { + fn as_ptr(&self) -> *mut raw::EvalState { + self.eval_state.as_ptr() + } +} +impl Drop for EvalStateRef { + fn drop(&mut self) { + unsafe { + raw::state_free(self.eval_state.as_ptr()); + } + } +} + +pub struct EvalState { + eval_state: Arc, store: Store, context: Context, } @@ -74,8 +91,10 @@ impl EvalState { )) }?; Ok(EvalState { - eval_state: NonNull::new(eval_state).unwrap_or_else(|| { - panic!("nix_state_create returned a null pointer without an error") + eval_state: Arc::new(EvalStateRef { + eval_state: NonNull::new(eval_state).unwrap_or_else(|| { + panic!("nix_state_create returned a null pointer without an error") + }), }), store, context, @@ -412,10 +431,12 @@ pub fn gc_register_my_thread() -> Result<()> { } } -impl Drop for EvalState { - fn drop(&mut self) { - unsafe { - raw::state_free(self.raw_ptr()); +impl Clone for EvalState { + fn clone(&self) -> Self { + EvalState { + eval_state: self.eval_state.clone(), + store: self.store.clone(), + context: Context::new(), } } } From e0dead151e3c40054de509c1fdd9411285b962e0 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 27 Jun 2024 15:57:43 +0200 Subject: [PATCH 094/306] feat: Store.weak_ref() (cherry picked from commit 2fdcc5df62a6cea790bea9b867e1b6d044d4a28f) --- rust/nix-store/src/store.rs | 40 ++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/rust/nix-store/src/store.rs b/rust/nix-store/src/store.rs index d6cca3d..8ca2031 100644 --- a/rust/nix-store/src/store.rs +++ b/rust/nix-store/src/store.rs @@ -7,7 +7,7 @@ use nix_util::{check_call, result_string_init}; use std::ffi::{c_char, CString}; use std::ptr::null_mut; use std::ptr::NonNull; -use std::sync::Arc; +use std::sync::{Arc, Weak}; /* TODO make Nix itself thread safe */ lazy_static! { @@ -33,6 +33,20 @@ impl Drop for StoreRef { } } +/// A [Weak] reference to a store. +pub struct StoreWeak { + inner: Weak, +} +impl StoreWeak { + /// Upgrade the weak reference to a proper [Store]. + pub fn upgrade(&self) -> Option { + self.inner.upgrade().map(|inner| Store { + inner, + context: Context::new(), + }) + } +} + pub struct Store { inner: Arc, /* An error context to reuse. This way we don't have to allocate them for each store operation. */ @@ -109,6 +123,12 @@ impl Store { }?; r } + + pub fn weak_ref(&self) -> StoreWeak { + StoreWeak { + inner: Arc::downgrade(&self.inner), + } + } } impl Clone for Store { @@ -156,4 +176,22 @@ mod tests { let uri = store.get_uri().unwrap(); assert_eq!(uri, "https://cache.nixos.org"); } + + #[test] + fn weak_ref() { + let mut store = Store::open("auto", HashMap::new()).unwrap(); + let uri = store.get_uri().unwrap(); + let weak = store.weak_ref(); + let mut store2 = weak.upgrade().unwrap(); + assert_eq!(store2.get_uri().unwrap(), uri); + } + #[test] + fn weak_ref_gone() { + let weak = { + let store = Store::open("auto", HashMap::new()).unwrap(); + store.weak_ref() + }; + assert!(weak.upgrade().is_none()); + assert!(weak.inner.upgrade().is_none()); + } } From 01c9d0b2d347f13fd1a8be2833867a6ceed32ed8 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 27 Jun 2024 16:01:40 +0200 Subject: [PATCH 095/306] feat: EvalState.weak_ref() (cherry picked from commit d7a72c82ebfbfbb1b58fa15044b63648b2109260) --- rust/nix-expr/src/eval_state.rs | 57 +++++++++++++++++++++++++++++++-- rust/nix-store/src/store.rs | 2 ++ 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 8596dc5..b1ada05 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -4,14 +4,14 @@ use anyhow::{bail, Result}; use lazy_static::lazy_static; use nix_c_raw as raw; use nix_store::path::StorePath; -use nix_store::store::Store; +use nix_store::store::{Store, StoreWeak}; use nix_util::context::Context; use nix_util::string_return::{callback_get_result_string, callback_get_result_string_data}; use nix_util::{check_call, check_call_opt_key, result_string_init}; use std::ffi::{c_char, CString}; use std::os::raw::c_uint; use std::ptr::{null, null_mut, NonNull}; -use std::sync::Arc; +use std::sync::{Arc, Weak}; lazy_static! { static ref INIT: Result<()> = { @@ -38,6 +38,26 @@ pub struct RealisedString { pub paths: Vec, } +/// A [Weak] reference to an [EvalState] +pub struct EvalStateWeak { + inner: Weak, + store: StoreWeak, +} +impl EvalStateWeak { + /// Upgrade the weak reference to a proper [EvalState]. + /// + /// If no normal reference to the [EvalState] is around anymore elsewhere, this fails by returning `None`. + pub fn upgrade(&self) -> Option { + self.inner.upgrade().and_then(|eval_state| { + self.store.upgrade().map(|store| EvalState { + eval_state: eval_state, + store: store, + context: Context::new(), + }) + }) + } +} + struct EvalStateRef { eval_state: NonNull, } @@ -106,6 +126,13 @@ impl EvalState { pub fn store(&self) -> &Store { &self.store } + pub fn weak_ref(&self) -> EvalStateWeak { + EvalStateWeak { + inner: Arc::downgrade(&self.eval_state), + store: self.store.weak_ref(), + } + } + /// Parses and evaluates a Nix expression `expr`. /// /// Expressions can contain relative paths such as `./.` that are resolved relative to the given `path`. @@ -481,6 +508,32 @@ mod tests { .unwrap(); } + #[test] + fn weak_ref() { + gc_registering_current_thread(|| { + let store = Store::open("auto", HashMap::new()).unwrap(); + let es = EvalState::new(store, []).unwrap(); + let weak = es.weak_ref(); + let _es = weak.upgrade().unwrap(); + }) + .unwrap(); + } + + #[test] + fn weak_ref_gone() { + gc_registering_current_thread(|| { + let weak = { + let store = Store::open("auto", HashMap::new()).unwrap(); + let es = EvalState::new(store, []).unwrap(); + es.weak_ref() + }; + assert!(weak.upgrade().is_none()); + assert!(weak.store.upgrade().is_none()); + assert!(weak.inner.upgrade().is_none()); + }) + .unwrap(); + } + #[test] fn eval_state_lookup_path() { let import_expression = "import + import "; diff --git a/rust/nix-store/src/store.rs b/rust/nix-store/src/store.rs index 8ca2031..631a6f5 100644 --- a/rust/nix-store/src/store.rs +++ b/rust/nix-store/src/store.rs @@ -39,6 +39,8 @@ pub struct StoreWeak { } impl StoreWeak { /// Upgrade the weak reference to a proper [Store]. + /// + /// If no normal reference to the [Store] is around anymore elsewhere, this fails by returning `None`. pub fn upgrade(&self) -> Option { self.inner.upgrade().map(|inner| Store { inner, From eb0595226d185ee2913bfb75dae17ffb8441eba9 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 27 Jun 2024 18:26:05 +0200 Subject: [PATCH 096/306] doc: Context (cherry picked from commit 976ecda38530d57b872c20eaeab5d3631076140b) --- rust/nix-util/src/context.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rust/nix-util/src/context.rs b/rust/nix-util/src/context.rs index 55f0ab4..d4dbe94 100644 --- a/rust/nix-util/src/context.rs +++ b/rust/nix-util/src/context.rs @@ -3,6 +3,9 @@ use nix_c_raw as raw; use std::ptr::null_mut; use std::ptr::NonNull; +/// A context for error handling, when interacting directly with the generated bindings for the C API in [nix_c_raw]. +/// +/// The `nix-store` and `nix-expr` libraries that consume this type internally store a private context in their `EvalState` and `Store` structs to avoid allocating a new context for each operation. The state of a context is irrelevant when used correctly (e.g. with [check_call!]), so it's safe to reuse, and safe to allocate more contexts in methods such as [Clone::clone]. pub struct Context { inner: NonNull, } From 5c43a28cf06c6ba919c42bdc0b9271f7940bf15b Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 27 Jun 2024 18:46:54 +0200 Subject: [PATCH 097/306] Basic primops support (cherry picked from commit e1be238ad440ad6f73a4f15cb752439b240a0333) --- rust/nix-expr/src/eval_state.rs | 233 +++++++++++++++++++++++++++++++- 1 file changed, 232 insertions(+), 1 deletion(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index b1ada05..ba4abc1 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -8,7 +8,8 @@ use nix_store::store::{Store, StoreWeak}; use nix_util::context::Context; use nix_util::string_return::{callback_get_result_string, callback_get_result_string_data}; use nix_util::{check_call, check_call_opt_key, result_string_init}; -use std::ffi::{c_char, CString}; +use std::ffi::{c_char, c_int, c_void, CString}; +use std::mem::ManuallyDrop; use std::os::raw::c_uint; use std::ptr::{null, null_mut, NonNull}; use std::sync::{Arc, Weak}; @@ -414,6 +415,72 @@ impl EvalState { }?; Ok(Value::new(value)) } + + pub fn thunk(&mut self, f: Box Result>) -> Result { + // Nix doesn't have a function for creating a thunk, so we have to + // create a function and pass it a dummy argument. + let f = self.new_value_function( + FUNCTION_ANONYMOUS.as_ptr(), + Box::new(move |eval_state, _dummy: &[Value; 1]| f(eval_state)), + )?; + self.new_value_apply(&f, &f) + } + + /// Create a new function that is backed by a Rust function. + /// This is also known as a "primop" in Nix, short for primitive operation. + /// The `builtins.*` are examples of primops. + pub fn new_value_function( + &mut self, + name: *const i8, + f: Box Result>, + ) -> Result { + if N == 0 { + return self.thunk(Box::new(move |eval_state| { + f(eval_state, { + let empty: &[Value] = &[]; + empty.try_into().unwrap() + }) + })); + } + + let mut args = Vec::new(); + for _ in 0..N { + args.push(FUNCTION_ANONYMOUS_ARG.as_ptr()); + } + args.push(null()); + // This leaks + + let user_data = { + // We'll be leaking this Box. + // TODO: Use the GC with finalizer, if possible. + let user_data = ManuallyDrop::new(Box::new(PrimOpContext { + arity: N, + function: Box::new(move |eval_state, args| { + let r = f(eval_state, args.try_into().unwrap()); + r + }), + eval_state: self.weak_ref(), + })); + user_data.as_ref() as *const PrimOpContext as *mut c_void + }; + let op = unsafe { + check_call!(raw::alloc_primop( + &mut self.context, + FUNCTION_ADAPTER, + N as c_int, + name, + args.as_mut_ptr(), /* TODO add an extra const to bindings to avoid mut here. */ + FUNCTION_ANONYMOUS_DOC.as_ptr(), + user_data + ))? + }; + let value = self.new_value_uninitialized()?; + // Then use it in a value + unsafe { + check_call!(raw::init_primop(&mut self.context, value.raw_ptr(), op))?; + } + Ok(value) + } } pub fn gc_now() { @@ -468,6 +535,55 @@ impl Clone for EvalState { } } +/// The user_data for our Nix primops +struct PrimOpContext { + arity: usize, + // Something like Haskell's Dynamic + function: Box Result>, + eval_state: EvalStateWeak, +} + +unsafe extern "C" fn function_adapter( + user_data: *mut ::std::os::raw::c_void, + context_out: *mut raw::c_context, + _state: *mut raw::EvalState, + args: *mut *mut raw::Value, + ret: *mut raw::Value, +) { + let primop_info = (user_data as *const PrimOpContext).as_ref().unwrap(); + let mut eval_state = primop_info.eval_state.upgrade().unwrap_or_else(|| { + panic!("Nix primop called after EvalState was dropped"); + }); + let args_raw_slice = unsafe { std::slice::from_raw_parts(args, primop_info.arity) }; + let args_vec: Vec = args_raw_slice + .iter() + .map(|v| Value::new_borrowed(*v as *mut c_void)) + .collect(); + let args_slice = args_vec.as_slice(); + + let r = primop_info.function.as_ref()(&mut eval_state, args_slice); + + match r { + Ok(v) => unsafe { + raw::copy_value(context_out, ret, v.raw_ptr()); + }, + Err(e) => unsafe { + let cstr = CString::new(e.to_string()).unwrap_or_else(|_e| { + CString::new("") + .unwrap() + }); + raw::set_err_msg(context_out, raw::NIX_ERR_UNKNOWN, cstr.as_ptr()); + }, + } +} + +static FUNCTION_ADAPTER: raw::PrimOpFun = Some(function_adapter); + +lazy_static! { + pub static ref FUNCTION_ANONYMOUS: CString = CString::new("anonymous-primop").unwrap(); + static ref FUNCTION_ANONYMOUS_ARG: CString = CString::new("x").unwrap(); + static ref FUNCTION_ANONYMOUS_DOC: CString = CString::new("anonymous primop").unwrap(); +} /// Initialize the Nix library for testing. This includes some modifications to the Nix settings, that must not be used in production. /// Use at your own peril, in rust test suites. pub fn test_init() { @@ -492,6 +608,7 @@ mod tests { use std::collections::HashMap; use std::fs::read_dir; use std::io::Write as _; + use std::sync::{Arc, Mutex}; #[ctor] fn setup() { @@ -1264,7 +1381,121 @@ mod tests { }) .unwrap(); } + fn empty(foldable: impl IntoIterator) -> bool { foldable.into_iter().all(|_| false) } + + #[test] + fn eval_state_primop_anon_call() { + gc_registering_current_thread(|| { + let store = Store::open("auto", []).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); + let bias: Arc> = Arc::new(Mutex::new(0)); + let bias_control = bias.clone(); + let f = es + .new_value_function( + FUNCTION_ANONYMOUS.as_ptr(), + Box::new(move |es, [a, b]| { + let a = es.require_int(a)?; + let b = es.require_int(b)?; + let c = *bias.lock().unwrap(); + Ok(es.new_value_int(a + b + c)?) + }), + ) + .unwrap(); + { + *bias_control.lock().unwrap() = 10; + } + let a = es.new_value_int(2).unwrap(); + let b = es.new_value_int(3).unwrap(); + let fa = es.call(f, a).unwrap(); + let v = es.call(fa, b).unwrap(); + es.force(&v).unwrap(); + let t = es.value_type(&v).unwrap(); + assert!(t == ValueType::Int); + let i = es.require_int(&v).unwrap(); + assert!(i == 15); + }) + .unwrap(); + } + + #[test] + fn eval_state_primop_anon_call_throw() { + gc_registering_current_thread(|| { + let store = Store::open("auto", []).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); + let f = es + .new_value_function( + FUNCTION_ANONYMOUS.as_ptr(), + Box::new(move |es, [a]| { + let a = es.require_int(a)?; + bail!("error with arg [{}]", a); + }), + ) + .unwrap(); + let a = es.new_value_int(2).unwrap(); + let r = es.call(f, a); + match r { + Ok(_) => panic!("expected an error"), + Err(e) => { + if !e.to_string().contains("error with arg [2]") { + eprintln!("unexpected error message: {}", e); + assert!(false); + } + } + } + }) + .unwrap(); + } + + #[test] + fn eval_state_primop_anon_call_no_args() { + gc_registering_current_thread(|| { + let store = Store::open("auto", []).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); + let v = es + .new_value_function( + FUNCTION_ANONYMOUS.as_ptr(), + Box::new(move |es, []| Ok(es.new_value_int(42)?)), + ) + .unwrap(); + es.force(&v).unwrap(); + let t = es.value_type(&v).unwrap(); + eprintln!("{:?}", t); + assert!(t == ValueType::Int); + let i = es.require_int(&v).unwrap(); + assert!(i == 42); + }) + .unwrap(); + } + + #[test] + fn eval_state_primop_anon_call_no_args_lazy() { + gc_registering_current_thread(|| { + let store = Store::open("auto", []).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); + let v = es + .new_value_function( + FUNCTION_ANONYMOUS.as_ptr(), + Box::new(move |_es, []| { + bail!("error message in test case eval_state_primop_anon_call_no_args_lazy") + }), + ) + .unwrap(); + let r = es.force(&v); + match r { + Ok(_) => panic!("expected an error"), + Err(e) => { + if !e.to_string().contains( + "error message in test case eval_state_primop_anon_call_no_args_lazy", + ) { + eprintln!("unexpected error message: {}", e); + assert!(false); + } + } + } + }) + .unwrap(); + } } From 312c86b8115eb81c4c4fe4136d2d9ba33abc8925 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 19 Jul 2024 17:54:31 +0200 Subject: [PATCH 098/306] feat: PrimOp type (cherry picked from commit 67616c4a55b9d98d716384ffc07d3b3880dd76e4) --- rust/Cargo.lock | 11 ++ rust/nix-expr/Cargo.toml | 1 + rust/nix-expr/src/eval_state.rs | 184 +++++++++++++++++--------------- rust/nix-expr/src/lib.rs | 1 + rust/nix-expr/src/primop.rs | 120 +++++++++++++++++++++ 5 files changed, 233 insertions(+), 84 deletions(-) create mode 100644 rust/nix-expr/src/primop.rs diff --git a/rust/Cargo.lock b/rust/Cargo.lock index f9800e7..51e5dc9 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -72,6 +72,16 @@ dependencies = [ "libloading", ] +[[package]] +name = "cstr" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68523903c8ae5aacfa32a0d9ae60cadeb764e1da14ee0d26b1f3089f13a54636" +dependencies = [ + "proc-macro2", + "quote", +] + [[package]] name = "ctor" version = "0.2.7" @@ -193,6 +203,7 @@ name = "nix-expr" version = "0.1.0" dependencies = [ "anyhow", + "cstr", "ctor", "lazy_static", "nix-c-raw", diff --git a/rust/nix-expr/Cargo.toml b/rust/nix-expr/Cargo.toml index 2733579..1de81e5 100644 --- a/rust/nix-expr/Cargo.toml +++ b/rust/nix-expr/Cargo.toml @@ -13,3 +13,4 @@ nix-c-raw = { path = "../nix-c-raw" } lazy_static = "1.4.0" ctor = "0.2.7" tempfile = "3.10.1" +cstr = "0.2.12" diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index ba4abc1..ca68676 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -1,3 +1,4 @@ +use crate::primop; use crate::value::{Int, Value, ValueType}; use anyhow::Context as _; use anyhow::{bail, Result}; @@ -8,8 +9,7 @@ use nix_store::store::{Store, StoreWeak}; use nix_util::context::Context; use nix_util::string_return::{callback_get_result_string, callback_get_result_string_data}; use nix_util::{check_call, check_call_opt_key, result_string_init}; -use std::ffi::{c_char, c_int, c_void, CString}; -use std::mem::ManuallyDrop; +use std::ffi::{c_char, CStr, CString}; use std::os::raw::c_uint; use std::ptr::{null, null_mut, NonNull}; use std::sync::{Arc, Weak}; @@ -78,7 +78,7 @@ impl Drop for EvalStateRef { pub struct EvalState { eval_state: Arc, store: Store, - context: Context, + pub(crate) context: Context, } impl EvalState { pub fn new<'a>(store: Store, lookup_path: impl IntoIterator) -> Result { @@ -426,9 +426,23 @@ impl EvalState { self.new_value_apply(&f, &f) } - /// Create a new function that is backed by a Rust function. + /// Create a new Nix function that is implemented by a Rust function. /// This is also known as a "primop" in Nix, short for primitive operation. - /// The `builtins.*` are examples of primops. + /// Most of the `builtins.*` values are examples of primops, but + /// `new_value_primop` does not affect `builtins`. + pub fn new_value_primop(&mut self, primop: primop::PrimOp) -> Result { + let value = self.new_value_uninitialized()?; + unsafe { + check_call!(raw::init_primop( + &mut self.context, + value.raw_ptr(), + primop.ptr + ))?; + }; + Ok(value) + } + + /// A worse quality shortcut for calling [new_value_primop]. pub fn new_value_function( &mut self, name: *const i8, @@ -443,43 +457,21 @@ impl EvalState { })); } - let mut args = Vec::new(); - for _ in 0..N { - args.push(FUNCTION_ANONYMOUS_ARG.as_ptr()); - } - args.push(null()); - // This leaks + let name = unsafe { CStr::from_ptr(name) }; - let user_data = { - // We'll be leaking this Box. - // TODO: Use the GC with finalizer, if possible. - let user_data = ManuallyDrop::new(Box::new(PrimOpContext { - arity: N, - function: Box::new(move |eval_state, args| { - let r = f(eval_state, args.try_into().unwrap()); - r - }), - eval_state: self.weak_ref(), - })); - user_data.as_ref() as *const PrimOpContext as *mut c_void - }; - let op = unsafe { - check_call!(raw::alloc_primop( - &mut self.context, - FUNCTION_ADAPTER, - N as c_int, + let args: [&CStr; N] = [FUNCTION_ANONYMOUS_ARG.as_ref(); N]; + + let prim = primop::PrimOp::new( + self, + primop::PrimOpMeta { name, - args.as_mut_ptr(), /* TODO add an extra const to bindings to avoid mut here. */ - FUNCTION_ANONYMOUS_DOC.as_ptr(), - user_data - ))? - }; - let value = self.new_value_uninitialized()?; - // Then use it in a value - unsafe { - check_call!(raw::init_primop(&mut self.context, value.raw_ptr(), op))?; - } - Ok(value) + args, + doc: FUNCTION_ANONYMOUS_DOC.as_ref(), + }, + f, + )?; + + self.new_value_primop(prim) } } @@ -535,50 +527,6 @@ impl Clone for EvalState { } } -/// The user_data for our Nix primops -struct PrimOpContext { - arity: usize, - // Something like Haskell's Dynamic - function: Box Result>, - eval_state: EvalStateWeak, -} - -unsafe extern "C" fn function_adapter( - user_data: *mut ::std::os::raw::c_void, - context_out: *mut raw::c_context, - _state: *mut raw::EvalState, - args: *mut *mut raw::Value, - ret: *mut raw::Value, -) { - let primop_info = (user_data as *const PrimOpContext).as_ref().unwrap(); - let mut eval_state = primop_info.eval_state.upgrade().unwrap_or_else(|| { - panic!("Nix primop called after EvalState was dropped"); - }); - let args_raw_slice = unsafe { std::slice::from_raw_parts(args, primop_info.arity) }; - let args_vec: Vec = args_raw_slice - .iter() - .map(|v| Value::new_borrowed(*v as *mut c_void)) - .collect(); - let args_slice = args_vec.as_slice(); - - let r = primop_info.function.as_ref()(&mut eval_state, args_slice); - - match r { - Ok(v) => unsafe { - raw::copy_value(context_out, ret, v.raw_ptr()); - }, - Err(e) => unsafe { - let cstr = CString::new(e.to_string()).unwrap_or_else(|_e| { - CString::new("") - .unwrap() - }); - raw::set_err_msg(context_out, raw::NIX_ERR_UNKNOWN, cstr.as_ptr()); - }, - } -} - -static FUNCTION_ADAPTER: raw::PrimOpFun = Some(function_adapter); - lazy_static! { pub static ref FUNCTION_ANONYMOUS: CString = CString::new("anonymous-primop").unwrap(); static ref FUNCTION_ANONYMOUS_ARG: CString = CString::new("x").unwrap(); @@ -604,6 +552,7 @@ pub fn test_init() { #[cfg(test)] mod tests { use super::*; + use cstr::cstr; use ctor::ctor; use std::collections::HashMap; use std::fs::read_dir; @@ -1498,4 +1447,71 @@ mod tests { }) .unwrap(); } + + #[test] + pub fn eval_state_primop_custom() { + gc_registering_current_thread(|| { + let store = Store::open("auto", []).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); + let primop = primop::PrimOp::new( + &mut es, + primop::PrimOpMeta { + name: cstr!("frobnicate"), + doc: cstr!("Frobnicates widgets"), + args: [cstr!("x"), cstr!("y")], + }, + Box::new(|es, args| { + let a = es.require_int(&args[0])?; + let b = es.require_int(&args[1])?; + Ok(es.new_value_int(a + b)?) + }), + ) + .unwrap(); + let f = es.new_value_primop(primop).unwrap(); + let a = es.new_value_int(2).unwrap(); + let b = es.new_value_int(3).unwrap(); + let fa = es.call(f, a).unwrap(); + let fb = es.call(fa, b).unwrap(); + es.force(&fb).unwrap(); + let t = es.value_type(&fb).unwrap(); + assert!(t == ValueType::Int); + let i = es.require_int(&fb).unwrap(); + assert!(i == 5); + }) + .unwrap(); + } + + #[test] + pub fn eval_state_primop_custom_throw() { + gc_registering_current_thread(|| { + let store = Store::open("auto", []).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); + let primop = primop::PrimOp::new( + &mut es, + primop::PrimOpMeta { + name: cstr!("frobnicate"), + doc: cstr!("Frobnicates widgets"), + args: [cstr!("x")], + }, + Box::new(|_es, _args| bail!("The frob unexpectedly fizzled")), + ) + .unwrap(); + let f = es.new_value_primop(primop).unwrap(); + let a = es.new_value_int(0).unwrap(); + match es.call(f, a) { + Ok(_) => panic!("expected an error"), + Err(e) => { + if !e.to_string().contains("The frob unexpectedly fizzled") { + eprintln!("unexpected error message: {}", e); + assert!(false); + } + if !e.to_string().contains("frobnicate") { + eprintln!("unexpected error message: {}", e); + assert!(false); + } + } + } + }) + .unwrap(); + } } diff --git a/rust/nix-expr/src/lib.rs b/rust/nix-expr/src/lib.rs index ead2024..41c4b39 100644 --- a/rust/nix-expr/src/lib.rs +++ b/rust/nix-expr/src/lib.rs @@ -1,2 +1,3 @@ pub mod eval_state; +pub mod primop; pub mod value; diff --git a/rust/nix-expr/src/primop.rs b/rust/nix-expr/src/primop.rs new file mode 100644 index 0000000..d2b2033 --- /dev/null +++ b/rust/nix-expr/src/primop.rs @@ -0,0 +1,120 @@ +use crate::eval_state::{EvalState, EvalStateWeak}; +use crate::value::Value; +use anyhow::Result; +use nix_c_raw as raw; +use nix_util::check_call; +use std::ffi::{c_int, c_void, CStr, CString}; +use std::mem::ManuallyDrop; +use std::ptr::{null, null_mut}; + +/// Metadata for a primop, used with `PrimOp::new`. +pub struct PrimOpMeta<'a, const N: usize> { + /// Name of the primop. Note that primops do not have to be registered as + /// builtins. Nonetheless, a name is required for documentation purposes, e.g. + /// :doc in the repl. + pub name: &'a CStr, + + /// Documentation for the primop. This is displayed in the repl when using + /// :doc. The format is markdown. + pub doc: &'a CStr, + + /// The number of arguments the function takes, as well as names for the + /// arguments, to be presented in the documentation (if applicable, e.g. + /// :doc in the repl). + pub args: [&'a CStr; N], +} + +pub struct PrimOp { + pub(crate) ptr: *mut raw::PrimOp, +} +impl Drop for PrimOp { + fn drop(&mut self) { + unsafe { + raw::gc_decref(null_mut(), self.ptr as *mut c_void); + } + } +} +impl PrimOp { + pub fn new( + eval_state: &mut EvalState, + meta: PrimOpMeta, + f: Box Result>, + ) -> Result { + let mut args = Vec::new(); + for arg in meta.args { + args.push(arg.as_ptr()); + } + args.push(null()); + + // Primops weren't meant to be dynamically created, as of writing. + // This leaks, and so do the primop fields in Nix internally. + let user_data = { + // We'll be leaking this Box. + // TODO: Use the GC with finalizer, if possible. + let user_data = ManuallyDrop::new(Box::new(PrimOpContext { + arity: N, + function: Box::new(move |eval_state, args| { + let r = f(eval_state, args.try_into().unwrap()); + r + }), + eval_state: eval_state.weak_ref(), + })); + user_data.as_ref() as *const PrimOpContext as *mut c_void + }; + let op = unsafe { + check_call!(raw::alloc_primop( + &mut eval_state.context, + FUNCTION_ADAPTER, + N as c_int, + meta.name.as_ptr(), + args.as_mut_ptr(), /* TODO add an extra const to bindings to avoid mut here. */ + meta.doc.as_ptr(), + user_data + ))? + }; + Ok(PrimOp { ptr: op }) + } +} + +/// The user_data for our Nix primops +struct PrimOpContext { + arity: usize, + function: Box Result>, + eval_state: EvalStateWeak, +} + +unsafe extern "C" fn function_adapter( + user_data: *mut ::std::os::raw::c_void, + context_out: *mut raw::c_context, + _state: *mut raw::EvalState, + args: *mut *mut raw::Value, + ret: *mut raw::Value, +) { + let primop_info = (user_data as *const PrimOpContext).as_ref().unwrap(); + let mut eval_state = primop_info.eval_state.upgrade().unwrap_or_else(|| { + panic!("Nix primop called after EvalState was dropped"); + }); + let args_raw_slice = unsafe { std::slice::from_raw_parts(args, primop_info.arity) }; + let args_vec: Vec = args_raw_slice + .iter() + .map(|v| Value::new_borrowed(*v)) + .collect(); + let args_slice = args_vec.as_slice(); + + let r = primop_info.function.as_ref()(&mut eval_state, args_slice); + + match r { + Ok(v) => unsafe { + raw::copy_value(context_out, ret, v.raw_ptr()); + }, + Err(e) => unsafe { + let cstr = CString::new(e.to_string()).unwrap_or_else(|_e| { + CString::new("") + .unwrap() + }); + raw::set_err_msg(context_out, raw::err_NIX_ERR_UNKNOWN, cstr.as_ptr()); + }, + } +} + +static FUNCTION_ADAPTER: raw::PrimOpFun = Some(function_adapter); From da9bb4c885675a058ad8a731ec175bbaae8032bc Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 19 Jul 2024 18:04:34 +0200 Subject: [PATCH 099/306] cleanup: Remove EvalState::new_value_function It provides not so great values for some of the parameters, and we don't really need its convenience. (cherry picked from commit 52b7b58eb7fa96a265883cbf92e3a635735fe360) --- rust/nix-expr/src/eval_state.rs | 172 +++++++++++++++++--------------- 1 file changed, 90 insertions(+), 82 deletions(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index ca68676..381767b 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -9,7 +9,7 @@ use nix_store::store::{Store, StoreWeak}; use nix_util::context::Context; use nix_util::string_return::{callback_get_result_string, callback_get_result_string_data}; use nix_util::{check_call, check_call_opt_key, result_string_init}; -use std::ffi::{c_char, CStr, CString}; +use std::ffi::{c_char, CString}; use std::os::raw::c_uint; use std::ptr::{null, null_mut, NonNull}; use std::sync::{Arc, Weak}; @@ -416,16 +416,6 @@ impl EvalState { Ok(Value::new(value)) } - pub fn thunk(&mut self, f: Box Result>) -> Result { - // Nix doesn't have a function for creating a thunk, so we have to - // create a function and pass it a dummy argument. - let f = self.new_value_function( - FUNCTION_ANONYMOUS.as_ptr(), - Box::new(move |eval_state, _dummy: &[Value; 1]| f(eval_state)), - )?; - self.new_value_apply(&f, &f) - } - /// Create a new Nix function that is implemented by a Rust function. /// This is also known as a "primop" in Nix, short for primitive operation. /// Most of the `builtins.*` values are examples of primops, but @@ -441,38 +431,6 @@ impl EvalState { }; Ok(value) } - - /// A worse quality shortcut for calling [new_value_primop]. - pub fn new_value_function( - &mut self, - name: *const i8, - f: Box Result>, - ) -> Result { - if N == 0 { - return self.thunk(Box::new(move |eval_state| { - f(eval_state, { - let empty: &[Value] = &[]; - empty.try_into().unwrap() - }) - })); - } - - let name = unsafe { CStr::from_ptr(name) }; - - let args: [&CStr; N] = [FUNCTION_ANONYMOUS_ARG.as_ref(); N]; - - let prim = primop::PrimOp::new( - self, - primop::PrimOpMeta { - name, - args, - doc: FUNCTION_ANONYMOUS_DOC.as_ref(), - }, - f, - )?; - - self.new_value_primop(prim) - } } pub fn gc_now() { @@ -527,11 +485,6 @@ impl Clone for EvalState { } } -lazy_static! { - pub static ref FUNCTION_ANONYMOUS: CString = CString::new("anonymous-primop").unwrap(); - static ref FUNCTION_ANONYMOUS_ARG: CString = CString::new("x").unwrap(); - static ref FUNCTION_ANONYMOUS_DOC: CString = CString::new("anonymous primop").unwrap(); -} /// Initialize the Nix library for testing. This includes some modifications to the Nix settings, that must not be used in production. /// Use at your own peril, in rust test suites. pub fn test_init() { @@ -555,6 +508,7 @@ mod tests { use cstr::cstr; use ctor::ctor; use std::collections::HashMap; + use std::ffi::CStr; use std::fs::read_dir; use std::io::Write as _; use std::sync::{Arc, Mutex}; @@ -564,6 +518,60 @@ mod tests { test_init(); } + lazy_static! { + pub static ref FUNCTION_ANONYMOUS: CString = CString::new("anonymous-primop").unwrap(); + static ref FUNCTION_ANONYMOUS_ARG: CString = CString::new("x").unwrap(); + static ref FUNCTION_ANONYMOUS_DOC: CString = CString::new("anonymous primop").unwrap(); + } + pub fn thunk( + es: &mut EvalState, + f: Box Result>, + ) -> Result { + // Nix doesn't have a function for creating a thunk, so we have to + // create a function and pass it a dummy argument. + let f = new_value_function( + es, + FUNCTION_ANONYMOUS.as_ptr(), + Box::new(move |eval_state, _dummy: &[Value; 1]| f(eval_state)), + )?; + es.new_value_apply(&f, &f) + } + + /// A worse quality shortcut for calling [new_value_primop]. + pub fn new_value_function( + es: &mut EvalState, + name: *const i8, + f: Box Result>, + ) -> Result { + if N == 0 { + return thunk( + es, + Box::new(move |eval_state| { + f(eval_state, { + let empty: &[Value] = &[]; + empty.try_into().unwrap() + }) + }), + ); + } + + let name = unsafe { CStr::from_ptr(name) }; + + let args: [&CStr; N] = [FUNCTION_ANONYMOUS_ARG.as_ref(); N]; + + let prim = primop::PrimOp::new( + es, + primop::PrimOpMeta { + name, + args, + doc: FUNCTION_ANONYMOUS_DOC.as_ref(), + }, + f, + )?; + + es.new_value_primop(prim) + } + #[test] fn eval_state_new_and_drop() { gc_registering_current_thread(|| { @@ -1342,17 +1350,17 @@ mod tests { let mut es = EvalState::new(store, []).unwrap(); let bias: Arc> = Arc::new(Mutex::new(0)); let bias_control = bias.clone(); - let f = es - .new_value_function( - FUNCTION_ANONYMOUS.as_ptr(), - Box::new(move |es, [a, b]| { - let a = es.require_int(a)?; - let b = es.require_int(b)?; - let c = *bias.lock().unwrap(); - Ok(es.new_value_int(a + b + c)?) - }), - ) - .unwrap(); + let f = new_value_function( + &mut es, + FUNCTION_ANONYMOUS.as_ptr(), + Box::new(move |es, [a, b]| { + let a = es.require_int(a)?; + let b = es.require_int(b)?; + let c = *bias.lock().unwrap(); + Ok(es.new_value_int(a + b + c)?) + }), + ) + .unwrap(); { *bias_control.lock().unwrap() = 10; } @@ -1374,15 +1382,15 @@ mod tests { gc_registering_current_thread(|| { let store = Store::open("auto", []).unwrap(); let mut es = EvalState::new(store, []).unwrap(); - let f = es - .new_value_function( - FUNCTION_ANONYMOUS.as_ptr(), - Box::new(move |es, [a]| { - let a = es.require_int(a)?; - bail!("error with arg [{}]", a); - }), - ) - .unwrap(); + let f = new_value_function( + &mut es, + FUNCTION_ANONYMOUS.as_ptr(), + Box::new(move |es, [a]| { + let a = es.require_int(a)?; + bail!("error with arg [{}]", a); + }), + ) + .unwrap(); let a = es.new_value_int(2).unwrap(); let r = es.call(f, a); match r { @@ -1403,12 +1411,12 @@ mod tests { gc_registering_current_thread(|| { let store = Store::open("auto", []).unwrap(); let mut es = EvalState::new(store, []).unwrap(); - let v = es - .new_value_function( - FUNCTION_ANONYMOUS.as_ptr(), - Box::new(move |es, []| Ok(es.new_value_int(42)?)), - ) - .unwrap(); + let v = new_value_function( + &mut es, + FUNCTION_ANONYMOUS.as_ptr(), + Box::new(move |es, []| Ok(es.new_value_int(42)?)), + ) + .unwrap(); es.force(&v).unwrap(); let t = es.value_type(&v).unwrap(); eprintln!("{:?}", t); @@ -1424,14 +1432,14 @@ mod tests { gc_registering_current_thread(|| { let store = Store::open("auto", []).unwrap(); let mut es = EvalState::new(store, []).unwrap(); - let v = es - .new_value_function( - FUNCTION_ANONYMOUS.as_ptr(), - Box::new(move |_es, []| { - bail!("error message in test case eval_state_primop_anon_call_no_args_lazy") - }), - ) - .unwrap(); + let v = new_value_function( + &mut es, + FUNCTION_ANONYMOUS.as_ptr(), + Box::new(move |_es, []| { + bail!("error message in test case eval_state_primop_anon_call_no_args_lazy") + }), + ) + .unwrap(); let r = es.force(&v); match r { Ok(_) => panic!("expected an error"), From ee6c04a94170df3059a96b2b64a4caddc0a7c282 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 19 Jul 2024 18:29:56 +0200 Subject: [PATCH 100/306] feat: Reintroduce EvalState::new_value_thunk (cherry picked from commit a2c49a524cf76ca2c9a6759d271d58076756ac09) --- rust/nix-expr/src/eval_state.rs | 52 +++++++++++++++++++++++---------- rust/nix-expr/src/primop.rs | 2 ++ 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 381767b..05bd091 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -2,6 +2,7 @@ use crate::primop; use crate::value::{Int, Value, ValueType}; use anyhow::Context as _; use anyhow::{bail, Result}; +use cstr::cstr; use lazy_static::lazy_static; use nix_c_raw as raw; use nix_store::path::StorePath; @@ -307,6 +308,36 @@ impl EvalState { Ok(v) } + /// Create a new thunk that will evaluate to the result of the given function. + /// The function will be called with the current EvalState. + /// The function must not return a thunk. + /// + /// The name is shown in stack traces. + pub fn new_value_thunk( + &mut self, + name: &str, + f: Box Result>, + ) -> Result { + // Nix doesn't have a function for creating a thunk, so we have to + // create a function and pass it a dummy argument. + let name = CString::new(name).with_context(|| "new_thunk: name contains null byte")?; + let primop = primop::PrimOp::new( + self, + primop::PrimOpMeta { + // name is observable in stack traces, ie if the thunk returns Err + name: name.as_c_str(), + // doc is unlikely to be observable, so we provide a constant one for simplicity. + doc: cstr!("Performs an on demand computation, implemented outside the Nix language in native code."), + // like doc, unlikely to be observed + args: [CString::new("internal_unused").unwrap().as_c_str()], + }, + Box::new(move |eval_state, _dummy: &[Value; 1]| f(eval_state)), + )?; + + let p = self.new_value_primop(primop)?; + self.new_value_apply(&p, &p) + } + /// Not exposed, because the caller must always explicitly handle the context or not accept one at all. fn get_string(&mut self, value: &Value) -> Result { let mut r = result_string_init!(); @@ -523,19 +554,6 @@ mod tests { static ref FUNCTION_ANONYMOUS_ARG: CString = CString::new("x").unwrap(); static ref FUNCTION_ANONYMOUS_DOC: CString = CString::new("anonymous primop").unwrap(); } - pub fn thunk( - es: &mut EvalState, - f: Box Result>, - ) -> Result { - // Nix doesn't have a function for creating a thunk, so we have to - // create a function and pass it a dummy argument. - let f = new_value_function( - es, - FUNCTION_ANONYMOUS.as_ptr(), - Box::new(move |eval_state, _dummy: &[Value; 1]| f(eval_state)), - )?; - es.new_value_apply(&f, &f) - } /// A worse quality shortcut for calling [new_value_primop]. pub fn new_value_function( @@ -544,8 +562,8 @@ mod tests { f: Box Result>, ) -> Result { if N == 0 { - return thunk( - es, + return es.new_value_thunk( + "test_thunk", Box::new(move |eval_state| { f(eval_state, { let empty: &[Value] = &[]; @@ -1450,6 +1468,10 @@ mod tests { eprintln!("unexpected error message: {}", e); assert!(false); } + if !e.to_string().contains("test_thunk") { + eprintln!("unexpected error message: {}", e); + assert!(false); + } } } }) diff --git a/rust/nix-expr/src/primop.rs b/rust/nix-expr/src/primop.rs index d2b2033..9234954 100644 --- a/rust/nix-expr/src/primop.rs +++ b/rust/nix-expr/src/primop.rs @@ -40,6 +40,8 @@ impl PrimOp { meta: PrimOpMeta, f: Box Result>, ) -> Result { + assert!(N != 0); + let mut args = Vec::new(); for arg in meta.args { args.push(arg.as_ptr()); From b5f9764c4c147a846175a0bb5c20c884833c18c7 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 19 Jul 2024 18:36:58 +0200 Subject: [PATCH 101/306] refact: Remove some lazy_static test globals (cherry picked from commit 4f0ce4960ec468679993cc9fd70820f6d0532c09) --- rust/nix-expr/src/eval_state.rs | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 05bd091..1088aac 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -549,16 +549,10 @@ mod tests { test_init(); } - lazy_static! { - pub static ref FUNCTION_ANONYMOUS: CString = CString::new("anonymous-primop").unwrap(); - static ref FUNCTION_ANONYMOUS_ARG: CString = CString::new("x").unwrap(); - static ref FUNCTION_ANONYMOUS_DOC: CString = CString::new("anonymous primop").unwrap(); - } - /// A worse quality shortcut for calling [new_value_primop]. pub fn new_value_function( es: &mut EvalState, - name: *const i8, + name: &CStr, f: Box Result>, ) -> Result { if N == 0 { @@ -573,16 +567,14 @@ mod tests { ); } - let name = unsafe { CStr::from_ptr(name) }; - - let args: [&CStr; N] = [FUNCTION_ANONYMOUS_ARG.as_ref(); N]; + let args: [&CStr; N] = [cstr!("arg"); N]; let prim = primop::PrimOp::new( es, primop::PrimOpMeta { name, args, - doc: FUNCTION_ANONYMOUS_DOC.as_ref(), + doc: cstr!("anonymous test function"), }, f, )?; @@ -1370,7 +1362,7 @@ mod tests { let bias_control = bias.clone(); let f = new_value_function( &mut es, - FUNCTION_ANONYMOUS.as_ptr(), + cstr!("testFunction"), Box::new(move |es, [a, b]| { let a = es.require_int(a)?; let b = es.require_int(b)?; @@ -1402,7 +1394,7 @@ mod tests { let mut es = EvalState::new(store, []).unwrap(); let f = new_value_function( &mut es, - FUNCTION_ANONYMOUS.as_ptr(), + cstr!("throwingTestFunction"), Box::new(move |es, [a]| { let a = es.require_int(a)?; bail!("error with arg [{}]", a); @@ -1431,7 +1423,7 @@ mod tests { let mut es = EvalState::new(store, []).unwrap(); let v = new_value_function( &mut es, - FUNCTION_ANONYMOUS.as_ptr(), + cstr!("zeroArgTestFunction"), Box::new(move |es, []| Ok(es.new_value_int(42)?)), ) .unwrap(); @@ -1452,7 +1444,7 @@ mod tests { let mut es = EvalState::new(store, []).unwrap(); let v = new_value_function( &mut es, - FUNCTION_ANONYMOUS.as_ptr(), + cstr!("zeroArgTestFunction"), Box::new(move |_es, []| { bail!("error message in test case eval_state_primop_anon_call_no_args_lazy") }), From 5355b663c05a7780bc60e42658d22c56b2c8a7d2 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 19 Jul 2024 18:50:10 +0200 Subject: [PATCH 102/306] refact: Inline new_value_function(), a test helper (cherry picked from commit a354accb952558d30a780eb57fd3161be139db7d) --- rust/nix-expr/src/eval_state.rs | 100 ++++++++++++++------------------ 1 file changed, 42 insertions(+), 58 deletions(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 1088aac..5851fec 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -539,7 +539,6 @@ mod tests { use cstr::cstr; use ctor::ctor; use std::collections::HashMap; - use std::ffi::CStr; use std::fs::read_dir; use std::io::Write as _; use std::sync::{Arc, Mutex}; @@ -549,39 +548,6 @@ mod tests { test_init(); } - /// A worse quality shortcut for calling [new_value_primop]. - pub fn new_value_function( - es: &mut EvalState, - name: &CStr, - f: Box Result>, - ) -> Result { - if N == 0 { - return es.new_value_thunk( - "test_thunk", - Box::new(move |eval_state| { - f(eval_state, { - let empty: &[Value] = &[]; - empty.try_into().unwrap() - }) - }), - ); - } - - let args: [&CStr; N] = [cstr!("arg"); N]; - - let prim = primop::PrimOp::new( - es, - primop::PrimOpMeta { - name, - args, - doc: cstr!("anonymous test function"), - }, - f, - )?; - - es.new_value_primop(prim) - } - #[test] fn eval_state_new_and_drop() { gc_registering_current_thread(|| { @@ -1360,9 +1326,14 @@ mod tests { let mut es = EvalState::new(store, []).unwrap(); let bias: Arc> = Arc::new(Mutex::new(0)); let bias_control = bias.clone(); - let f = new_value_function( + + let primop = primop::PrimOp::new( &mut es, - cstr!("testFunction"), + primop::PrimOpMeta { + name: cstr!("testFunction"), + args: [cstr!("a"), cstr!("b")], + doc: cstr!("anonymous test function"), + }, Box::new(move |es, [a, b]| { let a = es.require_int(a)?; let b = es.require_int(b)?; @@ -1371,6 +1342,9 @@ mod tests { }), ) .unwrap(); + + let f = es.new_value_primop(primop).unwrap(); + { *bias_control.lock().unwrap() = 10; } @@ -1392,14 +1366,24 @@ mod tests { gc_registering_current_thread(|| { let store = Store::open("auto", []).unwrap(); let mut es = EvalState::new(store, []).unwrap(); - let f = new_value_function( - &mut es, - cstr!("throwingTestFunction"), - Box::new(move |es, [a]| { - let a = es.require_int(a)?; - bail!("error with arg [{}]", a); - }), - ) + let f = { + let es: &mut EvalState = &mut es; + let prim = primop::PrimOp::new( + es, + primop::PrimOpMeta { + name: cstr!("throwingTestFunction"), + args: [cstr!("arg")], + doc: cstr!("anonymous test function"), + }, + Box::new(move |es, [a]| { + let a = es.require_int(a)?; + bail!("error with arg [{}]", a); + }), + ) + .unwrap(); + + es.new_value_primop(prim) + } .unwrap(); let a = es.new_value_int(2).unwrap(); let r = es.call(f, a); @@ -1421,12 +1405,12 @@ mod tests { gc_registering_current_thread(|| { let store = Store::open("auto", []).unwrap(); let mut es = EvalState::new(store, []).unwrap(); - let v = new_value_function( - &mut es, - cstr!("zeroArgTestFunction"), - Box::new(move |es, []| Ok(es.new_value_int(42)?)), - ) - .unwrap(); + let v = es + .new_value_thunk( + "test_thunk", + Box::new(move |es: &mut EvalState| Ok(es.new_value_int(42)?)), + ) + .unwrap(); es.force(&v).unwrap(); let t = es.value_type(&v).unwrap(); eprintln!("{:?}", t); @@ -1442,14 +1426,14 @@ mod tests { gc_registering_current_thread(|| { let store = Store::open("auto", []).unwrap(); let mut es = EvalState::new(store, []).unwrap(); - let v = new_value_function( - &mut es, - cstr!("zeroArgTestFunction"), - Box::new(move |_es, []| { - bail!("error message in test case eval_state_primop_anon_call_no_args_lazy") - }), - ) - .unwrap(); + let v = es + .new_value_thunk( + "test_thunk", + Box::new(move |_| { + bail!("error message in test case eval_state_primop_anon_call_no_args_lazy") + }), + ) + .unwrap(); let r = es.force(&v); match r { Ok(_) => panic!("expected an error"), From 9213c9a5a08bf9e17db1234b0f03376c0568e471 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 9 Sep 2024 12:24:39 +0200 Subject: [PATCH 103/306] feat: Make Cargo.toml source locations explicit This allows cargo metadata to operate on it without adding the source files to the build. (A choice which will save a few rebuilds of the manual) (cherry picked from commit 1779295f3e13cc15f8422d52a3753bb927ac8fa7) --- rust/nix-c-raw/Cargo.toml | 1 + rust/nix-expr/Cargo.toml | 1 + rust/nix-store/Cargo.toml | 1 + rust/nix-util/Cargo.toml | 1 + 4 files changed, 4 insertions(+) diff --git a/rust/nix-c-raw/Cargo.toml b/rust/nix-c-raw/Cargo.toml index 462a09e..c1bbd01 100644 --- a/rust/nix-c-raw/Cargo.toml +++ b/rust/nix-c-raw/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" build = "build.rs" [lib] +path = "src/lib.rs" [build-dependencies] bindgen = "0.69.4" diff --git a/rust/nix-expr/Cargo.toml b/rust/nix-expr/Cargo.toml index 1de81e5..1c68648 100644 --- a/rust/nix-expr/Cargo.toml +++ b/rust/nix-expr/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" edition = "2021" [lib] +path = "src/lib.rs" [dependencies] anyhow = "1.0.79" diff --git a/rust/nix-store/Cargo.toml b/rust/nix-store/Cargo.toml index 3af19d4..2df0340 100644 --- a/rust/nix-store/Cargo.toml +++ b/rust/nix-store/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" edition = "2021" [lib] +path = "src/lib.rs" [dependencies] anyhow = "1.0.79" diff --git a/rust/nix-util/Cargo.toml b/rust/nix-util/Cargo.toml index e323aa3..a58cb43 100644 --- a/rust/nix-util/Cargo.toml +++ b/rust/nix-util/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" edition = "2021" [lib] +path = "src/lib.rs" [dependencies] anyhow = "1.0.79" From 69d4ccc42235a74f54f2146d489a4a350144baa5 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 2 Sep 2024 14:25:31 +0200 Subject: [PATCH 104/306] fix: Temporarily revert Nix to recover getFlake https://github.com/NixOS/nix/issues/11399 (cherry picked from commit 0f2ae7d080dda34938517f781b4eb436522e9d65) --- flake.lock | 80 ++++++++++++++++++------------------ flake.nix | 2 +- rust/nix-c-raw/build.rs | 1 + rust/nix-expr/src/primop.rs | 2 +- rust/nix-util/src/context.rs | 10 ++--- 5 files changed, 48 insertions(+), 47 deletions(-) diff --git a/flake.lock b/flake.lock index bb36bb7..f031e6b 100644 --- a/flake.lock +++ b/flake.lock @@ -95,50 +95,18 @@ "type": "github" } }, - "git-hooks-nix": { - "inputs": { - "flake-compat": [ - "nix" - ], - "gitignore": [ - "nix" - ], - "nixpkgs": [ - "nix", - "nixpkgs" - ], - "nixpkgs-stable": [ - "nix", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1721042469, - "narHash": "sha256-6FPUl7HVtvRHCCBQne7Ylp4p+dpP3P/OYuzjztZ4s70=", - "owner": "cachix", - "repo": "git-hooks.nix", - "rev": "f451c19376071a90d8c58ab1a953c6e9840527fd", - "type": "github" - }, - "original": { - "owner": "cachix", - "repo": "git-hooks.nix", - "type": "github" - } - }, "libgit2": { "flake": false, "locked": { - "lastModified": 1715853528, - "narHash": "sha256-J2rCxTecyLbbDdsyBWn9w7r3pbKRMkI9E7RvRgAqBdY=", + "lastModified": 1724328629, + "narHash": "sha256-7SuD4k+ORwFPwDm5Qr5eSV6GMVWjMfFed9KYi8riUQo=", "owner": "libgit2", "repo": "libgit2", - "rev": "36f7e21ad757a3dacc58cf7944329da6bc1d6e96", + "rev": "782e29c906f6e44b120843356f286b6a97d89f88", "type": "github" }, "original": { "owner": "libgit2", - "ref": "v1.8.1", "repo": "libgit2", "type": "github" } @@ -163,25 +131,26 @@ "inputs": { "flake-compat": "flake-compat", "flake-parts": "flake-parts_2", - "git-hooks-nix": "git-hooks-nix", "libgit2": "libgit2", "nixpkgs": [ "nixpkgs" ], "nixpkgs-23-11": "nixpkgs-23-11", - "nixpkgs-regression": "nixpkgs-regression" + "nixpkgs-regression": "nixpkgs-regression", + "pre-commit-hooks": "pre-commit-hooks" }, "locked": { - "lastModified": 1724439434, - "narHash": "sha256-3+UgAktTtkGUNpxMxr+q+R+z3r026L3PwJzG6RD2IXM=", + "lastModified": 1719448136, + "narHash": "sha256-ya0iofP+QysNzN7Gx7Btfe83ZW1YLpSdkccUNMnbBFQ=", "owner": "NixOS", "repo": "nix", - "rev": "85f1aa6b3df5c5fcc924a74e2a9cc8acea9ba0e1", + "rev": "ed129267dcd7dd2cce48c09b17aefd6cfc488bcd", "type": "github" }, "original": { "owner": "NixOS", "repo": "nix", + "rev": "ed129267dcd7dd2cce48c09b17aefd6cfc488bcd", "type": "github" } }, @@ -292,6 +261,37 @@ "type": "github" } }, + "pre-commit-hooks": { + "inputs": { + "flake-compat": [ + "nix" + ], + "gitignore": [ + "nix" + ], + "nixpkgs": [ + "nix", + "nixpkgs" + ], + "nixpkgs-stable": [ + "nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1724857454, + "narHash": "sha256-Qyl9Q4QMTLZnnBb/8OuQ9LSkzWjBU1T5l5zIzTxkkhk=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "4509ca64f1084e73bc7a721b20c669a8d4c5ebe6", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, "purescript-overlay": { "inputs": { "nixpkgs": [ diff --git a/flake.nix b/flake.nix index c67d7f1..a952e05 100644 --- a/flake.nix +++ b/flake.nix @@ -3,7 +3,7 @@ inputs = { flake-parts.url = "github:hercules-ci/flake-parts"; - nix.url = "github:NixOS/nix"; + nix.url = "github:NixOS/nix/ed129267dcd7dd2cce48c09b17aefd6cfc488bcd"; # 2.24-pre, before splitting libnixflake nix.inputs.nixpkgs.follows = "nixpkgs"; nix-cargo-integration.url = "github:yusdacra/nix-cargo-integration"; nix-cargo-integration.inputs.nixpkgs.follows = "nixpkgs"; diff --git a/rust/nix-c-raw/build.rs b/rust/nix-c-raw/build.rs index c544fe8..541ea9a 100644 --- a/rust/nix-c-raw/build.rs +++ b/rust/nix-c-raw/build.rs @@ -13,6 +13,7 @@ impl bindgen::callbacks::ParseCallbacks for StripNixPrefix { fn main() { // Tell cargo to invalidate the built crate whenever the wrapper changes println!("cargo:rerun-if-changed=include/nix-c-raw.h"); + // println!("cargo:rustc-link-lib=nixflake"); // https://rust-lang.github.io/rust-bindgen/library-usage.html let bindings = bindgen::Builder::default() diff --git a/rust/nix-expr/src/primop.rs b/rust/nix-expr/src/primop.rs index 9234954..9641abe 100644 --- a/rust/nix-expr/src/primop.rs +++ b/rust/nix-expr/src/primop.rs @@ -114,7 +114,7 @@ unsafe extern "C" fn function_adapter( CString::new("") .unwrap() }); - raw::set_err_msg(context_out, raw::err_NIX_ERR_UNKNOWN, cstr.as_ptr()); + raw::set_err_msg(context_out, raw::NIX_ERR_UNKNOWN, cstr.as_ptr()); }, } } diff --git a/rust/nix-util/src/context.rs b/rust/nix-util/src/context.rs index d4dbe94..dd0aecc 100644 --- a/rust/nix-util/src/context.rs +++ b/rust/nix-util/src/context.rs @@ -36,7 +36,7 @@ impl Context { /// We recommend to use `check_call!` if possible. pub fn check_err(&self) -> Result<()> { let err = unsafe { raw::err_code(self.inner.as_ptr()) }; - if err != raw::err_NIX_OK.try_into().unwrap() { + if err != raw::NIX_OK.try_into().unwrap() { // msgp is a borrowed pointer (pointing into the context), so we don't need to free it let msgp = unsafe { raw::err_msg(null_mut(), self.inner.as_ptr(), null_mut()) }; // Turn the i8 pointer into a Rust string by copying @@ -50,7 +50,7 @@ impl Context { unsafe { raw::set_err_msg( self.inner.as_ptr(), - raw::err_NIX_OK.try_into().unwrap(), + raw::NIX_OK.try_into().unwrap(), b"\0".as_ptr() as *const i8, ); } @@ -69,7 +69,7 @@ impl Context { f: F, ) -> Result> { let t = f(self.ptr()); - if unsafe { raw::err_code(self.inner.as_ptr()) == raw::err_NIX_ERR_KEY } { + if unsafe { raw::err_code(self.inner.as_ptr()) == raw::NIX_ERR_KEY } { self.clear(); return Ok(None); } @@ -112,7 +112,7 @@ macro_rules! check_call_opt_key { { let ctx : &mut $crate::context::Context = $ctx; let ret = $($f)::*(ctx.ptr(), $($arg,)*); - if unsafe { raw::err_code(ctx.ptr()) == raw::err_NIX_ERR_KEY } { + if unsafe { raw::err_code(ctx.ptr()) == raw::NIX_ERR_KEY } { ctx.clear(); return Ok(None); } @@ -143,7 +143,7 @@ mod tests { unsafe { raw::set_err_msg( ctx_ptr, - raw::err_NIX_ERR_UNKNOWN.try_into().unwrap(), + raw::NIX_ERR_UNKNOWN.try_into().unwrap(), b"dummy error message\0".as_ptr() as *const i8, ); } From 6f358cc1fbbb645c77f65b5d0ad7f035f3e0c878 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 25 Sep 2024 16:55:16 +0200 Subject: [PATCH 105/306] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'flake-parts': 'github:hercules-ci/flake-parts/af510d4a62d071ea13925ce41c95e3dec816c01d?narHash=sha256-ODYRm8zHfLTH3soTFWE452ydPYz2iTvr9T8ftDMUQ3E%3D' (2024-08-30) → 'github:hercules-ci/flake-parts/bcef6817a8b2aa20a5a6dbb19b43e63c5bf8619a?narHash=sha256-HO4zgY0ekfwO5bX0QH/3kJ/h4KvUDFZg8YpkNwIbg1U%3D' (2024-09-12) • Updated input 'flake-parts/nixpkgs-lib': 'https://github.com/NixOS/nixpkgs/archive/a5d394176e64ab29c852d03346c1fc9b0b7d33eb.tar.gz?narHash=sha256-uFf2QeW7eAHlYXuDktm9c25OxOyCoUOQmh5SZ9amE5Q%3D' (2024-08-01) → 'https://github.com/NixOS/nixpkgs/archive/356624c12086a18f2ea2825fed34523d60ccc4e3.tar.gz?narHash=sha256-Ss8QWLXdr2JCBPcYChJhz4xJm%2Bh/xjl4G0c0XlP6a74%3D' (2024-09-01) (cherry picked from commit 448b7152c4d44a3d9814fc09252b769006ebd7d6) --- flake.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/flake.lock b/flake.lock index f031e6b..330d1de 100644 --- a/flake.lock +++ b/flake.lock @@ -61,11 +61,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1725024810, - "narHash": "sha256-ODYRm8zHfLTH3soTFWE452ydPYz2iTvr9T8ftDMUQ3E=", + "lastModified": 1726153070, + "narHash": "sha256-HO4zgY0ekfwO5bX0QH/3kJ/h4KvUDFZg8YpkNwIbg1U=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "af510d4a62d071ea13925ce41c95e3dec816c01d", + "rev": "bcef6817a8b2aa20a5a6dbb19b43e63c5bf8619a", "type": "github" }, "original": { @@ -214,14 +214,14 @@ }, "nixpkgs-lib": { "locked": { - "lastModified": 1722555339, - "narHash": "sha256-uFf2QeW7eAHlYXuDktm9c25OxOyCoUOQmh5SZ9amE5Q=", + "lastModified": 1725233747, + "narHash": "sha256-Ss8QWLXdr2JCBPcYChJhz4xJm+h/xjl4G0c0XlP6a74=", "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/a5d394176e64ab29c852d03346c1fc9b0b7d33eb.tar.gz" + "url": "https://github.com/NixOS/nixpkgs/archive/356624c12086a18f2ea2825fed34523d60ccc4e3.tar.gz" }, "original": { "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/a5d394176e64ab29c852d03346c1fc9b0b7d33eb.tar.gz" + "url": "https://github.com/NixOS/nixpkgs/archive/356624c12086a18f2ea2825fed34523d60ccc4e3.tar.gz" } }, "nixpkgs-regression": { From b714f46e0797f89e72e2597f8d17a5fc700d4062 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 2 Oct 2024 14:50:47 +0200 Subject: [PATCH 106/306] feat: Make nixops4-eval async A step toward handling the arrival of new data (stdin) with priority over commands, avoiding roundtrips and re-evaluations. (cherry picked from commit 8a2a5197886025caf35653001f76a4b209d8c9e4) --- rust/Cargo.lock | 211 ++++++++++++++++++++++++++++++++++++ rust/nix-expr/src/value.rs | 1 + rust/nix-store/src/store.rs | 1 + 3 files changed, 213 insertions(+) diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 51e5dc9..a3d37de 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -2,6 +2,31 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +dependencies = [ + "lazy_static", + "regex", +] + +[[package]] +name = "addr2line" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "aho-corasick" version = "1.1.2" @@ -17,6 +42,38 @@ version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" +[[package]] +name = "async-trait" +version = "0.1.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.49", +] + +[[package]] +name = "backtrace" +version = "0.3.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "bindgen" version = "0.69.4" @@ -46,6 +103,21 @@ version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +[[package]] +name = "bytes" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" + +[[package]] +name = "cc" +version = "1.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812acba72f0a070b003d3697490d2b55b837230ae7c6c6497f05cc2ddbb8d938" +dependencies = [ + "shlex", +] + [[package]] name = "cexpr" version = "0.6.0" @@ -114,6 +186,24 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + +[[package]] +name = "gimli" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" + [[package]] name = "glob" version = "0.3.1" @@ -190,6 +280,27 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "miniz_oxide" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +dependencies = [ + "adler", +] + +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", +] + [[package]] name = "nix-c-raw" version = "0.1.0" @@ -241,6 +352,47 @@ dependencies = [ [[package]] name = "nixops4-core" version = "0.1.0" +dependencies = [ + "anyhow", + "serde", + "serde_json", +] + +[[package]] +name = "nixops4-resource" +version = "0.1.0" +dependencies = [ + "anyhow", + "nix", + "schemafy", + "serde", + "serde_json", +] + +[[package]] +name = "nixops4-resource-runner" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "clap-markdown", + "clap_complete", + "clap_derive", + "clap_mangen", + "nixops4-resource", + "serde", + "serde_json", +] + +[[package]] +name = "nixops4-resources-local" +version = "0.1.0" +dependencies = [ + "anyhow", + "nixops4-resource", + "serde", + "serde_json", +] [[package]] name = "nom" @@ -252,12 +404,27 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "object" +version = "0.36.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + [[package]] name = "pkg-config" version = "0.3.30" @@ -321,6 +488,27 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "roff" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88f8660c1ff60292143c98d08fc6e2f654d722db50410e3f3797d40baaf9d8f3" + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + [[package]] name = "rustc-hash" version = "1.1.0" @@ -369,6 +557,29 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "tokio" +version = "1.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +dependencies = [ + "backtrace", + "bytes", + "pin-project-lite", + "tokio-macros", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.49", +] + [[package]] name = "unicode-ident" version = "1.0.12" diff --git a/rust/nix-expr/src/value.rs b/rust/nix-expr/src/value.rs index e15d5b9..1781e23 100644 --- a/rust/nix-expr/src/value.rs +++ b/rust/nix-expr/src/value.rs @@ -53,6 +53,7 @@ impl ValueType { pub struct Value { inner: NonNull, } +unsafe impl Send for Value {} impl Value { /// Take ownership of a new Value. /// diff --git a/rust/nix-store/src/store.rs b/rust/nix-store/src/store.rs index 631a6f5..54fda9c 100644 --- a/rust/nix-store/src/store.rs +++ b/rust/nix-store/src/store.rs @@ -32,6 +32,7 @@ impl Drop for StoreRef { } } } +unsafe impl Send for StoreRef {} /// A [Weak] reference to a store. pub struct StoreWeak { From f177507f8887240d6ac4ca72e32c2f578613ebe7 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 9 Oct 2024 00:53:11 +0200 Subject: [PATCH 107/306] feat: Add ThreadRegistrationGuard in nix-expr (cherry picked from commit f287122e354535c2ee2f16c930038b19142f522a) --- rust/nix-expr/src/eval_state.rs | 50 ++++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 5851fec..6a3718b 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -470,30 +470,32 @@ pub fn gc_now() { } } +pub struct ThreadRegistrationGuard { + must_unregister: bool, +} +impl Drop for ThreadRegistrationGuard { + fn drop(&mut self) { + if self.must_unregister { + unsafe { + raw::GC_unregister_my_thread(); + } + } + } +} + /// Run a function while making sure that the current thread is registered with the GC. pub fn gc_registering_current_thread(f: F) -> Result where F: FnOnce() -> R, { - init()?; - if unsafe { raw::GC_thread_is_registered() } != 0 { - return Ok(f()); - } else { - gc_register_my_thread()?; - let r = f(); - unsafe { - raw::GC_unregister_my_thread(); - } - return Ok(r); - } + let guard = gc_register_my_thread()?; + let r = f(); + drop(guard); + Ok(r) } -pub fn gc_register_my_thread() -> Result<()> { +fn gc_register_my_thread_do_it() -> Result<()> { unsafe { - let already_done = raw::GC_thread_is_registered(); - if already_done != 0 { - return Ok(()); - } let mut sb: raw::GC_stack_base = raw::GC_stack_base { mem_base: null_mut(), }; @@ -506,6 +508,22 @@ pub fn gc_register_my_thread() -> Result<()> { } } +pub fn gc_register_my_thread() -> Result { + init()?; + unsafe { + let already_done = raw::GC_thread_is_registered(); + if already_done != 0 { + return Ok(ThreadRegistrationGuard { + must_unregister: false, + }); + } + gc_register_my_thread_do_it()?; + Ok(ThreadRegistrationGuard { + must_unregister: true, + }) + } +} + impl Clone for EvalState { fn clone(&self) -> Self { EvalState { From ad0f5d0240b72c2a5bd793495f45734b23e349f3 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 9 Oct 2024 00:54:13 +0200 Subject: [PATCH 108/306] fix: Remove impl Send for Value for now They're somewhat safe to use on a different thread, but we don't need to for now. By removing this, we'll be made aware as needed. (cherry picked from commit 2e953d0a1268e2f19671fdbc9e721fc630ac346b) --- rust/nix-expr/src/value.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/rust/nix-expr/src/value.rs b/rust/nix-expr/src/value.rs index 1781e23..e15d5b9 100644 --- a/rust/nix-expr/src/value.rs +++ b/rust/nix-expr/src/value.rs @@ -53,7 +53,6 @@ impl ValueType { pub struct Value { inner: NonNull, } -unsafe impl Send for Value {} impl Value { /// Take ownership of a new Value. /// From 4eade817b6c458a9d49a7e63fc760ca7a8e3d5da Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 16 Oct 2024 12:14:37 +0200 Subject: [PATCH 109/306] refact: Remove gc_registering_current_thread Closes #31 A guard object is more capable, as it can be used in various control flow and ownership schemes, including async code, but not that it is not Send. (cherry picked from commit f9aa5eab2561834c64ef9fe01979a91aee35848f) --- rust/nix-expr/src/eval_state.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 6a3718b..de5550f 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -483,17 +483,6 @@ impl Drop for ThreadRegistrationGuard { } } -/// Run a function while making sure that the current thread is registered with the GC. -pub fn gc_registering_current_thread(f: F) -> Result -where - F: FnOnce() -> R, -{ - let guard = gc_register_my_thread()?; - let r = f(); - drop(guard); - Ok(r) -} - fn gc_register_my_thread_do_it() -> Result<()> { unsafe { let mut sb: raw::GC_stack_base = raw::GC_stack_base { @@ -566,6 +555,17 @@ mod tests { test_init(); } + /// Run a function while making sure that the current thread is registered with the GC. + pub fn gc_registering_current_thread(f: F) -> Result + where + F: FnOnce() -> R, + { + let guard = gc_register_my_thread()?; + let r = f(); + drop(guard); + Ok(r) + } + #[test] fn eval_state_new_and_drop() { gc_registering_current_thread(|| { From c5692d05886cba2673e32de2a3a4e0160e0fc7d7 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 24 Oct 2024 12:23:27 +0200 Subject: [PATCH 110/306] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nix-cargo-integration': 'github:yusdacra/nix-cargo-integration/3a8e3bb661db28522aa2d4a55f1fccf9f95ec33e?narHash=sha256-fjwO6Pv3d35F6UErY42hc7zXJr6ek0LhSZlgEu%2BeI04%3D' (2024-08-23) → 'github:yusdacra/nix-cargo-integration/d4f92d8c201479762727991bb290da18129b858c?narHash=sha256-87MJq1yxwAcAK/aKSaJ0i4HpaorweyiwRY6kNzsc7v8%3D' (2024-10-24) • Updated input 'nix-cargo-integration/crane': 'github:ipetkov/crane/d535642bbe6f377077f7c23f0febb78b1463f449?narHash=sha256-xpW3VFUG7yE6UE6Wl0dhqencuENSkV7qpnpe9I8VbPw%3D' (2023-11-05) → 'github:ipetkov/crane/5b03654ce046b5167e7b0bccbd8244cb56c16f0e?narHash=sha256-/mumx8AQ5xFuCJqxCIOFCHTVlxHkMT21idpbgbm/TIE%3D' (2024-09-26) • Updated input 'nix-cargo-integration/dream2nix': 'github:nix-community/dream2nix/3fd4c14d3683baac8d1f94286ae14fe160888b51?narHash=sha256-fFS8aDnfK9Qfm2FLnQ8pqWk8FzvFEv5LvTuZTZLREnc%3D' (2024-08-01) → 'github:nix-community/dream2nix/c6935471f7e1a9e190aaa9ac9823dca34e00d92a?narHash=sha256-rhx5SYpIkPu7d%2BrjF9FGGBVxS0BwAEkmYIsJg2a3E20%3D' (2024-10-10) • Updated input 'nix-cargo-integration/dream2nix/purescript-overlay': 'github:thomashoneyman/purescript-overlay/047c7933abd6da8aa239904422e22d190ce55ead?narHash=sha256-eMjFmsj2G1E0Q5XiibUNgFjTiSz0GxIeSSzzVdoN730%3D' (2023-09-29) → 'github:thomashoneyman/purescript-overlay/988b09676c2a0e6a46dfa3589aa6763c90476b8a?narHash=sha256-TIw%2Bsac0NX0FeAneud%2BsQZT%2Bql1G/WEb7/Vb436rUXM%3D' (2024-08-24) • Added input 'nix-cargo-integration/dream2nix/purescript-overlay/flake-compat': 'github:edolstra/flake-compat/0f9255e01c2351cc7d116c072cb317785dd33b33?narHash=sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U%3D' (2023-10-04) • Updated input 'nix-cargo-integration/dream2nix/purescript-overlay/slimlock': 'github:thomashoneyman/slimlock/b5c6cdcaf636ebbebd0a1f32520929394493f1a6?narHash=sha256-Wg0ViDotFWGWqKIQzyYCgayeH8s4U1OZcTiWTQYdAp4%3D' (2023-07-06) → 'github:thomashoneyman/slimlock/cf72723f59e2340d24881fd7bf61cb113b4c407c?narHash=sha256-xzkkMv3neJJJ89zo3o2ojp7nFeaZc2G0fYwNXNJRFlo%3D' (2023-07-07) • Updated input 'nix-cargo-integration/parts': 'github:hercules-ci/flake-parts/8471fe90ad337a8074e957b69ca4d0089218391d?narHash=sha256-XOQkdLafnb/p9ij77byFQjDf5m5QYl9b2REiVClC%2Bx4%3D' (2024-08-01) → 'github:hercules-ci/flake-parts/3d04084d54bedc3d6b8b736c70ef449225c361b1?narHash=sha256-K5ZLCyfO/Zj9mPFldf3iwS6oZStJcU4tSpiXTMYaaL0%3D' (2024-10-01) • Updated input 'nix-cargo-integration/rust-overlay': 'github:oxalica/rust-overlay/a18034322c7703fcfe5d7352a77981ba4a936a61?narHash=sha256-%2BCFDh1FUgyY7q0FiWhKJpHS7LlD3KbiqN5Z4Z%2B4bGmc%3D' (2024-08-23) → 'github:oxalica/rust-overlay/29b1275740d9283467b8117499ec8cbb35250584?narHash=sha256-Rb6JUop7NRklg0uzcre%2BA%2BEbrn/ZiQPkm4QdKg6/3pw%3D' (2024-10-24) • Updated input 'nix-cargo-integration/treefmt': 'github:numtide/treefmt-nix/070f834771efa715f3e74cd8ab93ecc96fabc951?narHash=sha256-kKJtaiU5Ou%2Be/0Qs7SICXF22DLx4V/WhG1P6%2Bk4yeOE%3D' (2024-08-22) → 'github:numtide/treefmt-nix/aac86347fb5063960eccb19493e0cadcdb4205ca?narHash=sha256-XGOvuIPW1XRfPgHtGYXd5MAmJzZtOuwlfKDgxX5KT3s%3D' (2024-10-22) (cherry picked from commit 3389f8f935c29ee7aed0f6c3d0015293bac614b8) --- flake.lock | 67 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 42 insertions(+), 25 deletions(-) diff --git a/flake.lock b/flake.lock index 330d1de..e480107 100644 --- a/flake.lock +++ b/flake.lock @@ -3,16 +3,16 @@ "crane": { "flake": false, "locked": { - "lastModified": 1699217310, - "narHash": "sha256-xpW3VFUG7yE6UE6Wl0dhqencuENSkV7qpnpe9I8VbPw=", + "lastModified": 1727316705, + "narHash": "sha256-/mumx8AQ5xFuCJqxCIOFCHTVlxHkMT21idpbgbm/TIE=", "owner": "ipetkov", "repo": "crane", - "rev": "d535642bbe6f377077f7c23f0febb78b1463f449", + "rev": "5b03654ce046b5167e7b0bccbd8244cb56c16f0e", "type": "github" }, "original": { "owner": "ipetkov", - "ref": "v0.15.0", + "ref": "v0.19.0", "repo": "crane", "type": "github" } @@ -27,11 +27,11 @@ "pyproject-nix": "pyproject-nix" }, "locked": { - "lastModified": 1722526955, - "narHash": "sha256-fFS8aDnfK9Qfm2FLnQ8pqWk8FzvFEv5LvTuZTZLREnc=", + "lastModified": 1728585693, + "narHash": "sha256-rhx5SYpIkPu7d+rjF9FGGBVxS0BwAEkmYIsJg2a3E20=", "owner": "nix-community", "repo": "dream2nix", - "rev": "3fd4c14d3683baac8d1f94286ae14fe160888b51", + "rev": "c6935471f7e1a9e190aaa9ac9823dca34e00d92a", "type": "github" }, "original": { @@ -56,6 +56,22 @@ "type": "github" } }, + "flake-compat_2": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, "flake-parts": { "inputs": { "nixpkgs-lib": "nixpkgs-lib" @@ -167,11 +183,11 @@ "treefmt": "treefmt" }, "locked": { - "lastModified": 1724393640, - "narHash": "sha256-fjwO6Pv3d35F6UErY42hc7zXJr6ek0LhSZlgEu+eI04=", + "lastModified": 1729750574, + "narHash": "sha256-87MJq1yxwAcAK/aKSaJ0i4HpaorweyiwRY6kNzsc7v8=", "owner": "yusdacra", "repo": "nix-cargo-integration", - "rev": "3a8e3bb661db28522aa2d4a55f1fccf9f95ec33e", + "rev": "d4f92d8c201479762727991bb290da18129b858c", "type": "github" }, "original": { @@ -248,11 +264,11 @@ ] }, "locked": { - "lastModified": 1722555600, - "narHash": "sha256-XOQkdLafnb/p9ij77byFQjDf5m5QYl9b2REiVClC+x4=", + "lastModified": 1727826117, + "narHash": "sha256-K5ZLCyfO/Zj9mPFldf3iwS6oZStJcU4tSpiXTMYaaL0=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "8471fe90ad337a8074e957b69ca4d0089218391d", + "rev": "3d04084d54bedc3d6b8b736c70ef449225c361b1", "type": "github" }, "original": { @@ -294,6 +310,7 @@ }, "purescript-overlay": { "inputs": { + "flake-compat": "flake-compat_2", "nixpkgs": [ "nix-cargo-integration", "dream2nix", @@ -302,11 +319,11 @@ "slimlock": "slimlock" }, "locked": { - "lastModified": 1696022621, - "narHash": "sha256-eMjFmsj2G1E0Q5XiibUNgFjTiSz0GxIeSSzzVdoN730=", + "lastModified": 1724504251, + "narHash": "sha256-TIw+sac0NX0FeAneud+sQZT+ql1G/WEb7/Vb436rUXM=", "owner": "thomashoneyman", "repo": "purescript-overlay", - "rev": "047c7933abd6da8aa239904422e22d190ce55ead", + "rev": "988b09676c2a0e6a46dfa3589aa6763c90476b8a", "type": "github" }, "original": { @@ -343,11 +360,11 @@ "rust-overlay": { "flake": false, "locked": { - "lastModified": 1724379657, - "narHash": "sha256-+CFDh1FUgyY7q0FiWhKJpHS7LlD3KbiqN5Z4Z+4bGmc=", + "lastModified": 1729736953, + "narHash": "sha256-Rb6JUop7NRklg0uzcre+A+Ebrn/ZiQPkm4QdKg6/3pw=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "a18034322c7703fcfe5d7352a77981ba4a936a61", + "rev": "29b1275740d9283467b8117499ec8cbb35250584", "type": "github" }, "original": { @@ -366,11 +383,11 @@ ] }, "locked": { - "lastModified": 1688610262, - "narHash": "sha256-Wg0ViDotFWGWqKIQzyYCgayeH8s4U1OZcTiWTQYdAp4=", + "lastModified": 1688756706, + "narHash": "sha256-xzkkMv3neJJJ89zo3o2ojp7nFeaZc2G0fYwNXNJRFlo=", "owner": "thomashoneyman", "repo": "slimlock", - "rev": "b5c6cdcaf636ebbebd0a1f32520929394493f1a6", + "rev": "cf72723f59e2340d24881fd7bf61cb113b4c407c", "type": "github" }, "original": { @@ -387,11 +404,11 @@ ] }, "locked": { - "lastModified": 1724338379, - "narHash": "sha256-kKJtaiU5Ou+e/0Qs7SICXF22DLx4V/WhG1P6+k4yeOE=", + "lastModified": 1729613947, + "narHash": "sha256-XGOvuIPW1XRfPgHtGYXd5MAmJzZtOuwlfKDgxX5KT3s=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "070f834771efa715f3e74cd8ab93ecc96fabc951", + "rev": "aac86347fb5063960eccb19493e0cadcdb4205ca", "type": "github" }, "original": { From 676120cd6a8d63925101619eeba37eb6699938cc Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 26 Nov 2024 13:42:34 +0100 Subject: [PATCH 111/306] doc: Set flake description (cherry picked from commit 6d5f5e5b4cd0f5b5118d619adbeae7bc335c02f3) --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index a952e05..04452e2 100644 --- a/flake.nix +++ b/flake.nix @@ -1,5 +1,5 @@ { - description = "A flake with pre-commit hooks"; + description = "Rust bindings for the Nix C API"; inputs = { flake-parts.url = "github:hercules-ci/flake-parts"; From d40bbbed888f79f839f51091249cc1a50010a75e Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 26 Nov 2024 13:58:04 +0100 Subject: [PATCH 112/306] maint: Update Nix (cherry picked from commit 203f5d519369235097fef2bdaefa6b3d0f5e963b) --- flake.lock | 8 ++++---- flake.nix | 2 +- rust/Cargo.lock | 15 +++++++++++++++ rust/Cargo.toml | 1 + rust/nix-c-raw/build.rs | 4 ++-- rust/nix-c-raw/include/nix-c-raw.h | 1 + rust/nix-expr/src/primop.rs | 2 +- rust/nix-flake/Cargo.toml | 18 ++++++++++++++++++ rust/nix-flake/src/lib.rs | 26 ++++++++++++++++++++++++++ rust/nix-util/src/context.rs | 10 +++++----- 10 files changed, 74 insertions(+), 13 deletions(-) create mode 100644 rust/nix-flake/Cargo.toml create mode 100644 rust/nix-flake/src/lib.rs diff --git a/flake.lock b/flake.lock index e480107..3dbe5a6 100644 --- a/flake.lock +++ b/flake.lock @@ -156,17 +156,17 @@ "pre-commit-hooks": "pre-commit-hooks" }, "locked": { - "lastModified": 1719448136, - "narHash": "sha256-ya0iofP+QysNzN7Gx7Btfe83ZW1YLpSdkccUNMnbBFQ=", + "lastModified": 1732892090, + "narHash": "sha256-Ka/uNdaqpTAiVL++4MPHg8fG5o1tiJeY6G2t5UiKhd8=", "owner": "NixOS", "repo": "nix", - "rev": "ed129267dcd7dd2cce48c09b17aefd6cfc488bcd", + "rev": "64000481168d1da9d2519f055dd1fdee22275c21", "type": "github" }, "original": { "owner": "NixOS", + "ref": "master", "repo": "nix", - "rev": "ed129267dcd7dd2cce48c09b17aefd6cfc488bcd", "type": "github" } }, diff --git a/flake.nix b/flake.nix index 04452e2..63d7b5a 100644 --- a/flake.nix +++ b/flake.nix @@ -3,7 +3,7 @@ inputs = { flake-parts.url = "github:hercules-ci/flake-parts"; - nix.url = "github:NixOS/nix/ed129267dcd7dd2cce48c09b17aefd6cfc488bcd"; # 2.24-pre, before splitting libnixflake + nix.url = "github:NixOS/nix/master"; nix.inputs.nixpkgs.follows = "nixpkgs"; nix-cargo-integration.url = "github:yusdacra/nix-cargo-integration"; nix-cargo-integration.inputs.nixpkgs.follows = "nixpkgs"; diff --git a/rust/Cargo.lock b/rust/Cargo.lock index a3d37de..c6ce2f5 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -323,6 +323,21 @@ dependencies = [ "tempfile", ] +[[package]] +name = "nix-flake" +version = "0.1.0" +dependencies = [ + "anyhow", + "cstr", + "ctor", + "lazy_static", + "nix-c-raw", + "nix-expr", + "nix-store", + "nix-util", + "tempfile", +] + [[package]] name = "nix-store" version = "0.1.0" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 011675b..d234fbd 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ "nix-c-raw", + "nix-flake", "nix-expr", "nix-util", "nix-store", diff --git a/rust/nix-c-raw/build.rs b/rust/nix-c-raw/build.rs index 541ea9a..5f9f7b0 100644 --- a/rust/nix-c-raw/build.rs +++ b/rust/nix-c-raw/build.rs @@ -13,7 +13,7 @@ impl bindgen::callbacks::ParseCallbacks for StripNixPrefix { fn main() { // Tell cargo to invalidate the built crate whenever the wrapper changes println!("cargo:rerun-if-changed=include/nix-c-raw.h"); - // println!("cargo:rustc-link-lib=nixflake"); + println!("cargo:rustc-link-lib=nixflake"); // https://rust-lang.github.io/rust-bindgen/library-usage.html let bindings = bindgen::Builder::default() @@ -39,7 +39,7 @@ fn main() { fn c_headers() -> Vec { let mut args = Vec::new(); // args.push("-isystem".to_string()); - for path in pkg_config::probe_library("nix-expr-c") + for path in pkg_config::probe_library("nix-flake-c") .unwrap() .include_paths .iter() diff --git a/rust/nix-c-raw/include/nix-c-raw.h b/rust/nix-c-raw/include/nix-c-raw.h index 14fb03c..88e252f 100644 --- a/rust/nix-c-raw/include/nix-c-raw.h +++ b/rust/nix-c-raw/include/nix-c-raw.h @@ -4,3 +4,4 @@ #include #include #include +#include diff --git a/rust/nix-expr/src/primop.rs b/rust/nix-expr/src/primop.rs index 9641abe..9234954 100644 --- a/rust/nix-expr/src/primop.rs +++ b/rust/nix-expr/src/primop.rs @@ -114,7 +114,7 @@ unsafe extern "C" fn function_adapter( CString::new("") .unwrap() }); - raw::set_err_msg(context_out, raw::NIX_ERR_UNKNOWN, cstr.as_ptr()); + raw::set_err_msg(context_out, raw::err_NIX_ERR_UNKNOWN, cstr.as_ptr()); }, } } diff --git a/rust/nix-flake/Cargo.toml b/rust/nix-flake/Cargo.toml new file mode 100644 index 0000000..c2328ce --- /dev/null +++ b/rust/nix-flake/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "nix-flake" +version = "0.1.0" +edition = "2021" + +[lib] +path = "src/lib.rs" + +[dependencies] +anyhow = "1.0.79" +nix-expr = { path = "../nix-expr" } +nix-store = { path = "../nix-store" } +nix-util = { path = "../nix-util" } +nix-c-raw = { path = "../nix-c-raw" } +lazy_static = "1.4.0" +ctor = "0.2.7" +tempfile = "3.10.1" +cstr = "0.2.12" diff --git a/rust/nix-flake/src/lib.rs b/rust/nix-flake/src/lib.rs new file mode 100644 index 0000000..359b2c7 --- /dev/null +++ b/rust/nix-flake/src/lib.rs @@ -0,0 +1,26 @@ +use anyhow::Result; +use nix_c_raw as raw; +use nix_util::context::{self, Context}; + +pub struct FlakeSettings { + pub(crate) ptr: *mut raw::flake_settings, +} +impl Drop for FlakeSettings { + fn drop(&mut self) { + unsafe { + raw::flake_settings_free(self.ptr); + } + } +} +impl FlakeSettings { + pub fn new() -> Result { + let mut ctx = Context::new(); + let s = unsafe { context::check_call!(raw::flake_settings_new(&mut ctx)) }?; + Ok(FlakeSettings { ptr: s }) + } + pub fn init_globally(&mut self) -> Result<()> { + let mut ctx = Context::new(); + unsafe { context::check_call!(raw::flake_init_global(&mut ctx, self.ptr)) }?; + Ok(()) + } +} diff --git a/rust/nix-util/src/context.rs b/rust/nix-util/src/context.rs index dd0aecc..d4dbe94 100644 --- a/rust/nix-util/src/context.rs +++ b/rust/nix-util/src/context.rs @@ -36,7 +36,7 @@ impl Context { /// We recommend to use `check_call!` if possible. pub fn check_err(&self) -> Result<()> { let err = unsafe { raw::err_code(self.inner.as_ptr()) }; - if err != raw::NIX_OK.try_into().unwrap() { + if err != raw::err_NIX_OK.try_into().unwrap() { // msgp is a borrowed pointer (pointing into the context), so we don't need to free it let msgp = unsafe { raw::err_msg(null_mut(), self.inner.as_ptr(), null_mut()) }; // Turn the i8 pointer into a Rust string by copying @@ -50,7 +50,7 @@ impl Context { unsafe { raw::set_err_msg( self.inner.as_ptr(), - raw::NIX_OK.try_into().unwrap(), + raw::err_NIX_OK.try_into().unwrap(), b"\0".as_ptr() as *const i8, ); } @@ -69,7 +69,7 @@ impl Context { f: F, ) -> Result> { let t = f(self.ptr()); - if unsafe { raw::err_code(self.inner.as_ptr()) == raw::NIX_ERR_KEY } { + if unsafe { raw::err_code(self.inner.as_ptr()) == raw::err_NIX_ERR_KEY } { self.clear(); return Ok(None); } @@ -112,7 +112,7 @@ macro_rules! check_call_opt_key { { let ctx : &mut $crate::context::Context = $ctx; let ret = $($f)::*(ctx.ptr(), $($arg,)*); - if unsafe { raw::err_code(ctx.ptr()) == raw::NIX_ERR_KEY } { + if unsafe { raw::err_code(ctx.ptr()) == raw::err_NIX_ERR_KEY } { ctx.clear(); return Ok(None); } @@ -143,7 +143,7 @@ mod tests { unsafe { raw::set_err_msg( ctx_ptr, - raw::NIX_ERR_UNKNOWN.try_into().unwrap(), + raw::err_NIX_ERR_UNKNOWN.try_into().unwrap(), b"dummy error message\0".as_ptr() as *const i8, ); } From 77c9be2cbf7ec7ff6ed363bfa45ff6e443de7aff Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 26 Nov 2024 14:52:25 +0100 Subject: [PATCH 113/306] maint: Fix nix-expr tests by unsetting build hook (cherry picked from commit 52b6cd6b7867dafe0906184e540c5195ba776ae9) --- rust/nix-expr/src/eval_state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index de5550f..30eb91a 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -532,7 +532,7 @@ pub fn test_init() { // would cause the test suite to reinvokes itself, causing an infinite loop. // While _NIX_TEST_NO_SANDBOX=1 should prevent this, we may also set the // build hook to "" to prevent this. - // settings::set("build-hook", "")?; + nix_util::settings::set("build-hook", "").unwrap(); // When testing in the sandbox, the default build dir would be a parent of the storeDir, // which causes an error. So we set a custom build dir here. From 1c0e2cd72f3344a4e0a6d6d63784892fcd8bc8dc Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 26 Nov 2024 14:52:49 +0100 Subject: [PATCH 114/306] maint: Unset substituters in nix-expr tests Runs offline, and would only slow things down. (cherry picked from commit ea3ef1c59a1fe8e380302acc1885877a1ac8e0b6) --- rust/nix-expr/src/eval_state.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 30eb91a..61cfde8 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -538,6 +538,9 @@ pub fn test_init() { // which causes an error. So we set a custom build dir here. nix_util::settings::set("sandbox-build-dir", "/custom-build-dir-for-test").unwrap(); std::env::set_var("_NIX_TEST_NO_SANDBOX", "1"); + + // The tests run offline + nix_util::settings::set("substituters", "").unwrap(); } #[cfg(test)] From 12d3d62108f0967de7385b8278ebb431d2e29096 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 27 Nov 2024 14:13:54 +0100 Subject: [PATCH 115/306] fix: Deduplicate stores to work around nix#11979 Fixes tests hanging. Before this commit: nix build .#packages.x86_64-linux.nixops4-eval-release See https://github.com/NixOS/nix/issues/11979 (cherry picked from commit 03af71f92488f2ee683565318f24afd3e3c001df) --- rust/nix-expr/src/eval_state.rs | 3 +- rust/nix-store/src/store.rs | 53 +++++++++++++++++++++++++++++++-- 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 61cfde8..cfa39a3 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -594,7 +594,8 @@ mod tests { fn weak_ref_gone() { gc_registering_current_thread(|| { let weak = { - let store = Store::open("auto", HashMap::new()).unwrap(); + // Use a slightly different URL which is unique in the test suite, to bypass the global store cache + let store = Store::open("auto?foo=bar", HashMap::new()).unwrap(); let es = EvalState::new(store, []).unwrap(); es.weak_ref() }; diff --git a/rust/nix-store/src/store.rs b/rust/nix-store/src/store.rs index 54fda9c..87295d2 100644 --- a/rust/nix-store/src/store.rs +++ b/rust/nix-store/src/store.rs @@ -1,13 +1,14 @@ -use anyhow::{bail, Result}; +use anyhow::{bail, Error, Result}; use lazy_static::lazy_static; use nix_c_raw as raw; use nix_util::context::Context; use nix_util::string_return::{callback_get_result_string, callback_get_result_string_data}; use nix_util::{check_call, result_string_init}; +use std::collections::HashMap; use std::ffi::{c_char, CString}; use std::ptr::null_mut; use std::ptr::NonNull; -use std::sync::{Arc, Weak}; +use std::sync::{Arc, Mutex, Weak}; /* TODO make Nix itself thread safe */ lazy_static! { @@ -33,6 +34,8 @@ impl Drop for StoreRef { } } unsafe impl Send for StoreRef {} +/// Unlike pointers in general, operations on raw::Store are thread safe and it is therefore safe to share them between threads. +unsafe impl Sync for StoreRef {} /// A [Weak] reference to a store. pub struct StoreWeak { @@ -50,6 +53,13 @@ impl StoreWeak { } } +/// Protects against https://github.com/NixOS/nix/issues/11979 (unless different parameters are passed, in which case it's up to luck, but you do get your own parameters as you asked for). +type StoreCacheMap = HashMap<(String, Vec<(String, String)>), StoreWeak>; + +lazy_static! { + static ref STORE_CACHE: Arc> = Arc::new(Mutex::new(HashMap::new())); +} + pub struct Store { inner: Arc, /* An error context to reuse. This way we don't have to allocate them for each store operation. */ @@ -59,6 +69,41 @@ impl Store { pub fn open<'a, 'b>( url: &str, params: impl IntoIterator, + ) -> Result { + let params = params + .into_iter() + .map(|(k, v)| (k.to_owned(), v.to_owned())) + .collect::>(); + let params2 = params.clone(); + let mut store_cache = STORE_CACHE + .lock() + .map_err(|_| Error::msg("Failed to lock store cache. This should never happen."))?; + match store_cache.entry((url.to_string(), params)) { + std::collections::hash_map::Entry::Occupied(mut e) => { + if let Some(store) = e.get().upgrade() { + Ok(store) + } else { + let store = Self::open_uncached( + url, + params2.iter().map(|(k, v)| (k.as_str(), v.as_str())), + )?; + e.insert(store.weak_ref()); + Ok(store) + } + } + std::collections::hash_map::Entry::Vacant(e) => { + let store = Self::open_uncached( + url, + params2.iter().map(|(k, v)| (k.as_str(), v.as_str())), + )?; + e.insert(store.weak_ref()); + Ok(store) + } + } + } + fn open_uncached<'a, 'b>( + url: &str, + params: impl IntoIterator, ) -> Result { let x = INIT.as_ref(); match x { @@ -191,7 +236,9 @@ mod tests { #[test] fn weak_ref_gone() { let weak = { - let store = Store::open("auto", HashMap::new()).unwrap(); + // Concurrent tests calling Store::open will keep the weak reference to auto alive, + // so for this test we need to bypass the global cache. + let store = Store::open_uncached("auto", HashMap::new()).unwrap(); store.weak_ref() }; assert!(weak.upgrade().is_none()); From 1e3cce774278758ccdcac137d41eccdf2e3071e2 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 27 Nov 2024 10:26:39 +0100 Subject: [PATCH 116/306] refact: Resolve some clippy warnings (cherry picked from commit dc4bfaa993eb2d712d53f70551db9ac34b0cda08) --- rust/nix-c-raw/build.rs | 1 - rust/nix-expr/src/eval_state.rs | 4 ++-- rust/nix-expr/src/primop.rs | 5 +---- rust/nix-util/src/context.rs | 9 ++++----- rust/nix-util/src/string_return.rs | 10 ++++++++-- 5 files changed, 15 insertions(+), 14 deletions(-) diff --git a/rust/nix-c-raw/build.rs b/rust/nix-c-raw/build.rs index 5f9f7b0..5dbdbc7 100644 --- a/rust/nix-c-raw/build.rs +++ b/rust/nix-c-raw/build.rs @@ -1,4 +1,3 @@ -use bindgen; use std::env; use std::path::PathBuf; diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index cfa39a3..12689e7 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -52,8 +52,8 @@ impl EvalStateWeak { pub fn upgrade(&self) -> Option { self.inner.upgrade().and_then(|eval_state| { self.store.upgrade().map(|store| EvalState { - eval_state: eval_state, - store: store, + eval_state, + store, context: Context::new(), }) }) diff --git a/rust/nix-expr/src/primop.rs b/rust/nix-expr/src/primop.rs index 9234954..2d2e2d2 100644 --- a/rust/nix-expr/src/primop.rs +++ b/rust/nix-expr/src/primop.rs @@ -55,10 +55,7 @@ impl PrimOp { // TODO: Use the GC with finalizer, if possible. let user_data = ManuallyDrop::new(Box::new(PrimOpContext { arity: N, - function: Box::new(move |eval_state, args| { - let r = f(eval_state, args.try_into().unwrap()); - r - }), + function: Box::new(move |eval_state, args| f(eval_state, args.try_into().unwrap())), eval_state: eval_state.weak_ref(), })); user_data.as_ref() as *const PrimOpContext as *mut c_void diff --git a/rust/nix-util/src/context.rs b/rust/nix-util/src/context.rs index d4dbe94..8e12a8b 100644 --- a/rust/nix-util/src/context.rs +++ b/rust/nix-util/src/context.rs @@ -18,10 +18,9 @@ impl Context { // We're almost certainly going to crash anyways. panic!("nix_c_context_create returned a null pointer"); } - let ctx = Context { + Context { inner: NonNull::new(ctx).unwrap(), - }; - ctx + } } /// Access the C context pointer. @@ -36,7 +35,7 @@ impl Context { /// We recommend to use `check_call!` if possible. pub fn check_err(&self) -> Result<()> { let err = unsafe { raw::err_code(self.inner.as_ptr()) }; - if err != raw::err_NIX_OK.try_into().unwrap() { + if err != raw::err_NIX_OK { // msgp is a borrowed pointer (pointing into the context), so we don't need to free it let msgp = unsafe { raw::err_msg(null_mut(), self.inner.as_ptr(), null_mut()) }; // Turn the i8 pointer into a Rust string by copying @@ -50,7 +49,7 @@ impl Context { unsafe { raw::set_err_msg( self.inner.as_ptr(), - raw::err_NIX_OK.try_into().unwrap(), + raw::err_NIX_OK, b"\0".as_ptr() as *const i8, ); } diff --git a/rust/nix-util/src/string_return.rs b/rust/nix-util/src/string_return.rs index f06fa32..2ccbc11 100644 --- a/rust/nix-util/src/string_return.rs +++ b/rust/nix-util/src/string_return.rs @@ -5,6 +5,12 @@ use anyhow::Result; /// This function is used by the other nix_* crates, and you should never need to call it yourself. /// /// Some functions in the nix library "return" strings without giving you ownership over them, by letting you pass a callback function that gets to look at that string. This callback simply turns that string pointer into an owned rust String. +/// +/// # Safety +/// +/// _Manual memory management_ +/// +/// Only for passing to the nix C API. Do not call this function directly. pub unsafe extern "C" fn callback_get_result_string( start: *const ::std::os::raw::c_char, n: std::os::raw::c_uint, @@ -12,7 +18,7 @@ pub unsafe extern "C" fn callback_get_result_string( ) { let ret = user_data as *mut Result; - if start == std::ptr::null() { + if start.is_null() { if n != 0 { panic!("callback_get_result_string: start is null but n is not zero"); } @@ -22,7 +28,7 @@ pub unsafe extern "C" fn callback_get_result_string( let slice = std::slice::from_raw_parts(start as *const u8, n as usize); - if !(*ret).is_err() { + if (*ret).is_ok() { panic!( "callback_get_result_string: Result must be initialized to Err. Did Nix call us twice?" ); From 9186def7356ab6261b60e68cdb497bbaf6d8d558 Mon Sep 17 00:00:00 2001 From: Hercules CI Effects Date: Mon, 2 Dec 2024 00:58:19 +0000 Subject: [PATCH 117/306] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nix-cargo-integration': 'github:yusdacra/nix-cargo-integration/5230dddd1afff06cca6ae3780dec1bf709b528cc?narHash=sha256-KfanwS52gty87OZFWD84cLVJO6TV7lk0YtOZj5pgJWs%3D' (2025-09-29) → 'github:yusdacra/nix-cargo-integration/413617712f5189397cdf602485f89bf2b0a0e4af?narHash=sha256-g7TCUozMeW3q5Uc%2BwmZI64yzFucQ3SYlZQepo7prarA%3D' (2024-12-01) • Updated input 'nix-cargo-integration/dream2nix': 'github:nix-community/dream2nix/fbec3263cb4895ac86ee9506cdc4e6919a1a2214?narHash=sha256-nrDovydywSKRbWim9Ynmgj8SBm8LK3DI2WuhIqzOHYI%3D' (2025-08-12) → 'github:nix-community/dream2nix/a8dac99db44307fdecead13a39c584b97812d0d4?narHash=sha256-ViyEMSYwaza6y55XTDrsRi2K4YKCLsefMTorjWSE27s%3D' (2024-11-21) • Updated input 'nix-cargo-integration/dream2nix/pyproject-nix': 'github:pyproject-nix/pyproject.nix/16ee295c25107a94e59a7fc7f2e5322851781162?narHash=sha256-luVj97hIMpCbwhx3hWiRwjP2YvljWy8FM%2B4W9njDhLA%3D' (2025-07-14) → 'github:davhau/pyproject.nix/5a06a2697b228c04dd2f35659b4b659ca74f7aeb?narHash=sha256-hFg5s/hoJFv7tDpiGvEvXP0UfFvFEDgTdyHIjDVHu1I%3D' (2023-12-13) • Removed input 'nix-cargo-integration/dream2nix/pyproject-nix/nixpkgs' • Updated input 'nix-cargo-integration/parts': 'github:hercules-ci/flake-parts/4524271976b625a4a605beefd893f270620fd751?narHash=sha256-%2BuWLQZccFHwqpGqr2Yt5VsW/PbeJVTn9Dk6SHWhNRPw%3D' (2025-09-01) → 'github:hercules-ci/flake-parts/506278e768c2a08bec68eb62932193e341f55c90?narHash=sha256-hgmguH29K2fvs9szpq2r3pz2/8cJd2LPS%2Bb4tfNFCwE%3D' (2024-11-01) • Updated input 'nix-cargo-integration/rust-overlay': 'github:oxalica/rust-overlay/be3b8843a2be2411500f6c052876119485e957a2?narHash=sha256-xm4kEUcV2jk6u15aHazFP4YsMwhq%2BPczA%2BUl/4FDKWI%3D' (2025-09-29) → 'github:oxalica/rust-overlay/8e18f10703112e6c33e1c0d8b93e8305f6f0a75c?narHash=sha256-Chv9%2B3zrf1DhdB9JyskjoV0vJbCQEgkVqrU3p4RPLv8%3D' (2024-12-01) • Updated input 'nix-cargo-integration/treefmt': 'github:numtide/treefmt-nix/5eda4ee8121f97b218f7cc73f5172098d458f1d1?narHash=sha256-ySNJ008muQAds2JemiyrWYbwbG%2BV7S5wg3ZVKGHSFu8%3D' (2025-09-24) → 'github:numtide/treefmt-nix/6209c381904cab55796c5d7350e89681d3b2a8ef?narHash=sha256-2qbdorpq0TXHBWbVXaTqKoikN4bqAtAplTwGuII%2BoAc%3D' (2024-11-29) After • Updated input 'nix': 'github:NixOS/nix/4f50b1d178366e9892f8cd5d894365a51aa3387d?narHash=sha256-SYZXusCn2bdkpcmqZph%2BbsDtSQbByKtw/7wKOy/HD8I%3D' (2024-11-26) → 'github:NixOS/nix/64000481168d1da9d2519f055dd1fdee22275c21?narHash=sha256-Ka/uNdaqpTAiVL%2B%2B4MPHg8fG5o1tiJeY6G2t5UiKhd8%3D' (2024-11-29) • Updated input 'nix-cargo-integration': 'github:yusdacra/nix-cargo-integration/2e51be6bafce8a2c0bd57fc9d10700331b86d027?narHash=sha256-jeCP6rJAhtDqayQtJaUtbVJ7aN0KoKNYfCLEecQc2vo%3D' (2024-11-26) → 'github:yusdacra/nix-cargo-integration/413617712f5189397cdf602485f89bf2b0a0e4af?narHash=sha256-g7TCUozMeW3q5Uc%2BwmZI64yzFucQ3SYlZQepo7prarA%3D' (2024-12-01) • Updated input 'nix-cargo-integration/rust-overlay': 'github:oxalica/rust-overlay/414e748aae5c9e6ca63c5aafffda03e5dad57ceb?narHash=sha256-J2/hxOO1VtBA/u%2Ba%2B9E%2B3iJpWT3xsBdghgYAVfoGCJo%3D' (2024-11-26) → 'github:oxalica/rust-overlay/8e18f10703112e6c33e1c0d8b93e8305f6f0a75c?narHash=sha256-Chv9%2B3zrf1DhdB9JyskjoV0vJbCQEgkVqrU3p4RPLv8%3D' (2024-12-01) • Updated input 'nix-cargo-integration/treefmt': 'github:numtide/treefmt-nix/705df92694af7093dfbb27109ce16d828a79155f?narHash=sha256-5WSng844vXt8uytT5djmqBCkopyle6ciFgteuA9bJpw%3D' (2024-11-22) → 'github:numtide/treefmt-nix/6209c381904cab55796c5d7350e89681d3b2a8ef?narHash=sha256-2qbdorpq0TXHBWbVXaTqKoikN4bqAtAplTwGuII%2BoAc%3D' (2024-11-29) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/23e89b7da85c3640bbc2173fe04f4bd114342367?narHash=sha256-y/MEyuJ5oBWrWAic/14LaIr/u5E0wRVzyYsouYY3W6w%3D' (2024-11-19) → 'github:NixOS/nixpkgs/970e93b9f82e2a0f3675757eb0bfc73297cc6370?narHash=sha256-jNRNr49UiuIwaarqijgdTR2qLPifxsVhlJrKzQ8XUIE%3D' (2024-11-28) • Updated input 'nixpkgs-old': 'github:NixOS/nixpkgs/e8c38b73aeb218e27163376a2d617e61a2ad9b59?narHash=sha256-df3dJApLPhd11AlueuoN0Q4fHo/hagP75LlM5K1sz9g%3D' (2024-11-16) → 'github:NixOS/nixpkgs/7e1ca67996afd8233d9033edd26e442836cc2ad6?narHash=sha256-8qwPSE2g1othR1u4uP86NXxm6i7E9nHPyJX3m3lx7Q4%3D' (2024-12-01) (loosely cherry picked from commit 03140e5b6d8bbdecd94821ced2baf6d86781b3b1) --- flake.lock | 51 ++++++++++++++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/flake.lock b/flake.lock index 3dbe5a6..0c013f6 100644 --- a/flake.lock +++ b/flake.lock @@ -27,11 +27,11 @@ "pyproject-nix": "pyproject-nix" }, "locked": { - "lastModified": 1728585693, - "narHash": "sha256-rhx5SYpIkPu7d+rjF9FGGBVxS0BwAEkmYIsJg2a3E20=", + "lastModified": 1732214960, + "narHash": "sha256-ViyEMSYwaza6y55XTDrsRi2K4YKCLsefMTorjWSE27s=", "owner": "nix-community", "repo": "dream2nix", - "rev": "c6935471f7e1a9e190aaa9ac9823dca34e00d92a", + "rev": "a8dac99db44307fdecead13a39c584b97812d0d4", "type": "github" }, "original": { @@ -183,11 +183,11 @@ "treefmt": "treefmt" }, "locked": { - "lastModified": 1729750574, - "narHash": "sha256-87MJq1yxwAcAK/aKSaJ0i4HpaorweyiwRY6kNzsc7v8=", + "lastModified": 1733033761, + "narHash": "sha256-g7TCUozMeW3q5Uc+wmZI64yzFucQ3SYlZQepo7prarA=", "owner": "yusdacra", "repo": "nix-cargo-integration", - "rev": "d4f92d8c201479762727991bb290da18129b858c", + "rev": "413617712f5189397cdf602485f89bf2b0a0e4af", "type": "github" }, "original": { @@ -198,11 +198,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1724819573, - "narHash": "sha256-GnR7/ibgIH1vhoy8cYdmXE6iyZqKqFxQSVkFgosBh6w=", + "lastModified": 1732837521, + "narHash": "sha256-jNRNr49UiuIwaarqijgdTR2qLPifxsVhlJrKzQ8XUIE=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "71e91c409d1e654808b2621f28a327acfdad8dc2", + "rev": "970e93b9f82e2a0f3675757eb0bfc73297cc6370", "type": "github" }, "original": { @@ -237,7 +237,7 @@ }, "original": { "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/356624c12086a18f2ea2825fed34523d60ccc4e3.tar.gz" + "url": "https://github.com/NixOS/nixpkgs/archive/cc2f28000298e1269cea6612cd06ec9979dd5d7f.tar.gz" } }, "nixpkgs-regression": { @@ -264,11 +264,11 @@ ] }, "locked": { - "lastModified": 1727826117, - "narHash": "sha256-K5ZLCyfO/Zj9mPFldf3iwS6oZStJcU4tSpiXTMYaaL0=", + "lastModified": 1730504689, + "narHash": "sha256-hgmguH29K2fvs9szpq2r3pz2/8cJd2LPS+b4tfNFCwE=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "3d04084d54bedc3d6b8b736c70ef449225c361b1", + "rev": "506278e768c2a08bec68eb62932193e341f55c90", "type": "github" }, "original": { @@ -319,11 +319,11 @@ "slimlock": "slimlock" }, "locked": { - "lastModified": 1724504251, - "narHash": "sha256-TIw+sac0NX0FeAneud+sQZT+ql1G/WEb7/Vb436rUXM=", + "lastModified": 1728546539, + "narHash": "sha256-Sws7w0tlnjD+Bjck1nv29NjC5DbL6nH5auL9Ex9Iz2A=", "owner": "thomashoneyman", "repo": "purescript-overlay", - "rev": "988b09676c2a0e6a46dfa3589aa6763c90476b8a", + "rev": "4ad4c15d07bd899d7346b331f377606631eb0ee4", "type": "github" }, "original": { @@ -358,13 +358,18 @@ } }, "rust-overlay": { - "flake": false, + "inputs": { + "nixpkgs": [ + "nix-cargo-integration", + "nixpkgs" + ] + }, "locked": { - "lastModified": 1729736953, - "narHash": "sha256-Rb6JUop7NRklg0uzcre+A+Ebrn/ZiQPkm4QdKg6/3pw=", + "lastModified": 1733020719, + "narHash": "sha256-Chv9+3zrf1DhdB9JyskjoV0vJbCQEgkVqrU3p4RPLv8=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "29b1275740d9283467b8117499ec8cbb35250584", + "rev": "8e18f10703112e6c33e1c0d8b93e8305f6f0a75c", "type": "github" }, "original": { @@ -404,11 +409,11 @@ ] }, "locked": { - "lastModified": 1729613947, - "narHash": "sha256-XGOvuIPW1XRfPgHtGYXd5MAmJzZtOuwlfKDgxX5KT3s=", + "lastModified": 1732894027, + "narHash": "sha256-2qbdorpq0TXHBWbVXaTqKoikN4bqAtAplTwGuII+oAc=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "aac86347fb5063960eccb19493e0cadcdb4205ca", + "rev": "6209c381904cab55796c5d7350e89681d3b2a8ef", "type": "github" }, "original": { From 22d2b3e6f3d676a4e7d0bc11623c693836af7969 Mon Sep 17 00:00:00 2001 From: Hercules CI Effects Date: Mon, 2 Dec 2024 00:58:21 +0000 Subject: [PATCH 118/306] dev/flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'hercules-ci-effects': 'github:hercules-ci/hercules-ci-effects/d70658494391994c7b32e8fe5610dae76737e4df?narHash=sha256-2W//PmgocN9lplDJ7WoiP9EcrfUxqvtxplCAqlwvquY%3D' (2024-10-29) → 'github:hercules-ci/hercules-ci-effects/b89ac4d66d618b915b1f0a408e2775fe3821d141?narHash=sha256-mnynlrPeiW0nUQ8KGZHb3WyxAxA3Ye/BH8gMjdoKP6E%3D' (2024-11-06) • Updated input 'pre-commit-hooks-nix': 'github:cachix/pre-commit-hooks.nix/af8a16fe5c264f5e9e18bcee2859b40a656876cf?narHash=sha256-W1MIJpADXQCgosJZT8qBYLRuZls2KSiKdpnTVdKBuvU%3D' (2024-10-30) → 'github:cachix/pre-commit-hooks.nix/3308484d1a443fc5bc92012435d79e80458fe43c?narHash=sha256-mnTbjpdqF0luOkou8ZFi2asa1N3AA2CchR/RqCNmsGE%3D' (2024-11-19) • Updated input 'pre-commit-hooks-nix/nixpkgs-stable': 'github:NixOS/nixpkgs/194846768975b7ad2c4988bdb82572c00222c0d7?narHash=sha256-NGKVY4PjzwAa4upkGtAMz1npHGoRzWotlSnVlqI40mo%3D' (2024-07-07) → 'github:NixOS/nixpkgs/d063c1dd113c91ab27959ba540c0d9753409edf3?narHash=sha256-edm8WG19kWozJ/GqyYx2VjW99EdhjKwbY3ZwdlPAAlo%3D' (2024-11-04) (cherry picked from commit 7529f8878378da938fb79a42f8f61c2a4c9537c4) --- dev/flake.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/dev/flake.lock b/dev/flake.lock index f8b1bc3..b69d295 100644 --- a/dev/flake.lock +++ b/dev/flake.lock @@ -63,11 +63,11 @@ "nixpkgs": "nixpkgs" }, "locked": { - "lastModified": 1719226092, - "narHash": "sha256-YNkUMcCUCpnULp40g+svYsaH1RbSEj6s4WdZY/SHe38=", + "lastModified": 1730903510, + "narHash": "sha256-mnynlrPeiW0nUQ8KGZHb3WyxAxA3Ye/BH8gMjdoKP6E=", "owner": "hercules-ci", "repo": "hercules-ci-effects", - "rev": "11e4b8dc112e2f485d7c97e1cee77f9958f498f5", + "rev": "b89ac4d66d618b915b1f0a408e2775fe3821d141", "type": "github" }, "original": { @@ -94,11 +94,11 @@ }, "nixpkgs-stable": { "locked": { - "lastModified": 1720386169, - "narHash": "sha256-NGKVY4PjzwAa4upkGtAMz1npHGoRzWotlSnVlqI40mo=", + "lastModified": 1730741070, + "narHash": "sha256-edm8WG19kWozJ/GqyYx2VjW99EdhjKwbY3ZwdlPAAlo=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "194846768975b7ad2c4988bdb82572c00222c0d7", + "rev": "d063c1dd113c91ab27959ba540c0d9753409edf3", "type": "github" }, "original": { @@ -116,11 +116,11 @@ "nixpkgs-stable": "nixpkgs-stable" }, "locked": { - "lastModified": 1724857454, - "narHash": "sha256-Qyl9Q4QMTLZnnBb/8OuQ9LSkzWjBU1T5l5zIzTxkkhk=", + "lastModified": 1732021966, + "narHash": "sha256-mnTbjpdqF0luOkou8ZFi2asa1N3AA2CchR/RqCNmsGE=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "4509ca64f1084e73bc7a721b20c669a8d4c5ebe6", + "rev": "3308484d1a443fc5bc92012435d79e80458fe43c", "type": "github" }, "original": { From 237a2281c19fa92368bc24b779cc1cba95f16ffb Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 2 Dec 2024 13:52:17 +0100 Subject: [PATCH 119/306] refact: require_attrs_names -> require_attrs_names_unsorted (cherry picked from commit 13a3adf4f17351bc6ba3d91567a642ec1ae704f6) --- rust/nix-expr/src/eval_state.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 12689e7..efca46d 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -210,7 +210,7 @@ impl EvalState { } /// Evaluate, and require that the value is an attrset. /// Returns a list of the keys in the attrset. - pub fn require_attrs_names(&mut self, v: &Value) -> Result> { + pub fn require_attrs_names_unsorted(&mut self, v: &Value) -> Result> { let t = self.value_type(v)?; if t != ValueType::AttrSet { bail!("expected an attrset, but got a {:?}", t); @@ -729,34 +729,34 @@ mod tests { es.force(&v).unwrap(); let t = es.value_type_unforced(&v); assert!(t == Some(ValueType::AttrSet)); - let attrs = es.require_attrs_names(&v).unwrap(); + let attrs = es.require_attrs_names_unsorted(&v).unwrap(); assert_eq!(attrs.len(), 0); }) .unwrap() } #[test] - fn eval_state_require_attrs_names_forces_thunk() { + fn eval_state_require_attrs_names_unsorted_forces_thunk() { gc_registering_current_thread(|| { let store = Store::open("auto", HashMap::new()).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let v = make_thunk(&mut es, "{ a = 1; b = 2; }"); let t = es.value_type_unforced(&v); assert!(t == None); - let attrs = es.require_attrs_names(&v).unwrap(); + let attrs = es.require_attrs_names_unsorted(&v).unwrap(); assert_eq!(attrs.len(), 2); }) .unwrap() } #[test] - fn eval_state_require_attrs_names_bad_type() { + fn eval_state_require_attrs_names_unsorted_bad_type() { gc_registering_current_thread(|| { let store = Store::open("auto", HashMap::new()).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let v = es.eval_from_string("1", "").unwrap(); es.force(&v).unwrap(); - let r = es.require_attrs_names(&v); + let r = es.require_attrs_names_unsorted(&v); assert!(r.is_err()); assert_eq!( r.unwrap_err().to_string(), @@ -773,7 +773,7 @@ mod tests { let mut es = EvalState::new(store, []).unwrap(); let expr = r#"{ a = throw "nope a"; b = throw "nope b"; }"#; let v = es.eval_from_string(expr, "").unwrap(); - let attrs = es.require_attrs_names(&v).unwrap(); + let attrs = es.require_attrs_names_unsorted(&v).unwrap(); assert_eq!(attrs.len(), 2); assert_eq!(attrs[0], "a"); assert_eq!(attrs[1], "b"); From 76b92a577d2e7c055a519f050827869ff4dd3f54 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 2 Dec 2024 14:02:51 +0100 Subject: [PATCH 120/306] feat: Add EvalState.require_attrs_names (sorted) (cherry picked from commit 0b1aca5a1ee7136e76a201477643cfc3acbf2676) --- rust/nix-expr/src/eval_state.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index efca46d..1afce0e 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -208,8 +208,20 @@ impl EvalState { } unsafe { check_call!(raw::get_int(&mut self.context, v.raw_ptr())) } } + /// Evaluate, and require that the value is an attrset. /// Returns a list of the keys in the attrset. + /// + /// NOTE: this currently implements its own sorting, which probably matches Nix's implementation, but is not guaranteed. + pub fn require_attrs_names(&mut self, v: &Value) -> Result> { + self.require_attrs_names_unsorted(v).map(|mut v| { + v.sort(); + v + }) + } + + /// For when [require_attrs_names] isn't fast enough. + /// Only use when it's ok that the keys are returned in an arbitrary order. pub fn require_attrs_names_unsorted(&mut self, v: &Value) -> Result> { let t = self.value_type(v)?; if t != ValueType::AttrSet { From 8a437b71a080e23de79d4e3787bd07709615f0ed Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 2 Dec 2024 14:08:03 +0100 Subject: [PATCH 121/306] test: Fix flaky eval_state_value_attrs_names_example (cherry picked from commit 4583b422038661de02c6fe672559d2519f38dfce) --- rust/nix-expr/src/eval_state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 1afce0e..510d095 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -785,7 +785,7 @@ mod tests { let mut es = EvalState::new(store, []).unwrap(); let expr = r#"{ a = throw "nope a"; b = throw "nope b"; }"#; let v = es.eval_from_string(expr, "").unwrap(); - let attrs = es.require_attrs_names_unsorted(&v).unwrap(); + let attrs = es.require_attrs_names(&v).unwrap(); assert_eq!(attrs.len(), 2); assert_eq!(attrs[0], "a"); assert_eq!(attrs[1], "b"); From dc01d3731fdc9e60123e8d31640b70cb02c388d8 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 1 Dec 2024 18:56:41 +0100 Subject: [PATCH 122/306] feat: Add EvalState::call_multi (cherry picked from commit e1866f0c61011ffb57b607abe871340aca294f19) --- rust/nix-expr/src/eval_state.rs | 82 +++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 510d095..258dd48 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -433,6 +433,24 @@ impl EvalState { Ok(value) } + /// Eagerly apply a function with multiple curried arguments. + #[doc(alias = "nix_value_call_multi")] + pub fn call_multi(&mut self, f: &Value, args: &[Value]) -> Result { + let value = self.new_value_uninitialized()?; + let mut args_ptrs = args.iter().map(|a| a.raw_ptr()).collect::>(); + unsafe { + check_call!(raw::value_call_multi( + &mut self.context, + self.eval_state.as_ptr(), + f.raw_ptr(), + args_ptrs.len(), + args_ptrs.as_mut_ptr(), + value.raw_ptr() + )) + }?; + Ok(value) + } + /// Apply a function to an argument, but don't evaluate the result just yet. /// /// For an eager version, see [`call`][`EvalState::call`]. @@ -1166,6 +1184,24 @@ mod tests { .unwrap(); } + #[test] + fn eval_state_call_multi() { + gc_registering_current_thread(|| { + let store = Store::open("auto", HashMap::new()).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); + // This is a function that takes two arguments. + let f = es.eval_from_string("x: y: x - y", "").unwrap(); + let a = es.eval_from_string("2", "").unwrap(); + let b = es.eval_from_string("3", "").unwrap(); + let v = es.call_multi(&f, &[a, b]).unwrap(); + let t = es.value_type_unforced(&v); + assert!(t == Some(ValueType::Int)); + let i = es.require_int(&v).unwrap(); + assert!(i == -1); + }) + .unwrap(); + } + #[test] fn eval_state_apply() { gc_registering_current_thread(|| { @@ -1206,6 +1242,29 @@ mod tests { .unwrap(); } + #[test] + fn eval_state_call_multi_fail_body() { + gc_registering_current_thread(|| { + let store = Store::open("auto", HashMap::new()).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); + // This is a function that takes two arguments. + let f = es.eval_from_string("x: y: x - y", "").unwrap(); + let a = es.eval_from_string("2", "").unwrap(); + let b = es.eval_from_string("true", "").unwrap(); + let r = es.call_multi(&f, &[a, b]); + match r { + Ok(_) => panic!("expected an error"), + Err(e) => { + if !e.to_string().contains("expected an integer but found") { + eprintln!("{}", e); + assert!(false); + } + } + } + }) + .unwrap(); + } + #[test] fn eval_state_apply_fail_body() { gc_registering_current_thread(|| { @@ -1252,6 +1311,29 @@ mod tests { .unwrap(); } + #[test] + fn eval_state_call_multi_fail_args() { + gc_registering_current_thread(|| { + let store = Store::open("auto", HashMap::new()).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); + // This is a function that takes two arguments. + let f = es.eval_from_string("{x}: {y}: x - y", "").unwrap(); + let a = es.eval_from_string("{x = 2;}", "").unwrap(); + let b = es.eval_from_string("{}", "").unwrap(); + let r = es.call_multi(&f, &[a, b]); + match r { + Ok(_) => panic!("expected an error"), + Err(e) => { + if !e.to_string().contains("called without required argument") { + eprintln!("{}", e); + assert!(false); + } + } + } + }) + .unwrap(); + } + /// This tests the behavior of `new_value_apply`, which is lazy, unlike `call`. #[test] fn eval_state_apply_fail_args_lazy() { From 28deb20a2b56c88c120fc25a275be0653c00f3e5 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 1 Dec 2024 21:50:07 +0100 Subject: [PATCH 123/306] feat: EvalState.new_value_attrs (cherry picked from commit 5b3f4d97f968b518c901c2de3759640b91fd37c2) --- rust/nix-expr/src/eval_state.rs | 122 ++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 258dd48..6e18d6a 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -492,6 +492,60 @@ impl EvalState { }; Ok(value) } + + pub fn new_value_attrs(&mut self, attrs: I) -> Result + where + I: IntoIterator, + I::IntoIter: ExactSizeIterator, + { + let iter = attrs.into_iter(); + let size = iter.len(); + let bindings_builder = BindingsBuilder::new(self, size)?; + for (name, value) in iter { + let name = + CString::new(name).with_context(|| "new_value_attrs: name contains null byte")?; + unsafe { + check_call!(raw::bindings_builder_insert( + &mut self.context, + bindings_builder.ptr, + name.as_ptr(), + value.raw_ptr() + ))?; + } + } + let value = self.new_value_uninitialized()?; + unsafe { + check_call!(raw::make_attrs( + &mut self.context, + value.raw_ptr(), + bindings_builder.ptr + ))?; + } + Ok(value) + } +} + +struct BindingsBuilder { + ptr: *mut raw::BindingsBuilder, +} +impl Drop for BindingsBuilder { + fn drop(&mut self) { + unsafe { + raw::bindings_builder_free(self.ptr); + } + } +} +impl BindingsBuilder { + fn new(eval_state: &mut EvalState, capacity: usize) -> Result { + let ptr = unsafe { + check_call!(raw::make_bindings_builder( + &mut eval_state.context, + eval_state.eval_state.as_ptr(), + capacity + )) + }?; + Ok(BindingsBuilder { ptr }) + } } pub fn gc_now() { @@ -1636,4 +1690,72 @@ mod tests { }) .unwrap(); } + + #[test] + pub fn eval_state_new_value_attrs_from_slice_empty() { + gc_registering_current_thread(|| { + let store = Store::open("auto", []).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); + let attrs = es.new_value_attrs([]).unwrap(); + let t = es.value_type(&attrs).unwrap(); + assert!(t == ValueType::AttrSet); + let names = es.require_attrs_names(&attrs).unwrap(); + assert!(names.is_empty()); + }) + .unwrap(); + } + + #[test] + pub fn eval_state_new_value_attrs_from_vec() { + gc_registering_current_thread(|| { + let store = Store::open("auto", []).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); + let attrs = { + let a = es.new_value_int(1).unwrap(); + let b = es.new_value_int(2).unwrap(); + es.new_value_attrs(vec![("a".to_string(), a), ("b".to_string(), b)]) + .unwrap() + }; + let t = es.value_type(&attrs).unwrap(); + assert!(t == ValueType::AttrSet); + let names = es.require_attrs_names(&attrs).unwrap(); + assert_eq!(names.len(), 2); + assert_eq!(names[0], "a"); + assert_eq!(names[1], "b"); + let a = es.require_attrs_select(&attrs, "a").unwrap(); + let b = es.require_attrs_select(&attrs, "b").unwrap(); + let i = es.require_int(&a).unwrap(); + assert_eq!(i, 1); + let i = es.require_int(&b).unwrap(); + assert_eq!(i, 2); + }) + .unwrap(); + } + + #[test] + pub fn eval_state_new_value_attrs_from_hashmap() { + gc_registering_current_thread(|| { + let store = Store::open("auto", []).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); + let attrs = { + let a = es.new_value_int(1).unwrap(); + let b = es.new_value_int(2).unwrap(); + es.new_value_attrs(HashMap::from([("a".to_string(), a), ("b".to_string(), b)])) + .unwrap() + }; + let t = es.value_type(&attrs).unwrap(); + assert!(t == ValueType::AttrSet); + let names = es.require_attrs_names(&attrs).unwrap(); + assert_eq!(names.len(), 2); + assert_eq!(names[0], "a"); + assert_eq!(names[1], "b"); + let a = es.require_attrs_select(&attrs, "a").unwrap(); + let b = es.require_attrs_select(&attrs, "b").unwrap(); + let i = es.require_int(&a).unwrap(); + assert_eq!(i, 1); + let i = es.require_int(&b).unwrap(); + assert_eq!(i, 2); + }) + .unwrap(); + } } From 6193575d1e7d337cd4125e3ad6b0d969d9f0cd86 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 16 Dec 2024 14:11:17 +0100 Subject: [PATCH 124/306] fix: Require non-null pointer in StorePath Fixes https://github.com/nixops4/nixops4/issues/65, possible undefined behavior. This doesn't make the code nice wrt *const/*mut distinction, but since we're not mutating it, this should be fine. (cherry picked from commit 75d448aad923a5f835f0562400e223df43103ea4) --- rust/nix-expr/src/eval_state.rs | 5 +++++ rust/nix-store/src/path.rs | 22 ++++++++++++++++------ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 6e18d6a..46802d3 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -403,6 +403,11 @@ impl EvalState { let mut paths = Vec::with_capacity(n as usize); for i in 0..n { let path = raw::realised_string_get_store_path(rs, i); + let path = NonNull::new(path as *mut raw::StorePath).ok_or_else(|| { + anyhow::format_err!( + "nix_realised_string_get_store_path returned a null pointer" + ) + })?; paths.push(StorePath::new_raw_clone(path)); } paths diff --git a/rust/nix-store/src/path.rs b/rust/nix-store/src/path.rs index ad1a0f3..5fa847c 100644 --- a/rust/nix-store/src/path.rs +++ b/rust/nix-store/src/path.rs @@ -1,3 +1,5 @@ +use std::ptr::NonNull; + use anyhow::Result; use nix_c_raw as raw; use nix_util::{ @@ -6,38 +8,46 @@ use nix_util::{ }; pub struct StorePath { - raw: *mut raw::StorePath, + raw: NonNull, } impl StorePath { /// This is a low level function that you shouldn't have to call unless you are developing the Nix bindings. /// /// Construct a new `StorePath` by first cloning the C store path. /// This does not take ownership of the C store path, so it should be a borrowed value, or you should free it. - pub fn new_raw_clone(raw: *const raw::StorePath) -> Self { - Self::new_raw(unsafe { raw::store_path_clone(raw as *mut raw::StorePath) }) + pub fn new_raw_clone(raw: NonNull) -> Self { + Self::new_raw( + NonNull::new(unsafe { raw::store_path_clone(raw.as_ptr()) }) + .or_else(|| panic!("nix_store_path_clone returned a null pointer")) + .unwrap(), + ) } /// This is a low level function that you shouldn't have to call unless you are developing the Nix bindings. /// /// Takes ownership of a C `nix_store_path`. It will be freed when the `StorePath` is dropped. - pub fn new_raw(raw: *mut raw::StorePath) -> Self { + pub fn new_raw(raw: NonNull) -> Self { StorePath { raw } } pub fn name(&self) -> Result { unsafe { let mut r = result_string_init!(); raw::store_path_name( - self.raw, + self.as_ptr(), Some(callback_get_result_string), callback_get_result_string_data(&mut r), ); r } } + + pub fn as_ptr(&self) -> *mut nix_c_raw::StorePath { + self.raw.as_ptr() + } } impl Drop for StorePath { fn drop(&mut self) { unsafe { - raw::store_path_free(self.raw); + raw::store_path_free(self.as_ptr()); } } } From ce07ed1c07981df7ed6d5a069165cb8e1343feea Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 17 Dec 2024 09:28:18 +0100 Subject: [PATCH 125/306] fix: Mark all StorePath pointer management as unsafe ... because it is. I had previously dismissed the comparatively trivial unsafety of these functions, assuming the caller is aware of the purpose of them and reasonably familiar with manual memory management. That would have been fine in an unsafe by default language like C++, which Rust is not. (cherry picked from commit b43455fdd0468f067741a79a7031ba2fa907f0eb) --- rust/nix-store/src/path.rs | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/rust/nix-store/src/path.rs b/rust/nix-store/src/path.rs index 5fa847c..fc20382 100644 --- a/rust/nix-store/src/path.rs +++ b/rust/nix-store/src/path.rs @@ -14,18 +14,27 @@ impl StorePath { /// This is a low level function that you shouldn't have to call unless you are developing the Nix bindings. /// /// Construct a new `StorePath` by first cloning the C store path. - /// This does not take ownership of the C store path, so it should be a borrowed value, or you should free it. - pub fn new_raw_clone(raw: NonNull) -> Self { + /// + /// # Safety + /// + /// This does not take ownership of the C store path, so it should be a borrowed pointer, or you should free it. + pub unsafe fn new_raw_clone(raw: NonNull) -> Self { Self::new_raw( - NonNull::new(unsafe { raw::store_path_clone(raw.as_ptr()) }) + NonNull::new(raw::store_path_clone(raw.as_ptr())) .or_else(|| panic!("nix_store_path_clone returned a null pointer")) .unwrap(), ) } + /// This is a low level function that you shouldn't have to call unless you are developing the Nix bindings. /// /// Takes ownership of a C `nix_store_path`. It will be freed when the `StorePath` is dropped. - pub fn new_raw(raw: NonNull) -> Self { + /// + /// # Safety + /// + /// The caller must ensure that the provided `NonNull` is valid and that the ownership + /// semantics are correctly followed. The `raw` pointer must not be used after being passed to this function. + pub unsafe fn new_raw(raw: NonNull) -> Self { StorePath { raw } } pub fn name(&self) -> Result { @@ -40,7 +49,14 @@ impl StorePath { } } - pub fn as_ptr(&self) -> *mut nix_c_raw::StorePath { + /// This is a low level function that you shouldn't have to call unless you are developing the Nix bindings. + /// + /// Get a pointer to the underlying Nix C API store path. + /// + /// # Safety + /// + /// This function is unsafe because it returns a raw pointer. The caller must ensure that the pointer is not used beyond the lifetime of this `StorePath`. + pub unsafe fn as_ptr(&self) -> *mut nix_c_raw::StorePath { self.raw.as_ptr() } } From 3d3c77ecedf8fb797ab8b5b37dbd723174c317e5 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 17 Dec 2024 09:37:18 +0100 Subject: [PATCH 126/306] doc: StorePath.name() (cherry picked from commit 2f3dc27eb816634ebb8edbabd409ebc52db9a0bb) --- rust/nix-store/src/path.rs | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/rust/nix-store/src/path.rs b/rust/nix-store/src/path.rs index fc20382..a0cb301 100644 --- a/rust/nix-store/src/path.rs +++ b/rust/nix-store/src/path.rs @@ -11,6 +11,21 @@ pub struct StorePath { raw: NonNull, } impl StorePath { + /// Get the name of the store path. + /// + /// For a store path like `/nix/store/abc1234...-foo-1.2`, this function will return `foo-1.2`. + pub fn name(&self) -> Result { + unsafe { + let mut r = result_string_init!(); + raw::store_path_name( + self.as_ptr(), + Some(callback_get_result_string), + callback_get_result_string_data(&mut r), + ); + r + } + } + /// This is a low level function that you shouldn't have to call unless you are developing the Nix bindings. /// /// Construct a new `StorePath` by first cloning the C store path. @@ -37,17 +52,6 @@ impl StorePath { pub unsafe fn new_raw(raw: NonNull) -> Self { StorePath { raw } } - pub fn name(&self) -> Result { - unsafe { - let mut r = result_string_init!(); - raw::store_path_name( - self.as_ptr(), - Some(callback_get_result_string), - callback_get_result_string_data(&mut r), - ); - r - } - } /// This is a low level function that you shouldn't have to call unless you are developing the Nix bindings. /// From c9d54ccbc67b8b01cf8b7f54e80dc1566dd57a9c Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 16 Dec 2024 12:52:15 +0100 Subject: [PATCH 127/306] feat: Store.get_storedir() (cherry picked from commit 6f4ba636f1e563167e6456d42c9fb6f65c9ca504) --- flake.lock | 6 +++--- rust/Cargo.lock | 3 ++- rust/nix-store/Cargo.toml | 4 ++++ rust/nix-store/build.rs | 39 +++++++++++++++++++++++++++++++++++++ rust/nix-store/src/store.rs | 15 ++++++++++++++ 5 files changed, 63 insertions(+), 4 deletions(-) create mode 100644 rust/nix-store/build.rs diff --git a/flake.lock b/flake.lock index 0c013f6..3dbb956 100644 --- a/flake.lock +++ b/flake.lock @@ -156,11 +156,11 @@ "pre-commit-hooks": "pre-commit-hooks" }, "locked": { - "lastModified": 1732892090, - "narHash": "sha256-Ka/uNdaqpTAiVL++4MPHg8fG5o1tiJeY6G2t5UiKhd8=", + "lastModified": 1734340930, + "narHash": "sha256-BeyD6r1rQWaL+K7vLRSiCcZG4czEfmBYEL3kvBS2nGw=", "owner": "NixOS", "repo": "nix", - "rev": "64000481168d1da9d2519f055dd1fdee22275c21", + "rev": "2f32cf6d90df599d620eea5c48b9469f1c251025", "type": "github" }, "original": { diff --git a/rust/Cargo.lock b/rust/Cargo.lock index c6ce2f5..eaa0409 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "Inflector" @@ -346,6 +346,7 @@ dependencies = [ "lazy_static", "nix-c-raw", "nix-util", + "pkg-config", ] [[package]] diff --git a/rust/nix-store/Cargo.toml b/rust/nix-store/Cargo.toml index 2df0340..6c42ac9 100644 --- a/rust/nix-store/Cargo.toml +++ b/rust/nix-store/Cargo.toml @@ -2,6 +2,7 @@ name = "nix-store" version = "0.1.0" edition = "2021" +build = "build.rs" [lib] path = "src/lib.rs" @@ -11,3 +12,6 @@ anyhow = "1.0.79" nix-util = { path = "../nix-util" } nix-c-raw = { path = "../nix-c-raw" } lazy_static = "1.4.0" + +[build-dependencies] +pkg-config = "0.3.30" diff --git a/rust/nix-store/build.rs b/rust/nix-store/build.rs new file mode 100644 index 0000000..f573fe8 --- /dev/null +++ b/rust/nix-store/build.rs @@ -0,0 +1,39 @@ +fn main() { + // Get nix version + let nix_version = pkg_config::probe_library("nix-store-c").unwrap().version; + + // Generate version flags + // Unfortunately, Rust doesn't give us a "greater than" operator in conditional + // compilation, so we pre-evaluate the version comparisons here, making use + // of the multi-valued nature of Rust cfgs. + let relevant_versions = vec!["2.26"]; + let versions = relevant_versions + .iter() + .map(|v| format!("\"{}\"", v)) + .collect::>() + .join(","); + + // Declare the known versions, so that Rust can warn about unknown versions + // that aren't part of `relevant_versions` yet - feel free to add entries. + println!( + "cargo:rustc-check-cfg=cfg(nix_at_least,values({}))", + versions + ); + + let nix_version = nix_version.split('.').collect::>(); + let nix_version = ( + nix_version[0].parse::().unwrap(), + nix_version[1].parse::().unwrap(), + ); + + for version_str in relevant_versions { + let version = version_str.split('.').collect::>(); + let version = ( + version[0].parse::().unwrap(), + version[1].parse::().unwrap(), + ); + if nix_version >= version { + println!("cargo:rustc-cfg=nix_at_least=\"{}\"", version_str); + } + } +} diff --git a/rust/nix-store/src/store.rs b/rust/nix-store/src/store.rs index 87295d2..da4cbc9 100644 --- a/rust/nix-store/src/store.rs +++ b/rust/nix-store/src/store.rs @@ -172,6 +172,21 @@ impl Store { r } + #[cfg(nix_at_least = "2.26")] + #[doc(alias = "nix_store_get_storedir")] + pub fn get_storedir(&mut self) -> Result { + let mut r = result_string_init!(); + unsafe { + check_call!(raw::store_get_storedir( + &mut self.context, + self.inner.ptr(), + Some(callback_get_result_string), + callback_get_result_string_data(&mut r) + )) + }?; + r + } + pub fn weak_ref(&self) -> StoreWeak { StoreWeak { inner: Arc::downgrade(&self.inner), From c986c09b8c262726403baef72ae035583a0db483 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 17 Dec 2024 09:54:28 +0100 Subject: [PATCH 128/306] feat: Store.parse_store_path() (cherry picked from commit 6b92d4164b94d5030929dfb56577b1fd8d62e067) --- rust/nix-store/src/path.rs | 14 +++++++++++++ rust/nix-store/src/store.rs | 41 +++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/rust/nix-store/src/path.rs b/rust/nix-store/src/path.rs index a0cb301..e6b9ceb 100644 --- a/rust/nix-store/src/path.rs +++ b/rust/nix-store/src/path.rs @@ -71,3 +71,17 @@ impl Drop for StorePath { } } } + +#[cfg(test)] +mod tests { + #[test] + #[cfg(nix_at_least = "2.26" /* get_storedir */)] + fn store_path_name() { + let mut store = crate::store::Store::open("dummy://", []).unwrap(); + let store_dir = store.get_storedir().unwrap(); + let store_path_string = + format!("{store_dir}/rdd4pnr4x9rqc9wgbibhngv217w2xvxl-bash-interactive-5.2p26"); + let store_path = store.parse_store_path(store_path_string.as_str()).unwrap(); + assert_eq!(store_path.name().unwrap(), "bash-interactive-5.2p26"); + } +} diff --git a/rust/nix-store/src/store.rs b/rust/nix-store/src/store.rs index da4cbc9..dc6c9cc 100644 --- a/rust/nix-store/src/store.rs +++ b/rust/nix-store/src/store.rs @@ -10,6 +10,8 @@ use std::ptr::null_mut; use std::ptr::NonNull; use std::sync::{Arc, Mutex, Weak}; +use crate::path::StorePath; + /* TODO make Nix itself thread safe */ lazy_static! { static ref INIT: Result<()> = unsafe { @@ -187,6 +189,21 @@ impl Store { r } + #[doc(alias = "nix_store_parse_path")] + pub fn parse_store_path(&mut self, path: &str) -> Result { + let path = CString::new(path)?; + unsafe { + let store_path = check_call!(raw::store_parse_path( + &mut self.context, + self.inner.ptr(), + path.as_ptr() + ))?; + let store_path = + NonNull::new(store_path).expect("nix_store_parse_path returned a null pointer"); + Ok(StorePath::new_raw(store_path)) + } + } + pub fn weak_ref(&self) -> StoreWeak { StoreWeak { inner: Arc::downgrade(&self.inner), @@ -240,6 +257,30 @@ mod tests { assert_eq!(uri, "https://cache.nixos.org"); } + #[test] + #[cfg(nix_at_least = "2.26" /* get_storedir */)] + fn parse_store_path_ok() { + let mut store = crate::store::Store::open("dummy://", []).unwrap(); + let store_dir = store.get_storedir().unwrap(); + let store_path_string = + format!("{store_dir}/rdd4pnr4x9rqc9wgbibhngv217w2xvxl-bash-interactive-5.2p26"); + let store_path = store.parse_store_path(store_path_string.as_str()).unwrap(); + assert_eq!(store_path.name().unwrap(), "bash-interactive-5.2p26"); + } + + #[test] + fn parse_store_path_fail() { + let mut store = crate::store::Store::open("dummy://", []).unwrap(); + let store_path_string = format!("bash-interactive-5.2p26"); + let r = store.parse_store_path(store_path_string.as_str()); + match r { + Err(e) => { + assert!(e.to_string().contains("bash-interactive-5.2p26")); + } + _ => panic!("Expected error"), + } + } + #[test] fn weak_ref() { let mut store = Store::open("auto", HashMap::new()).unwrap(); From d19dd45bbf856af50ee0d7564783f9ab8d299bc0 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 17 Dec 2024 10:40:03 +0100 Subject: [PATCH 129/306] fix: Mark all pointer manipulation as unsafe See b43455fdd0468f067741a79a7031ba2fa907f0eb for rationale (cherry picked from commit b9996c6ddd3973cd419930210bf11a4d1bc6350b) --- rust/nix-expr/src/eval_state.rs | 35 ++++++++++++++++++++------------- rust/nix-expr/src/value.rs | 17 +++++++++++++--- rust/nix-store/src/store.rs | 10 ++++++++-- 3 files changed, 43 insertions(+), 19 deletions(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 46802d3..db7c97e 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -64,7 +64,10 @@ struct EvalStateRef { eval_state: NonNull, } impl EvalStateRef { - fn as_ptr(&self) -> *mut raw::EvalState { + /// # Safety + /// + /// This function is unsafe because it returns a raw pointer. The caller must ensure that the pointer is not used beyond the lifetime of the underlying [raw::EvalState]. + unsafe fn as_ptr(&self) -> *mut raw::EvalState { self.eval_state.as_ptr() } } @@ -122,7 +125,11 @@ impl EvalState { context, }) } - pub fn raw_ptr(&self) -> *mut raw::EvalState { + + /// # Safety + /// + /// This function is unsafe because it returns a raw pointer. The caller must ensure that the pointer is not used beyond the lifetime of this `EvalState`. + pub unsafe fn raw_ptr(&self) -> *mut raw::EvalState { self.eval_state.as_ptr() } pub fn store(&self) -> &Store { @@ -255,15 +262,15 @@ impl EvalState { } let attr_name = CString::new(attr_name) .with_context(|| "require_attrs_select: attrName contains null byte")?; - let v2 = unsafe { - check_call!(raw::get_attr_byname( + unsafe { + let v2 = check_call!(raw::get_attr_byname( &mut self.context, v.raw_ptr(), self.eval_state.as_ptr(), attr_name.as_ptr() - )) - }?; - Ok(Value::new(v2)) + ))?; + Ok(Value::new(v2)) + } } /// Evaluate, require that the value is an attrset, and select an attribute by name. @@ -292,7 +299,7 @@ impl EvalState { attr_name.as_ptr() )) }?; - Ok(v2.map(Value::new)) + Ok(v2.map(|x| unsafe { Value::new(x) })) } /// Create a new value containing the passed string. @@ -442,8 +449,8 @@ impl EvalState { #[doc(alias = "nix_value_call_multi")] pub fn call_multi(&mut self, f: &Value, args: &[Value]) -> Result { let value = self.new_value_uninitialized()?; - let mut args_ptrs = args.iter().map(|a| a.raw_ptr()).collect::>(); unsafe { + let mut args_ptrs = args.iter().map(|a| a.raw_ptr()).collect::>(); check_call!(raw::value_call_multi( &mut self.context, self.eval_state.as_ptr(), @@ -473,13 +480,13 @@ impl EvalState { } fn new_value_uninitialized(&mut self) -> Result { - let value = unsafe { - check_call!(raw::alloc_value( + unsafe { + let value = check_call!(raw::alloc_value( &mut self.context, self.eval_state.as_ptr() - )) - }?; - Ok(Value::new(value)) + ))?; + Ok(Value::new(value)) + } } /// Create a new Nix function that is implemented by a Rust function. diff --git a/rust/nix-expr/src/value.rs b/rust/nix-expr/src/value.rs index e15d5b9..ca7b5a4 100644 --- a/rust/nix-expr/src/value.rs +++ b/rust/nix-expr/src/value.rs @@ -57,7 +57,11 @@ impl Value { /// Take ownership of a new Value. /// /// This does not call `nix_gc_incref`, but does call `nix_gc_decref` when dropped. - pub(crate) fn new(inner: *mut raw::Value) -> Self { + /// + /// # Safety + /// + /// The caller must ensure that the provided `inner` has a positive reference count, and that `inner` is not used after the returned `Value` is dropped. + pub(crate) unsafe fn new(inner: *mut raw::Value) -> Self { Value { inner: NonNull::new(inner).unwrap(), } @@ -66,13 +70,20 @@ impl Value { /// Borrow a reference to a Value. /// /// This calls `nix_gc_incref`, and the returned Value will call `nix_gc_decref` when dropped. - pub(crate) fn new_borrowed(inner: *mut raw::Value) -> Self { + /// + /// # Safety + /// + /// The caller must ensure that the provided `inner` has a positive reference count. + pub(crate) unsafe fn new_borrowed(inner: *mut raw::Value) -> Self { let v = Value::new(inner); unsafe { raw::value_incref(null_mut(), inner) }; v } - pub(crate) fn raw_ptr(&self) -> *mut raw::Value { + /// # Safety + /// + /// The caller must ensure that the returned pointer is not used after the `Value` is dropped. + pub(crate) unsafe fn raw_ptr(&self) -> *mut raw::Value { self.inner.as_ptr() } } diff --git a/rust/nix-store/src/store.rs b/rust/nix-store/src/store.rs index dc6c9cc..d1d5a72 100644 --- a/rust/nix-store/src/store.rs +++ b/rust/nix-store/src/store.rs @@ -24,7 +24,10 @@ struct StoreRef { inner: NonNull, } impl StoreRef { - pub fn ptr(&self) -> *mut raw::Store { + /// # Safety + /// + /// The returned pointer is only valid as long as the `StoreRef` is alive. + pub unsafe fn ptr(&self) -> *mut raw::Store { self.inner.as_ptr() } } @@ -157,7 +160,10 @@ impl Store { Ok(store) } - pub fn raw_ptr(&self) -> *mut raw::Store { + /// # Safety + /// + /// The returned pointer is only valid as long as the `Store` is alive. + pub unsafe fn raw_ptr(&self) -> *mut raw::Store { self.inner.ptr() } From 1a3ade449523b98ada1214dad7faadafecd73eeb Mon Sep 17 00:00:00 2001 From: Hercules CI Effects Date: Thu, 2 Jan 2025 00:58:45 +0000 Subject: [PATCH 130/306] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'flake-parts': 'github:hercules-ci/flake-parts/506278e768c2a08bec68eb62932193e341f55c90?narHash=sha256-hgmguH29K2fvs9szpq2r3pz2/8cJd2LPS%2Bb4tfNFCwE%3D' (2024-11-01) → 'github:hercules-ci/flake-parts/f2f7418ce0ab4a5309a4596161d154cfc877af66?narHash=sha256-soePLBazJk0qQdDVhdbM98vYdssfs3WFedcq%2BraipRI%3D' (2025-01-01) • Updated input 'flake-parts/nixpkgs-lib': 'https://github.com/NixOS/nixpkgs/archive/cc2f28000298e1269cea6612cd06ec9979dd5d7f.tar.gz?narHash=sha256-lXvH/vOfb4aGYyvFmZK/HlsNsr/0CVWlwYvo2rxJk3s%3D' (2024-11-01) → 'https://github.com/NixOS/nixpkgs/archive/e9b51731911566bbf7e4895475a87fe06961de0b.tar.gz?narHash=sha256-CewEm1o2eVAnoqb6Ml%2BQi9Gg/EfNAxbRx1lANGVyoLI%3D' (2025-01-01) • Updated input 'nix': 'github:NixOS/nix/2f32cf6d90df599d620eea5c48b9469f1c251025?narHash=sha256-BeyD6r1rQWaL%2BK7vLRSiCcZG4czEfmBYEL3kvBS2nGw%3D' (2024-12-16) → 'github:NixOS/nix/deb3533eab9a36807d082992cccfbb7c4df3451e?narHash=sha256-RvFxgWnJznjdH0pu5O9ZevFhMbkVO7uMBopEjSmfuJc%3D' (2025-01-01) • Updated input 'nix/flake-compat': 'github:edolstra/flake-compat/0f9255e01c2351cc7d116c072cb317785dd33b33?narHash=sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U%3D' (2023-10-04) → 'github:edolstra/flake-compat/ff81ac966bb2cae68946d5ed5fc4994f96d0ffec?narHash=sha256-NeCCThCEP3eCl2l/%2B27kNNK7QrwZB1IJCrXfrbv5oqU%3D' (2024-12-04) • Updated input 'nix/flake-parts': 'github:hercules-ci/flake-parts/9227223f6d922fee3c7b190b2cc238a99527bbb7?narHash=sha256-pQMhCCHyQGRzdfAkdJ4cIWiw%2BJNuWsTX7f0ZYSyz0VY%3D' (2024-07-03) → 'github:hercules-ci/flake-parts/205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9?narHash=sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c%3D' (2024-12-04) • Updated input 'nix/git-hooks-nix': 'github:cachix/git-hooks.nix/f451c19376071a90d8c58ab1a953c6e9840527fd?narHash=sha256-6FPUl7HVtvRHCCBQne7Ylp4p%2BdpP3P/OYuzjztZ4s70%3D' (2024-07-15) → 'github:cachix/git-hooks.nix/aa9f40c906904ebd83da78e7f328cd8aeaeae785?narHash=sha256-NdaCraHPp8iYMWzdXAt5Nv6sA3MUzlCiGiR586TCwo0%3D' (2024-12-15) • Removed input 'nix/libgit2' • Updated input 'nix-cargo-integration': 'github:yusdacra/nix-cargo-integration/413617712f5189397cdf602485f89bf2b0a0e4af?narHash=sha256-g7TCUozMeW3q5Uc%2BwmZI64yzFucQ3SYlZQepo7prarA%3D' (2024-12-01) → 'github:yusdacra/nix-cargo-integration/ff12a974d569115027506ee59014ec2e7fdea3bf?narHash=sha256-1MjA5fAjg3eqwBkL/QzMz4c%2B87oem3vh8EXGRXjF1ug%3D' (2025-01-01) • Updated input 'nix-cargo-integration/dream2nix': 'github:nix-community/dream2nix/a8dac99db44307fdecead13a39c584b97812d0d4?narHash=sha256-ViyEMSYwaza6y55XTDrsRi2K4YKCLsefMTorjWSE27s%3D' (2024-11-21) → 'github:nix-community/dream2nix/8ce6284ff58208ed8961681276f82c2f8f978ef4?narHash=sha256-n5CwhmqKxifuD4Sq4WuRP/h5LO6f23cGnSAuJemnd/4%3D' (2024-12-25) • Updated input 'nix-cargo-integration/parts': 'github:hercules-ci/flake-parts/506278e768c2a08bec68eb62932193e341f55c90?narHash=sha256-hgmguH29K2fvs9szpq2r3pz2/8cJd2LPS%2Bb4tfNFCwE%3D' (2024-11-01) → 'github:hercules-ci/flake-parts/205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9?narHash=sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c%3D' (2024-12-04) • Updated input 'nix-cargo-integration/rust-overlay': 'github:oxalica/rust-overlay/8e18f10703112e6c33e1c0d8b93e8305f6f0a75c?narHash=sha256-Chv9%2B3zrf1DhdB9JyskjoV0vJbCQEgkVqrU3p4RPLv8%3D' (2024-12-01) → 'github:oxalica/rust-overlay/a00807363a8a6cae6c3fa84ff494bf9d96333674?narHash=sha256-%2BskLL6mq/T7s6J5YmSp89ivQOHBPQ40GEU2n8yqp6bs%3D' (2025-01-01) • Updated input 'nix-cargo-integration/treefmt': 'github:numtide/treefmt-nix/6209c381904cab55796c5d7350e89681d3b2a8ef?narHash=sha256-2qbdorpq0TXHBWbVXaTqKoikN4bqAtAplTwGuII%2BoAc%3D' (2024-11-29) → 'github:numtide/treefmt-nix/56c0ecd79f7ba01a0ec027da015df751d6ca3ae7?narHash=sha256-Q6xAmciTXDtZfUxf6c15QqtRR8BvX4edYPstF/uoqMk%3D' (2024-12-31) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/970e93b9f82e2a0f3675757eb0bfc73297cc6370?narHash=sha256-jNRNr49UiuIwaarqijgdTR2qLPifxsVhlJrKzQ8XUIE%3D' (2024-11-28) → 'github:NixOS/nixpkgs/88195a94f390381c6afcdaa933c2f6ff93959cb4?narHash=sha256-0q9NGQySwDQc7RhAV2ukfnu7Gxa5/ybJ2ANT8DQrQrs%3D' (2024-12-29) • Updated input 'nixpkgs-old': 'github:NixOS/nixpkgs/7e1ca67996afd8233d9033edd26e442836cc2ad6?narHash=sha256-8qwPSE2g1othR1u4uP86NXxm6i7E9nHPyJX3m3lx7Q4%3D' (2024-12-01) → 'github:NixOS/nixpkgs/b134951a4c9f3c995fd7be05f3243f8ecd65d798?narHash=sha256-OnSAY7XDSx7CtDoqNh8jwVwh4xNL/2HaJxGjryLWzX8%3D' (2024-12-30) (cherry picked from commit ad6be2541828e900f64cefc4d00bc982c92a3d28) --- flake.lock | 137 +++++++++++++++++++++++------------------------------ 1 file changed, 60 insertions(+), 77 deletions(-) diff --git a/flake.lock b/flake.lock index 3dbb956..090186b 100644 --- a/flake.lock +++ b/flake.lock @@ -27,11 +27,11 @@ "pyproject-nix": "pyproject-nix" }, "locked": { - "lastModified": 1732214960, - "narHash": "sha256-ViyEMSYwaza6y55XTDrsRi2K4YKCLsefMTorjWSE27s=", + "lastModified": 1735160684, + "narHash": "sha256-n5CwhmqKxifuD4Sq4WuRP/h5LO6f23cGnSAuJemnd/4=", "owner": "nix-community", "repo": "dream2nix", - "rev": "a8dac99db44307fdecead13a39c584b97812d0d4", + "rev": "8ce6284ff58208ed8961681276f82c2f8f978ef4", "type": "github" }, "original": { @@ -43,11 +43,11 @@ "flake-compat": { "flake": false, "locked": { - "lastModified": 1696426674, - "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "lastModified": 1733328505, + "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=", "owner": "edolstra", "repo": "flake-compat", - "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", "type": "github" }, "original": { @@ -77,11 +77,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1726153070, - "narHash": "sha256-HO4zgY0ekfwO5bX0QH/3kJ/h4KvUDFZg8YpkNwIbg1U=", + "lastModified": 1735774679, + "narHash": "sha256-soePLBazJk0qQdDVhdbM98vYdssfs3WFedcq+raipRI=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "bcef6817a8b2aa20a5a6dbb19b43e63c5bf8619a", + "rev": "f2f7418ce0ab4a5309a4596161d154cfc877af66", "type": "github" }, "original": { @@ -98,11 +98,11 @@ ] }, "locked": { - "lastModified": 1719994518, - "narHash": "sha256-pQMhCCHyQGRzdfAkdJ4cIWiw+JNuWsTX7f0ZYSyz0VY=", + "lastModified": 1733312601, + "narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "9227223f6d922fee3c7b190b2cc238a99527bbb7", + "rev": "205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9", "type": "github" }, "original": { @@ -111,19 +111,34 @@ "type": "github" } }, - "libgit2": { - "flake": false, + "git-hooks-nix": { + "inputs": { + "flake-compat": [ + "nix" + ], + "gitignore": [ + "nix" + ], + "nixpkgs": [ + "nix", + "nixpkgs" + ], + "nixpkgs-stable": [ + "nix", + "nixpkgs" + ] + }, "locked": { - "lastModified": 1724328629, - "narHash": "sha256-7SuD4k+ORwFPwDm5Qr5eSV6GMVWjMfFed9KYi8riUQo=", - "owner": "libgit2", - "repo": "libgit2", - "rev": "782e29c906f6e44b120843356f286b6a97d89f88", + "lastModified": 1734279981, + "narHash": "sha256-NdaCraHPp8iYMWzdXAt5Nv6sA3MUzlCiGiR586TCwo0=", + "owner": "cachix", + "repo": "git-hooks.nix", + "rev": "aa9f40c906904ebd83da78e7f328cd8aeaeae785", "type": "github" }, "original": { - "owner": "libgit2", - "repo": "libgit2", + "owner": "cachix", + "repo": "git-hooks.nix", "type": "github" } }, @@ -147,20 +162,19 @@ "inputs": { "flake-compat": "flake-compat", "flake-parts": "flake-parts_2", - "libgit2": "libgit2", + "git-hooks-nix": "git-hooks-nix", "nixpkgs": [ "nixpkgs" ], "nixpkgs-23-11": "nixpkgs-23-11", - "nixpkgs-regression": "nixpkgs-regression", - "pre-commit-hooks": "pre-commit-hooks" + "nixpkgs-regression": "nixpkgs-regression" }, "locked": { - "lastModified": 1734340930, - "narHash": "sha256-BeyD6r1rQWaL+K7vLRSiCcZG4czEfmBYEL3kvBS2nGw=", + "lastModified": 1735773647, + "narHash": "sha256-RvFxgWnJznjdH0pu5O9ZevFhMbkVO7uMBopEjSmfuJc=", "owner": "NixOS", "repo": "nix", - "rev": "2f32cf6d90df599d620eea5c48b9469f1c251025", + "rev": "deb3533eab9a36807d082992cccfbb7c4df3451e", "type": "github" }, "original": { @@ -183,11 +197,11 @@ "treefmt": "treefmt" }, "locked": { - "lastModified": 1733033761, - "narHash": "sha256-g7TCUozMeW3q5Uc+wmZI64yzFucQ3SYlZQepo7prarA=", + "lastModified": 1735712175, + "narHash": "sha256-1MjA5fAjg3eqwBkL/QzMz4c+87oem3vh8EXGRXjF1ug=", "owner": "yusdacra", "repo": "nix-cargo-integration", - "rev": "413617712f5189397cdf602485f89bf2b0a0e4af", + "rev": "ff12a974d569115027506ee59014ec2e7fdea3bf", "type": "github" }, "original": { @@ -198,11 +212,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1732837521, - "narHash": "sha256-jNRNr49UiuIwaarqijgdTR2qLPifxsVhlJrKzQ8XUIE=", + "lastModified": 1735471104, + "narHash": "sha256-0q9NGQySwDQc7RhAV2ukfnu7Gxa5/ybJ2ANT8DQrQrs=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "970e93b9f82e2a0f3675757eb0bfc73297cc6370", + "rev": "88195a94f390381c6afcdaa933c2f6ff93959cb4", "type": "github" }, "original": { @@ -230,14 +244,14 @@ }, "nixpkgs-lib": { "locked": { - "lastModified": 1725233747, - "narHash": "sha256-Ss8QWLXdr2JCBPcYChJhz4xJm+h/xjl4G0c0XlP6a74=", + "lastModified": 1735774519, + "narHash": "sha256-CewEm1o2eVAnoqb6Ml+Qi9Gg/EfNAxbRx1lANGVyoLI=", "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/356624c12086a18f2ea2825fed34523d60ccc4e3.tar.gz" + "url": "https://github.com/NixOS/nixpkgs/archive/e9b51731911566bbf7e4895475a87fe06961de0b.tar.gz" }, "original": { "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/cc2f28000298e1269cea6612cd06ec9979dd5d7f.tar.gz" + "url": "https://github.com/NixOS/nixpkgs/archive/e9b51731911566bbf7e4895475a87fe06961de0b.tar.gz" } }, "nixpkgs-regression": { @@ -264,11 +278,11 @@ ] }, "locked": { - "lastModified": 1730504689, - "narHash": "sha256-hgmguH29K2fvs9szpq2r3pz2/8cJd2LPS+b4tfNFCwE=", + "lastModified": 1733312601, + "narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "506278e768c2a08bec68eb62932193e341f55c90", + "rev": "205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9", "type": "github" }, "original": { @@ -277,37 +291,6 @@ "type": "github" } }, - "pre-commit-hooks": { - "inputs": { - "flake-compat": [ - "nix" - ], - "gitignore": [ - "nix" - ], - "nixpkgs": [ - "nix", - "nixpkgs" - ], - "nixpkgs-stable": [ - "nix", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1724857454, - "narHash": "sha256-Qyl9Q4QMTLZnnBb/8OuQ9LSkzWjBU1T5l5zIzTxkkhk=", - "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "rev": "4509ca64f1084e73bc7a721b20c669a8d4c5ebe6", - "type": "github" - }, - "original": { - "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "type": "github" - } - }, "purescript-overlay": { "inputs": { "flake-compat": "flake-compat_2", @@ -365,11 +348,11 @@ ] }, "locked": { - "lastModified": 1733020719, - "narHash": "sha256-Chv9+3zrf1DhdB9JyskjoV0vJbCQEgkVqrU3p4RPLv8=", + "lastModified": 1735698720, + "narHash": "sha256-+skLL6mq/T7s6J5YmSp89ivQOHBPQ40GEU2n8yqp6bs=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "8e18f10703112e6c33e1c0d8b93e8305f6f0a75c", + "rev": "a00807363a8a6cae6c3fa84ff494bf9d96333674", "type": "github" }, "original": { @@ -409,11 +392,11 @@ ] }, "locked": { - "lastModified": 1732894027, - "narHash": "sha256-2qbdorpq0TXHBWbVXaTqKoikN4bqAtAplTwGuII+oAc=", + "lastModified": 1735653038, + "narHash": "sha256-Q6xAmciTXDtZfUxf6c15QqtRR8BvX4edYPstF/uoqMk=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "6209c381904cab55796c5d7350e89681d3b2a8ef", + "rev": "56c0ecd79f7ba01a0ec027da015df751d6ca3ae7", "type": "github" }, "original": { From d693957c1addf5f91fcacb94484c9884e60c936c Mon Sep 17 00:00:00 2001 From: Hercules CI Effects Date: Thu, 2 Jan 2025 00:58:47 +0000 Subject: [PATCH 131/306] dev/flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'hercules-ci-effects': 'github:hercules-ci/hercules-ci-effects/b89ac4d66d618b915b1f0a408e2775fe3821d141?narHash=sha256-mnynlrPeiW0nUQ8KGZHb3WyxAxA3Ye/BH8gMjdoKP6E%3D' (2024-11-06) → 'github:hercules-ci/hercules-ci-effects/f6233b5cfbada692d93a73d6ed35bdbfd0fdb9c4?narHash=sha256-cwk53OX1S1bCFY09zydubZNmmwcx9l5XEba8mVYuNE4%3D' (2025-01-01) • Updated input 'hercules-ci-effects/flake-parts': 'github:hercules-ci/flake-parts/9126214d0a59633752a136528f5f3b9aa8565b7d?narHash=sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm%2BGpZNw%3D' (2024-04-01) → 'github:hercules-ci/flake-parts/205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9?narHash=sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c%3D' (2024-12-04) • Updated input 'hercules-ci-effects/nixpkgs': 'github:NixOS/nixpkgs/6143fc5eeb9c4f00163267708e26191d1e918932?narHash=sha256-%2Bz/XjO3QJs5rLE5UOf015gdVauVRQd2vZtsFkaXBq2Y%3D' (2024-04-21) → 'github:NixOS/nixpkgs/55d15ad12a74eb7d4646254e13638ad0c4128776?narHash=sha256-M1%2BuCoV5igihRfcUKrr1riygbe73/dzNnzPsmaLCmpo%3D' (2024-12-03) • Updated input 'pre-commit-hooks-nix': 'github:cachix/pre-commit-hooks.nix/3308484d1a443fc5bc92012435d79e80458fe43c?narHash=sha256-mnTbjpdqF0luOkou8ZFi2asa1N3AA2CchR/RqCNmsGE%3D' (2024-11-19) → 'github:cachix/pre-commit-hooks.nix/f0f0dc4920a903c3e08f5bdb9246bb572fcae498?narHash=sha256-ulZN7ps8nBV31SE%2BdwkDvKIzvN6hroRY8sYOT0w%2BE28%3D' (2024-12-21) (cherry picked from commit a5912eab101cf9394e4450739148dd1f760161bc) --- dev/flake.lock | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/dev/flake.lock b/dev/flake.lock index b69d295..39cb762 100644 --- a/dev/flake.lock +++ b/dev/flake.lock @@ -24,11 +24,11 @@ ] }, "locked": { - "lastModified": 1712014858, - "narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=", + "lastModified": 1733312601, + "narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "9126214d0a59633752a136528f5f3b9aa8565b7d", + "rev": "205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9", "type": "github" }, "original": { @@ -63,11 +63,11 @@ "nixpkgs": "nixpkgs" }, "locked": { - "lastModified": 1730903510, - "narHash": "sha256-mnynlrPeiW0nUQ8KGZHb3WyxAxA3Ye/BH8gMjdoKP6E=", + "lastModified": 1735695978, + "narHash": "sha256-cwk53OX1S1bCFY09zydubZNmmwcx9l5XEba8mVYuNE4=", "owner": "hercules-ci", "repo": "hercules-ci-effects", - "rev": "b89ac4d66d618b915b1f0a408e2775fe3821d141", + "rev": "f6233b5cfbada692d93a73d6ed35bdbfd0fdb9c4", "type": "github" }, "original": { @@ -78,11 +78,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1713714899, - "narHash": "sha256-+z/XjO3QJs5rLE5UOf015gdVauVRQd2vZtsFkaXBq2Y=", + "lastModified": 1733212471, + "narHash": "sha256-M1+uCoV5igihRfcUKrr1riygbe73/dzNnzPsmaLCmpo=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "6143fc5eeb9c4f00163267708e26191d1e918932", + "rev": "55d15ad12a74eb7d4646254e13638ad0c4128776", "type": "github" }, "original": { @@ -116,11 +116,11 @@ "nixpkgs-stable": "nixpkgs-stable" }, "locked": { - "lastModified": 1732021966, - "narHash": "sha256-mnTbjpdqF0luOkou8ZFi2asa1N3AA2CchR/RqCNmsGE=", + "lastModified": 1734797603, + "narHash": "sha256-ulZN7ps8nBV31SE+dwkDvKIzvN6hroRY8sYOT0w+E28=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "3308484d1a443fc5bc92012435d79e80458fe43c", + "rev": "f0f0dc4920a903c3e08f5bdb9246bb572fcae498", "type": "github" }, "original": { From 32136109fc99c1e362cc150e11f683c3e87b3483 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 8 Jan 2025 11:14:09 +0100 Subject: [PATCH 132/306] maint: Ask GCC for system headers In the previous update commit, the Nix `cc.version` didn't match the in-output directory name anymore, causing a build failure. By asking GCC properly, we are robust against such discrepancies. The script isn't great, relying on GCC "log" output specifics, but it seems that the GCC output has been like this for a while, and the lines before and after seem to be intentional about their purpose... (cherry picked from commit 68f928555660cb914105b0ad668d6cadc25223e9) --- dev/flake-module.nix | 3 ++- rust/bindgen-gcc.sh | 18 ++++++++++++++++++ rust/nci.nix | 15 ++++++++------- 3 files changed, 28 insertions(+), 8 deletions(-) create mode 100644 rust/bindgen-gcc.sh diff --git a/dev/flake-module.nix b/dev/flake-module.nix index 1f07ecb..ca5df3a 100644 --- a/dev/flake-module.nix +++ b/dev/flake-module.nix @@ -36,7 +36,7 @@ inputsFrom = [ config.nci.outputs.nix-bindings.devShell ]; inherit (config.nci.outputs.nix-bindings.devShell.env) LIBCLANG_PATH - BINDGEN_EXTRA_CLANG_ARGS + NIX_CC_UNWRAPPED ; NIX_DEBUG_INFO_DIRS = let @@ -66,6 +66,7 @@ ]; shellHook = '' ${config.pre-commit.installationScript} + source ${../rust/bindgen-gcc.sh} echo 1>&2 "Welcome to the development shell!" ''; # rust-analyzer needs a NIX_PATH for some reason diff --git a/rust/bindgen-gcc.sh b/rust/bindgen-gcc.sh new file mode 100644 index 0000000..d904b61 --- /dev/null +++ b/rust/bindgen-gcc.sh @@ -0,0 +1,18 @@ + +# Rust bindgen uses Clang to generate bindings, but that means that it can't +# find the "system" or compiler headers when the stdenv compiler is GCC. +# This script tells it where to find them. + +echo "Extending BINDGEN_EXTRA_CLANG_ARGS with system include paths..." 2>&1 +BINDGEN_EXTRA_CLANG_ARGS="''${BINDGEN_EXTRA_CLANG_ARGS:-}" +export BINDGEN_EXTRA_CLANG_ARGS +include_paths=$( + echo | $NIX_CC_UNWRAPPED -v -E -x c - 2>&1 \ + | awk '/#include <...> search starts here:/{flag=1;next} \ + /End of search list./{flag=0} \ + flag==1 {print $1}' +) +for path in $include_paths; do + echo " - $path" 2>&1 + BINDGEN_EXTRA_CLANG_ARGS="$BINDGEN_EXTRA_CLANG_ARGS -I$path" +done diff --git a/rust/nci.nix b/rust/nci.nix index 08edb9f..25c7dfc 100644 --- a/rust/nci.nix +++ b/rust/nci.nix @@ -13,6 +13,11 @@ nativeBuildInputs = [ pkgs.pkg-config ]; + # bindgen uses clang to generate bindings, but it doesn't know where to + # find our stdenv cc's headers, so when it's gcc, we need to tell it. + postConfigure = lib.optionalString pkgs.stdenv.cc.isGNU '' + source ${./bindgen-gcc.sh} + ''; # Prepare the environment for Nix to work. # Nix does not provide a suitable environment for running itself in # the sandbox - not by default. We configure it to use a relocated store. @@ -46,13 +51,9 @@ null # don't set the variable else lib.makeLibraryPath [ pkgs.buildPackages.llvmPackages.clang-unwrapped ]; - BINDGEN_EXTRA_CLANG_ARGS = - if pkgs.stdenv.cc.isClang then - null # don't set the variable - else - "-I${pkgs.stdenv.cc.libc.dev}/include" - + " -I${lib.getDev pkgs.stdenv.cc.cc}/lib/gcc/${pkgs.stdenv.hostPlatform.config}/${pkgs.stdenv.cc.cc.version}/include" - ; + } // lib.optionalAttrs pkgs.stdenv.cc.isGNU { + # Avoid cc wrapper, because we only need to add the compiler/"system" dirs + NIX_CC_UNWRAPPED = "${pkgs.stdenv.cc.cc}/bin/gcc"; }; }; }; From c7278d429faa72cb37a8248c93e89acc771e9c94 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 8 Jan 2025 17:32:18 +0100 Subject: [PATCH 133/306] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'flake-parts': 'github:hercules-ci/flake-parts/f2f7418ce0ab4a5309a4596161d154cfc877af66?narHash=sha256-soePLBazJk0qQdDVhdbM98vYdssfs3WFedcq%2BraipRI%3D' (2025-01-01) → 'github:hercules-ci/flake-parts/b905f6fc23a9051a6e1b741e1438dbfc0634c6de?narHash=sha256-%2Bhu54pAoLDEZT9pjHlqL9DNzWz0NbUn8NEAHP7PQPzU%3D' (2025-01-06) • Updated input 'nix': 'github:NixOS/nix/deb3533eab9a36807d082992cccfbb7c4df3451e?narHash=sha256-RvFxgWnJznjdH0pu5O9ZevFhMbkVO7uMBopEjSmfuJc%3D' (2025-01-01) → 'github:NixOS/nix/5230d3ecc4cd3a3d965902a56b5a21bcc99821c3?narHash=sha256-u6OD0BH%2BUxyfrWMMpBfM5cz/TDWU9lxJOujgzqBnN9A%3D' (2025-01-08) • Updated input 'nix-cargo-integration': 'github:yusdacra/nix-cargo-integration/ff12a974d569115027506ee59014ec2e7fdea3bf?narHash=sha256-1MjA5fAjg3eqwBkL/QzMz4c%2B87oem3vh8EXGRXjF1ug%3D' (2025-01-01) → 'github:yusdacra/nix-cargo-integration/1ce1f666c955e73f65de74f3a8c3ca2c3e5d741b?narHash=sha256-nOWLP6pSblYrCipiBb7/SQpGhNe7AHT8m9f%2B%2Bb8/Ni4%3D' (2025-01-08) • Updated input 'nix-cargo-integration/parts': 'github:hercules-ci/flake-parts/205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9?narHash=sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c%3D' (2024-12-04) → 'github:hercules-ci/flake-parts/b905f6fc23a9051a6e1b741e1438dbfc0634c6de?narHash=sha256-%2Bhu54pAoLDEZT9pjHlqL9DNzWz0NbUn8NEAHP7PQPzU%3D' (2025-01-06) • Updated input 'nix-cargo-integration/rust-overlay': 'github:oxalica/rust-overlay/a00807363a8a6cae6c3fa84ff494bf9d96333674?narHash=sha256-%2BskLL6mq/T7s6J5YmSp89ivQOHBPQ40GEU2n8yqp6bs%3D' (2025-01-01) → 'github:oxalica/rust-overlay/a0b81d4fa349d9af1765b0f0b4a899c13776f706?narHash=sha256-IKrk7RL%2BQ/2NC6%2BQl6dwwCNZI6T6JH2grTdJaVWHF0A%3D' (2025-01-08) • Updated input 'nix-cargo-integration/treefmt': 'github:numtide/treefmt-nix/56c0ecd79f7ba01a0ec027da015df751d6ca3ae7?narHash=sha256-Q6xAmciTXDtZfUxf6c15QqtRR8BvX4edYPstF/uoqMk%3D' (2024-12-31) → 'github:numtide/treefmt-nix/13c913f5deb3a5c08bb810efd89dc8cb24dd968b?narHash=sha256-p2r8xhQZ3TYIEKBoiEhllKWQqWNJNoT9v64Vmg4q8Zw%3D' (2025-01-06) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/88195a94f390381c6afcdaa933c2f6ff93959cb4?narHash=sha256-0q9NGQySwDQc7RhAV2ukfnu7Gxa5/ybJ2ANT8DQrQrs%3D' (2024-12-29) → 'github:NixOS/nixpkgs/8f3e1f807051e32d8c95cd12b9b421623850a34d?narHash=sha256-/qlNWm/IEVVH7GfgAIyP6EsVZI6zjAx1cV5zNyrs%2BrI%3D' (2025-01-04) (cherry picked from commit fe77bf1a1f3777d7e961c162ccce3dd7564a624a) --- flake.lock | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/flake.lock b/flake.lock index 090186b..6aff387 100644 --- a/flake.lock +++ b/flake.lock @@ -77,11 +77,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1735774679, - "narHash": "sha256-soePLBazJk0qQdDVhdbM98vYdssfs3WFedcq+raipRI=", + "lastModified": 1736143030, + "narHash": "sha256-+hu54pAoLDEZT9pjHlqL9DNzWz0NbUn8NEAHP7PQPzU=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "f2f7418ce0ab4a5309a4596161d154cfc877af66", + "rev": "b905f6fc23a9051a6e1b741e1438dbfc0634c6de", "type": "github" }, "original": { @@ -170,11 +170,11 @@ "nixpkgs-regression": "nixpkgs-regression" }, "locked": { - "lastModified": 1735773647, - "narHash": "sha256-RvFxgWnJznjdH0pu5O9ZevFhMbkVO7uMBopEjSmfuJc=", + "lastModified": 1736342444, + "narHash": "sha256-u6OD0BH+UxyfrWMMpBfM5cz/TDWU9lxJOujgzqBnN9A=", "owner": "NixOS", "repo": "nix", - "rev": "deb3533eab9a36807d082992cccfbb7c4df3451e", + "rev": "5230d3ecc4cd3a3d965902a56b5a21bcc99821c3", "type": "github" }, "original": { @@ -197,11 +197,11 @@ "treefmt": "treefmt" }, "locked": { - "lastModified": 1735712175, - "narHash": "sha256-1MjA5fAjg3eqwBkL/QzMz4c+87oem3vh8EXGRXjF1ug=", + "lastModified": 1736316962, + "narHash": "sha256-nOWLP6pSblYrCipiBb7/SQpGhNe7AHT8m9f++b8/Ni4=", "owner": "yusdacra", "repo": "nix-cargo-integration", - "rev": "ff12a974d569115027506ee59014ec2e7fdea3bf", + "rev": "1ce1f666c955e73f65de74f3a8c3ca2c3e5d741b", "type": "github" }, "original": { @@ -212,11 +212,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1735471104, - "narHash": "sha256-0q9NGQySwDQc7RhAV2ukfnu7Gxa5/ybJ2ANT8DQrQrs=", + "lastModified": 1736012469, + "narHash": "sha256-/qlNWm/IEVVH7GfgAIyP6EsVZI6zjAx1cV5zNyrs+rI=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "88195a94f390381c6afcdaa933c2f6ff93959cb4", + "rev": "8f3e1f807051e32d8c95cd12b9b421623850a34d", "type": "github" }, "original": { @@ -278,11 +278,11 @@ ] }, "locked": { - "lastModified": 1733312601, - "narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=", + "lastModified": 1736143030, + "narHash": "sha256-+hu54pAoLDEZT9pjHlqL9DNzWz0NbUn8NEAHP7PQPzU=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9", + "rev": "b905f6fc23a9051a6e1b741e1438dbfc0634c6de", "type": "github" }, "original": { @@ -348,11 +348,11 @@ ] }, "locked": { - "lastModified": 1735698720, - "narHash": "sha256-+skLL6mq/T7s6J5YmSp89ivQOHBPQ40GEU2n8yqp6bs=", + "lastModified": 1736303309, + "narHash": "sha256-IKrk7RL+Q/2NC6+Ql6dwwCNZI6T6JH2grTdJaVWHF0A=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "a00807363a8a6cae6c3fa84ff494bf9d96333674", + "rev": "a0b81d4fa349d9af1765b0f0b4a899c13776f706", "type": "github" }, "original": { @@ -392,11 +392,11 @@ ] }, "locked": { - "lastModified": 1735653038, - "narHash": "sha256-Q6xAmciTXDtZfUxf6c15QqtRR8BvX4edYPstF/uoqMk=", + "lastModified": 1736154270, + "narHash": "sha256-p2r8xhQZ3TYIEKBoiEhllKWQqWNJNoT9v64Vmg4q8Zw=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "56c0ecd79f7ba01a0ec027da015df751d6ca3ae7", + "rev": "13c913f5deb3a5c08bb810efd89dc8cb24dd968b", "type": "github" }, "original": { From 122ee75ce30798640240449e3e73c1f9cb2fed2c Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 2 Jan 2025 07:53:55 +0100 Subject: [PATCH 134/306] fix: Darwin compat (cherry picked from commit 711d95877a039bcd5b162604a22d1c051b59a49d) --- rust/nci.nix | 8 ++++---- rust/nix-expr/src/eval_state.rs | 5 ++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/rust/nci.nix b/rust/nci.nix index 25c7dfc..79202c3 100644 --- a/rust/nci.nix +++ b/rust/nci.nix @@ -47,10 +47,10 @@ # NOTE: duplicated in flake.nix devShell env = { LIBCLANG_PATH = - if pkgs.stdenv.cc.isClang then - null # don't set the variable - else - lib.makeLibraryPath [ pkgs.buildPackages.llvmPackages.clang-unwrapped ]; + lib.makeLibraryPath [ pkgs.buildPackages.llvmPackages.clang-unwrapped ]; + BINDGEN_EXTRA_CLANG_ARGS = + # Work around missing [[deprecated]] in clang + "-x c++ -std=c++2a"; } // lib.optionalAttrs pkgs.stdenv.cc.isGNU { # Avoid cc wrapper, because we only need to add the compiler/"system" dirs NIX_CC_UNWRAPPED = "${pkgs.stdenv.cc.cc}/bin/gcc"; diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index db7c97e..b631595 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -632,7 +632,10 @@ pub fn test_init() { // When testing in the sandbox, the default build dir would be a parent of the storeDir, // which causes an error. So we set a custom build dir here. - nix_util::settings::set("sandbox-build-dir", "/custom-build-dir-for-test").unwrap(); + // Only available on linux + if cfg!(target_os = "linux") { + nix_util::settings::set("sandbox-build-dir", "/custom-build-dir-for-test").unwrap(); + } std::env::set_var("_NIX_TEST_NO_SANDBOX", "1"); // The tests run offline From 91f9697f613f1df11632e52eb708847ce7e62666 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 16 Dec 2024 12:51:03 +0100 Subject: [PATCH 135/306] packaging: Only depend on Nix libs, not CLI (cherry picked from commit 6eed09254954e23ed0e0365e3101118c550be3c4) --- rust/nci.nix | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/rust/nci.nix b/rust/nci.nix index 79202c3..87392b3 100644 --- a/rust/nci.nix +++ b/rust/nci.nix @@ -6,10 +6,18 @@ drvConfig = { mkDerivation = { buildInputs = [ - config.packages.nix # stdbool.h pkgs.stdenv.cc - ]; + ] ++ + (if config.packages.nix?libs + then + let l = config.packages.nix.libs; in [ + l.nix-expr-c + l.nix-store-c + l.nix-util-c + l.nix-flake-c + ] + else [ config.packages.nix ]); nativeBuildInputs = [ pkgs.pkg-config ]; From cbce27eb64e99f2cf25a3c376eb25307d56fadbd Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 13 Dec 2024 00:54:35 +0100 Subject: [PATCH 136/306] feat: Support Store::open from environment, not just auto Note that `"auto"` holds a strange middle ground, reading part of the general environment, but not all of it. It ignores `NIX_REMOTE` and the `store` option. (cherry picked from commit 7ba92a3793c2fe11938e802e6b61edee042b193a) --- rust/nix-expr/src/eval_state.rs | 106 ++++++++++++++++---------------- rust/nix-store/src/path.rs | 2 +- rust/nix-store/src/store.rs | 53 ++++++++++------ 3 files changed, 87 insertions(+), 74 deletions(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index b631595..99e60cf 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -154,7 +154,7 @@ impl EvalState { /// use nix_expr::value::Value; /// /// # fn main() -> anyhow::Result<()> { - /// # let mut es = EvalState::new(Store::open("auto", [])?, [])?; + /// # let mut es = EvalState::new(Store::open(None, [])?, [])?; /// let v: Value = es.eval_from_string("42", ".")?; /// assert_eq!(es.require_int(&v)?, 42); /// # Ok(()) @@ -672,7 +672,7 @@ mod tests { fn eval_state_new_and_drop() { gc_registering_current_thread(|| { // very basic test: make sure initialization doesn't crash - let store = Store::open("auto", HashMap::new()).unwrap(); + let store = Store::open(None, HashMap::new()).unwrap(); let _e = EvalState::new(store, []).unwrap(); }) .unwrap(); @@ -681,7 +681,7 @@ mod tests { #[test] fn weak_ref() { gc_registering_current_thread(|| { - let store = Store::open("auto", HashMap::new()).unwrap(); + let store = Store::open(None, HashMap::new()).unwrap(); let es = EvalState::new(store, []).unwrap(); let weak = es.weak_ref(); let _es = weak.upgrade().unwrap(); @@ -694,7 +694,7 @@ mod tests { gc_registering_current_thread(|| { let weak = { // Use a slightly different URL which is unique in the test suite, to bypass the global store cache - let store = Store::open("auto?foo=bar", HashMap::new()).unwrap(); + let store = Store::open(Some("auto?foo=bar"), HashMap::new()).unwrap(); let es = EvalState::new(store, []).unwrap(); es.weak_ref() }; @@ -715,11 +715,11 @@ mod tests { writeln!(test_file0, "{integer0}").unwrap(); writeln!(test_file1, "{integer1}").unwrap(); gc_registering_current_thread(|| { - let mut es = EvalState::new(Store::open("auto", HashMap::new()).unwrap(), []).unwrap(); + let mut es = EvalState::new(Store::open(None, HashMap::new()).unwrap(), []).unwrap(); assert!(es.eval_from_string(import_expression, "").is_err()); let mut es = EvalState::new( - Store::open("auto", HashMap::new()).unwrap(), + Store::open(None, HashMap::new()).unwrap(), [ format!("test_file0={}", test_file0.path().to_str().unwrap()).as_str(), format!("test_file1={}", test_file1.path().to_str().unwrap()).as_str(), @@ -738,7 +738,7 @@ mod tests { #[test] fn eval_state_eval_from_string() { gc_registering_current_thread(|| { - let store = Store::open("auto", HashMap::new()).unwrap(); + let store = Store::open(None, HashMap::new()).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let v = es.eval_from_string("1", "").unwrap(); let v2 = v.clone(); @@ -755,7 +755,7 @@ mod tests { #[test] fn eval_state_value_bool() { gc_registering_current_thread(|| { - let store = Store::open("auto", HashMap::new()).unwrap(); + let store = Store::open(None, HashMap::new()).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let v = es.eval_from_string("true", "").unwrap(); es.force(&v).unwrap(); @@ -768,7 +768,7 @@ mod tests { #[test] fn eval_state_value_int() { gc_registering_current_thread(|| { - let store = Store::open("auto", HashMap::new()).unwrap(); + let store = Store::open(None, HashMap::new()).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let v = es.eval_from_string("1", "").unwrap(); es.force(&v).unwrap(); @@ -783,7 +783,7 @@ mod tests { #[test] fn eval_state_require_int_forces_thunk() { gc_registering_current_thread(|| { - let store = Store::open("auto", HashMap::new()).unwrap(); + let store = Store::open(None, HashMap::new()).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let f = es.eval_from_string("x: x + 1", "").unwrap(); let a = es.eval_from_string("2", "").unwrap(); @@ -810,7 +810,7 @@ mod tests { #[test] fn make_thunk_helper_works() { gc_registering_current_thread(|| { - let store = Store::open("auto", HashMap::new()).unwrap(); + let store = Store::open(None, HashMap::new()).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let v = make_thunk(&mut es, "1"); let t = es.value_type_unforced(&v); @@ -822,7 +822,7 @@ mod tests { #[test] fn eval_state_value_attrs_names_empty() { gc_registering_current_thread(|| { - let store = Store::open("auto", HashMap::new()).unwrap(); + let store = Store::open(None, HashMap::new()).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let v = es.eval_from_string("{ }", "").unwrap(); es.force(&v).unwrap(); @@ -837,7 +837,7 @@ mod tests { #[test] fn eval_state_require_attrs_names_unsorted_forces_thunk() { gc_registering_current_thread(|| { - let store = Store::open("auto", HashMap::new()).unwrap(); + let store = Store::open(None, HashMap::new()).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let v = make_thunk(&mut es, "{ a = 1; b = 2; }"); let t = es.value_type_unforced(&v); @@ -851,7 +851,7 @@ mod tests { #[test] fn eval_state_require_attrs_names_unsorted_bad_type() { gc_registering_current_thread(|| { - let store = Store::open("auto", HashMap::new()).unwrap(); + let store = Store::open(None, HashMap::new()).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let v = es.eval_from_string("1", "").unwrap(); es.force(&v).unwrap(); @@ -868,7 +868,7 @@ mod tests { #[test] fn eval_state_value_attrs_names_example() { gc_registering_current_thread(|| { - let store = Store::open("auto", HashMap::new()).unwrap(); + let store = Store::open(None, HashMap::new()).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let expr = r#"{ a = throw "nope a"; b = throw "nope b"; }"#; let v = es.eval_from_string(expr, "").unwrap(); @@ -883,7 +883,7 @@ mod tests { #[test] fn eval_state_require_attrs_select() { gc_registering_current_thread(|| { - let store = Store::open("auto", HashMap::new()).unwrap(); + let store = Store::open(None, HashMap::new()).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let expr = r#"{ a = "aye"; b = "bee"; }"#; let v = es.eval_from_string(expr, "").unwrap(); @@ -910,7 +910,7 @@ mod tests { #[test] fn eval_state_require_attrs_select_forces_thunk() { gc_registering_current_thread(|| { - let store = Store::open("auto", HashMap::new()).unwrap(); + let store = Store::open(None, HashMap::new()).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let expr = r#"{ a = "aye"; b = "bee"; }"#; let v = make_thunk(&mut es, expr); @@ -924,7 +924,7 @@ mod tests { #[test] fn eval_state_require_attrs_select_error() { gc_registering_current_thread(|| { - let store = Store::open("auto", HashMap::new()).unwrap(); + let store = Store::open(None, HashMap::new()).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let expr = r#"{ a = throw "oh no the error"; }"#; let v = es.eval_from_string(expr, "").unwrap(); @@ -945,7 +945,7 @@ mod tests { #[test] fn eval_state_require_attrs_select_opt() { gc_registering_current_thread(|| { - let store = Store::open("auto", HashMap::new()).unwrap(); + let store = Store::open(None, HashMap::new()).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let expr = r#"{ a = "aye"; b = "bee"; }"#; let v = es.eval_from_string(expr, "").unwrap(); @@ -962,7 +962,7 @@ mod tests { #[test] fn eval_state_require_attrs_select_opt_forces_thunk() { gc_registering_current_thread(|| { - let store = Store::open("auto", HashMap::new()).unwrap(); + let store = Store::open(None, HashMap::new()).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let expr = r#"{ a = "aye"; b = "bee"; }"#; let v = make_thunk(&mut es, expr); @@ -976,7 +976,7 @@ mod tests { #[test] fn eval_state_require_attrs_select_opt_error() { gc_registering_current_thread(|| { - let store = Store::open("auto", HashMap::new()).unwrap(); + let store = Store::open(None, HashMap::new()).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let expr = r#"{ a = throw "oh no the error"; }"#; let v = es.eval_from_string(expr, "").unwrap(); @@ -997,7 +997,7 @@ mod tests { #[test] fn eval_state_value_string() { gc_registering_current_thread(|| { - let store = Store::open("auto", HashMap::new()).unwrap(); + let store = Store::open(None, HashMap::new()).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let v = es.eval_from_string("\"hello\"", "").unwrap(); es.force(&v).unwrap(); @@ -1012,7 +1012,7 @@ mod tests { #[test] fn eval_state_value_string_forces_thunk() { gc_registering_current_thread(|| { - let store = Store::open("auto", HashMap::new()).unwrap(); + let store = Store::open(None, HashMap::new()).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let v = make_thunk(&mut es, "\"hello\""); assert!(es.value_type_unforced(&v).is_none()); @@ -1025,7 +1025,7 @@ mod tests { #[test] fn eval_state_value_string_unexpected_bool() { gc_registering_current_thread(|| { - let store = Store::open("auto", HashMap::new()).unwrap(); + let store = Store::open(None, HashMap::new()).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let v = es.eval_from_string("true", "").unwrap(); es.force(&v).unwrap(); @@ -1043,7 +1043,7 @@ mod tests { #[test] fn eval_state_value_string_unexpected_path_value() { gc_registering_current_thread(|| { - let store = Store::open("auto", HashMap::new()).unwrap(); + let store = Store::open(None, HashMap::new()).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let v = es.eval_from_string("/foo", "").unwrap(); es.force(&v).unwrap(); @@ -1060,7 +1060,7 @@ mod tests { #[test] fn eval_state_value_string_bad_utf() { gc_registering_current_thread(|| { - let store = Store::open("auto", HashMap::new()).unwrap(); + let store = Store::open(None, HashMap::new()).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let v = es .eval_from_string("builtins.substring 0 1 \"ü\"", "") @@ -1081,7 +1081,7 @@ mod tests { #[test] fn eval_state_value_string_unexpected_context() { gc_registering_current_thread(|| { - let store = Store::open("auto", HashMap::new()).unwrap(); + let store = Store::open(None, HashMap::new()).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let v = es .eval_from_string("(derivation { name = \"hello\"; system = \"dummy\"; builder = \"cmd.exe\"; }).outPath", "") @@ -1100,7 +1100,7 @@ mod tests { #[test] fn eval_state_new_string() { gc_registering_current_thread(|| { - let store = Store::open("auto", HashMap::new()).unwrap(); + let store = Store::open(None, HashMap::new()).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let v = es.new_value_str("hello").unwrap(); es.force(&v).unwrap(); @@ -1115,7 +1115,7 @@ mod tests { #[test] fn eval_state_new_string_empty() { gc_registering_current_thread(|| { - let store = Store::open("auto", HashMap::new()).unwrap(); + let store = Store::open(None, HashMap::new()).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let v = es.new_value_str("").unwrap(); es.force(&v).unwrap(); @@ -1130,7 +1130,7 @@ mod tests { #[test] fn eval_state_new_string_invalid() { gc_registering_current_thread(|| { - let store = Store::open("auto", HashMap::new()).unwrap(); + let store = Store::open(None, HashMap::new()).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let r = es.new_value_str("hell\0no"); match r { @@ -1149,7 +1149,7 @@ mod tests { #[test] fn eval_state_new_int() { gc_registering_current_thread(|| { - let store = Store::open("auto", HashMap::new()).unwrap(); + let store = Store::open(None, HashMap::new()).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let v = es.new_value_int(42).unwrap(); es.force(&v).unwrap(); @@ -1164,7 +1164,7 @@ mod tests { #[test] fn eval_state_value_attrset() { gc_registering_current_thread(|| { - let store = Store::open("auto", HashMap::new()).unwrap(); + let store = Store::open(None, HashMap::new()).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let v = es.eval_from_string("{ }", "").unwrap(); es.force(&v).unwrap(); @@ -1177,7 +1177,7 @@ mod tests { #[test] fn eval_state_value_list() { gc_registering_current_thread(|| { - let store = Store::open("auto", HashMap::new()).unwrap(); + let store = Store::open(None, HashMap::new()).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let v = es .eval_from_string("[ ]", "") @@ -1192,7 +1192,7 @@ mod tests { #[test] fn eval_state_realise_string() { gc_registering_current_thread(|| { - let store = Store::open("auto", HashMap::new()).unwrap(); + let store = Store::open(None, HashMap::new()).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let expr = r#" '' @@ -1239,7 +1239,7 @@ mod tests { #[test] fn eval_state_call() { gc_registering_current_thread(|| { - let store = Store::open("auto", HashMap::new()).unwrap(); + let store = Store::open(None, HashMap::new()).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let f = es.eval_from_string("x: x + 1", "").unwrap(); let a = es.eval_from_string("2", "").unwrap(); @@ -1256,7 +1256,7 @@ mod tests { #[test] fn eval_state_call_multi() { gc_registering_current_thread(|| { - let store = Store::open("auto", HashMap::new()).unwrap(); + let store = Store::open(None, HashMap::new()).unwrap(); let mut es = EvalState::new(store, []).unwrap(); // This is a function that takes two arguments. let f = es.eval_from_string("x: y: x - y", "").unwrap(); @@ -1274,7 +1274,7 @@ mod tests { #[test] fn eval_state_apply() { gc_registering_current_thread(|| { - let store = Store::open("auto", HashMap::new()).unwrap(); + let store = Store::open(None, HashMap::new()).unwrap(); let mut es = EvalState::new(store, []).unwrap(); // This is a function that takes two arguments. let f = es.eval_from_string("x: x + 1", "").unwrap(); @@ -1293,7 +1293,7 @@ mod tests { #[test] fn eval_state_call_fail_body() { gc_registering_current_thread(|| { - let store = Store::open("auto", HashMap::new()).unwrap(); + let store = Store::open(None, HashMap::new()).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let f = es.eval_from_string("x: x + 1", "").unwrap(); let a = es.eval_from_string("true", "").unwrap(); @@ -1314,7 +1314,7 @@ mod tests { #[test] fn eval_state_call_multi_fail_body() { gc_registering_current_thread(|| { - let store = Store::open("auto", HashMap::new()).unwrap(); + let store = Store::open(None, HashMap::new()).unwrap(); let mut es = EvalState::new(store, []).unwrap(); // This is a function that takes two arguments. let f = es.eval_from_string("x: y: x - y", "").unwrap(); @@ -1337,7 +1337,7 @@ mod tests { #[test] fn eval_state_apply_fail_body() { gc_registering_current_thread(|| { - let store = Store::open("auto", HashMap::new()).unwrap(); + let store = Store::open(None, HashMap::new()).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let f = es.eval_from_string("x: x + 1", "").unwrap(); let a = es.eval_from_string("true", "").unwrap(); @@ -1362,7 +1362,7 @@ mod tests { #[test] fn eval_state_call_fail_args() { gc_registering_current_thread(|| { - let store = Store::open("auto", HashMap::new()).unwrap(); + let store = Store::open(None, HashMap::new()).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let f = es.eval_from_string("{x}: x + 1", "").unwrap(); let a = es.eval_from_string("{}", "").unwrap(); @@ -1383,7 +1383,7 @@ mod tests { #[test] fn eval_state_call_multi_fail_args() { gc_registering_current_thread(|| { - let store = Store::open("auto", HashMap::new()).unwrap(); + let store = Store::open(None, HashMap::new()).unwrap(); let mut es = EvalState::new(store, []).unwrap(); // This is a function that takes two arguments. let f = es.eval_from_string("{x}: {y}: x - y", "").unwrap(); @@ -1407,7 +1407,7 @@ mod tests { #[test] fn eval_state_apply_fail_args_lazy() { gc_registering_current_thread(|| { - let store = Store::open("auto", HashMap::new()).unwrap(); + let store = Store::open(None, HashMap::new()).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let f = es.eval_from_string("{x}: x + 1", "").unwrap(); let a = es.eval_from_string("{}", "").unwrap(); @@ -1440,7 +1440,7 @@ mod tests { let mut es = EvalState::new( Store::open( - "local", + Some("local"), HashMap::from([ ("store", store_path), ("state", state_path), @@ -1507,7 +1507,7 @@ mod tests { #[test] fn eval_state_primop_anon_call() { gc_registering_current_thread(|| { - let store = Store::open("auto", []).unwrap(); + let store = Store::open(None, []).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let bias: Arc> = Arc::new(Mutex::new(0)); let bias_control = bias.clone(); @@ -1549,7 +1549,7 @@ mod tests { #[test] fn eval_state_primop_anon_call_throw() { gc_registering_current_thread(|| { - let store = Store::open("auto", []).unwrap(); + let store = Store::open(None, []).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let f = { let es: &mut EvalState = &mut es; @@ -1588,7 +1588,7 @@ mod tests { #[test] fn eval_state_primop_anon_call_no_args() { gc_registering_current_thread(|| { - let store = Store::open("auto", []).unwrap(); + let store = Store::open(None, []).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let v = es .new_value_thunk( @@ -1609,7 +1609,7 @@ mod tests { #[test] fn eval_state_primop_anon_call_no_args_lazy() { gc_registering_current_thread(|| { - let store = Store::open("auto", []).unwrap(); + let store = Store::open(None, []).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let v = es .new_value_thunk( @@ -1642,7 +1642,7 @@ mod tests { #[test] pub fn eval_state_primop_custom() { gc_registering_current_thread(|| { - let store = Store::open("auto", []).unwrap(); + let store = Store::open(None, []).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let primop = primop::PrimOp::new( &mut es, @@ -1675,7 +1675,7 @@ mod tests { #[test] pub fn eval_state_primop_custom_throw() { gc_registering_current_thread(|| { - let store = Store::open("auto", []).unwrap(); + let store = Store::open(None, []).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let primop = primop::PrimOp::new( &mut es, @@ -1709,7 +1709,7 @@ mod tests { #[test] pub fn eval_state_new_value_attrs_from_slice_empty() { gc_registering_current_thread(|| { - let store = Store::open("auto", []).unwrap(); + let store = Store::open(None, []).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let attrs = es.new_value_attrs([]).unwrap(); let t = es.value_type(&attrs).unwrap(); @@ -1723,7 +1723,7 @@ mod tests { #[test] pub fn eval_state_new_value_attrs_from_vec() { gc_registering_current_thread(|| { - let store = Store::open("auto", []).unwrap(); + let store = Store::open(None, []).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let attrs = { let a = es.new_value_int(1).unwrap(); @@ -1750,7 +1750,7 @@ mod tests { #[test] pub fn eval_state_new_value_attrs_from_hashmap() { gc_registering_current_thread(|| { - let store = Store::open("auto", []).unwrap(); + let store = Store::open(None, []).unwrap(); let mut es = EvalState::new(store, []).unwrap(); let attrs = { let a = es.new_value_int(1).unwrap(); diff --git a/rust/nix-store/src/path.rs b/rust/nix-store/src/path.rs index e6b9ceb..29b6c83 100644 --- a/rust/nix-store/src/path.rs +++ b/rust/nix-store/src/path.rs @@ -77,7 +77,7 @@ mod tests { #[test] #[cfg(nix_at_least = "2.26" /* get_storedir */)] fn store_path_name() { - let mut store = crate::store::Store::open("dummy://", []).unwrap(); + let mut store = crate::store::Store::open(Some("dummy://"), []).unwrap(); let store_dir = store.get_storedir().unwrap(); let store_path_string = format!("{store_dir}/rdd4pnr4x9rqc9wgbibhngv217w2xvxl-bash-interactive-5.2p26"); diff --git a/rust/nix-store/src/store.rs b/rust/nix-store/src/store.rs index d1d5a72..8b74a85 100644 --- a/rust/nix-store/src/store.rs +++ b/rust/nix-store/src/store.rs @@ -59,7 +59,7 @@ impl StoreWeak { } /// Protects against https://github.com/NixOS/nix/issues/11979 (unless different parameters are passed, in which case it's up to luck, but you do get your own parameters as you asked for). -type StoreCacheMap = HashMap<(String, Vec<(String, String)>), StoreWeak>; +type StoreCacheMap = HashMap<(Option, Vec<(String, String)>), StoreWeak>; lazy_static! { static ref STORE_CACHE: Arc> = Arc::new(Mutex::new(HashMap::new())); @@ -71,8 +71,11 @@ pub struct Store { context: Context, } impl Store { + /// Open a store. + /// + /// See [nix_c_raw::store_open] for more information. pub fn open<'a, 'b>( - url: &str, + url: Option<&str>, params: impl IntoIterator, ) -> Result { let params = params @@ -83,7 +86,7 @@ impl Store { let mut store_cache = STORE_CACHE .lock() .map_err(|_| Error::msg("Failed to lock store cache. This should never happen."))?; - match store_cache.entry((url.to_string(), params)) { + match store_cache.entry((url.map(Into::into), params)) { std::collections::hash_map::Entry::Occupied(mut e) => { if let Some(store) = e.get().upgrade() { Ok(store) @@ -107,7 +110,7 @@ impl Store { } } fn open_uncached<'a, 'b>( - url: &str, + url: Option<&str>, params: impl IntoIterator, ) -> Result { let x = INIT.as_ref(); @@ -121,7 +124,14 @@ impl Store { let mut context: Context = Context::new(); - let uri_ptr = CString::new(url)?; + let uri_cstring = match url { + Some(url) => Some(CString::new(url)?), + None => None, + }; + let uri_ptr = uri_cstring + .as_ref() + .map(|s| s.as_ptr()) + .unwrap_or(null_mut()); // this intermediate value must be here and must not be moved // because it owns the data the `*const c_char` pointers point to. @@ -141,13 +151,8 @@ impl Store { .chain(std::iter::once(null_mut())) // signal the end of the array .collect(); - let store = unsafe { - check_call!(raw::store_open( - &mut context, - uri_ptr.as_ptr(), - params.as_mut_ptr() - )) - }?; + let store = + unsafe { check_call!(raw::store_open(&mut context, uri_ptr, params.as_mut_ptr())) }?; if store.is_null() { panic!("nix_c_store_open returned a null pointer without an error"); } @@ -232,21 +237,29 @@ mod tests { use super::*; + #[test] + fn none_works() { + let res = Store::open(None, HashMap::new()); + res.unwrap(); + } + #[test] fn auto_works() { - let res = Store::open("auto", HashMap::new()); + // This is not actually a given. + // Maybe whatever is in NIX_REMOTE or nix.conf is really important. + let res = Store::open(Some("auto"), HashMap::new()); res.unwrap(); } #[test] fn invalid_uri_fails() { - let res = Store::open("invalid://uri", HashMap::new()); + let res = Store::open(Some("invalid://uri"), HashMap::new()); assert!(res.is_err()); } #[test] fn get_uri() { - let mut store = Store::open("auto", HashMap::new()).unwrap(); + let mut store = Store::open(None, HashMap::new()).unwrap(); let uri = store.get_uri().unwrap(); assert!(!uri.is_empty()); // must be ascii @@ -258,7 +271,7 @@ mod tests { #[test] #[ignore] // Needs network access fn get_uri_nixos_cache() { - let mut store = Store::open("https://cache.nixos.org/", HashMap::new()).unwrap(); + let mut store = Store::open(Some("https://cache.nixos.org/"), HashMap::new()).unwrap(); let uri = store.get_uri().unwrap(); assert_eq!(uri, "https://cache.nixos.org"); } @@ -266,7 +279,7 @@ mod tests { #[test] #[cfg(nix_at_least = "2.26" /* get_storedir */)] fn parse_store_path_ok() { - let mut store = crate::store::Store::open("dummy://", []).unwrap(); + let mut store = crate::store::Store::open(Some("dummy://"), []).unwrap(); let store_dir = store.get_storedir().unwrap(); let store_path_string = format!("{store_dir}/rdd4pnr4x9rqc9wgbibhngv217w2xvxl-bash-interactive-5.2p26"); @@ -276,7 +289,7 @@ mod tests { #[test] fn parse_store_path_fail() { - let mut store = crate::store::Store::open("dummy://", []).unwrap(); + let mut store = crate::store::Store::open(Some("dummy://"), []).unwrap(); let store_path_string = format!("bash-interactive-5.2p26"); let r = store.parse_store_path(store_path_string.as_str()); match r { @@ -289,7 +302,7 @@ mod tests { #[test] fn weak_ref() { - let mut store = Store::open("auto", HashMap::new()).unwrap(); + let mut store = Store::open(None, HashMap::new()).unwrap(); let uri = store.get_uri().unwrap(); let weak = store.weak_ref(); let mut store2 = weak.upgrade().unwrap(); @@ -300,7 +313,7 @@ mod tests { let weak = { // Concurrent tests calling Store::open will keep the weak reference to auto alive, // so for this test we need to bypass the global cache. - let store = Store::open_uncached("auto", HashMap::new()).unwrap(); + let store = Store::open_uncached(None, HashMap::new()).unwrap(); store.weak_ref() }; assert!(weak.upgrade().is_none()); From 71496990205bc4346cf835c1a3f620be1cb70f34 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 16 Dec 2024 12:51:43 +0100 Subject: [PATCH 137/306] doc: Add aliases (cherry picked from commit e52e118180d23439d0a7286bb41b8c9202066ad9) --- rust/nix-store/src/store.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rust/nix-store/src/store.rs b/rust/nix-store/src/store.rs index 8b74a85..9ac67ed 100644 --- a/rust/nix-store/src/store.rs +++ b/rust/nix-store/src/store.rs @@ -74,6 +74,7 @@ impl Store { /// Open a store. /// /// See [nix_c_raw::store_open] for more information. + #[doc(alias = "nix_store_open")] pub fn open<'a, 'b>( url: Option<&str>, params: impl IntoIterator, @@ -172,6 +173,7 @@ impl Store { self.inner.ptr() } + #[doc(alias = "nix_store_get_uri")] pub fn get_uri(&mut self) -> Result { let mut r = result_string_init!(); unsafe { From 145a594421f0d42491ac415944259dec31649ab8 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 20 Jan 2025 12:50:40 +0100 Subject: [PATCH 138/306] maint: Fix apparently ignored '' in BINDGEN_EXTRA_CLANG_ARGS (cherry picked from commit a6775b2b6209623f1e8f1cc837462a0804e0ab6e) --- rust/bindgen-gcc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/bindgen-gcc.sh b/rust/bindgen-gcc.sh index d904b61..923a278 100644 --- a/rust/bindgen-gcc.sh +++ b/rust/bindgen-gcc.sh @@ -4,7 +4,7 @@ # This script tells it where to find them. echo "Extending BINDGEN_EXTRA_CLANG_ARGS with system include paths..." 2>&1 -BINDGEN_EXTRA_CLANG_ARGS="''${BINDGEN_EXTRA_CLANG_ARGS:-}" +BINDGEN_EXTRA_CLANG_ARGS="${BINDGEN_EXTRA_CLANG_ARGS:-}" export BINDGEN_EXTRA_CLANG_ARGS include_paths=$( echo | $NIX_CC_UNWRAPPED -v -E -x c - 2>&1 \ From 8bf888020d38c8fc295a7b9a14861a195fafcc4d Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 22 Jan 2025 10:50:28 +0100 Subject: [PATCH 139/306] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/8f3e1f807051e32d8c95cd12b9b421623850a34d?narHash=sha256-/qlNWm/IEVVH7GfgAIyP6EsVZI6zjAx1cV5zNyrs%2BrI%3D' (2025-01-04) → 'github:NixOS/nixpkgs/9e4d5190a9482a1fb9d18adf0bdb83c6e506eaab?narHash=sha256-nmKOgAU48S41dTPIXAq0AHZSehWUn6ZPrUKijHAMmIk%3D' (2025-01-21) (cherry picked from commit 88539274107c9baa5c66f723c87c526117795f56) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 6aff387..194361c 100644 --- a/flake.lock +++ b/flake.lock @@ -212,11 +212,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1736012469, - "narHash": "sha256-/qlNWm/IEVVH7GfgAIyP6EsVZI6zjAx1cV5zNyrs+rI=", + "lastModified": 1737469691, + "narHash": "sha256-nmKOgAU48S41dTPIXAq0AHZSehWUn6ZPrUKijHAMmIk=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "8f3e1f807051e32d8c95cd12b9b421623850a34d", + "rev": "9e4d5190a9482a1fb9d18adf0bdb83c6e506eaab", "type": "github" }, "original": { From 895336ac7939a9d932abf36ece0dd2aec3f1e31e Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 29 Jan 2025 05:06:41 +0100 Subject: [PATCH 140/306] License as LGPL-2.1 (cherry picked from commit 1127742e387ef78b91ec1e4cec0bb17b5e85666c) --- LICENSE | 501 ++++++++++++++++++++++++++++++++++++++ rust/nix-c-raw/Cargo.toml | 1 + rust/nix-expr/Cargo.toml | 1 + rust/nix-flake/Cargo.toml | 1 + rust/nix-store/Cargo.toml | 1 + rust/nix-util/Cargo.toml | 1 + 6 files changed, 506 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f6683e7 --- /dev/null +++ b/LICENSE @@ -0,0 +1,501 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see . + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Moe Ghoul, President of Vice + +That's all there is to it! diff --git a/rust/nix-c-raw/Cargo.toml b/rust/nix-c-raw/Cargo.toml index c1bbd01..4bb530a 100644 --- a/rust/nix-c-raw/Cargo.toml +++ b/rust/nix-c-raw/Cargo.toml @@ -3,6 +3,7 @@ name = "nix-c-raw" version = "0.1.0" edition = "2021" build = "build.rs" +license = "LGPL-2.1" [lib] path = "src/lib.rs" diff --git a/rust/nix-expr/Cargo.toml b/rust/nix-expr/Cargo.toml index 1c68648..08c5e37 100644 --- a/rust/nix-expr/Cargo.toml +++ b/rust/nix-expr/Cargo.toml @@ -2,6 +2,7 @@ name = "nix-expr" version = "0.1.0" edition = "2021" +license = "LGPL-2.1" [lib] path = "src/lib.rs" diff --git a/rust/nix-flake/Cargo.toml b/rust/nix-flake/Cargo.toml index c2328ce..5b7f2d4 100644 --- a/rust/nix-flake/Cargo.toml +++ b/rust/nix-flake/Cargo.toml @@ -2,6 +2,7 @@ name = "nix-flake" version = "0.1.0" edition = "2021" +license = "LGPL-2.1" [lib] path = "src/lib.rs" diff --git a/rust/nix-store/Cargo.toml b/rust/nix-store/Cargo.toml index 6c42ac9..2d3764e 100644 --- a/rust/nix-store/Cargo.toml +++ b/rust/nix-store/Cargo.toml @@ -3,6 +3,7 @@ name = "nix-store" version = "0.1.0" edition = "2021" build = "build.rs" +license = "LGPL-2.1" [lib] path = "src/lib.rs" diff --git a/rust/nix-util/Cargo.toml b/rust/nix-util/Cargo.toml index a58cb43..6293092 100644 --- a/rust/nix-util/Cargo.toml +++ b/rust/nix-util/Cargo.toml @@ -2,6 +2,7 @@ name = "nix-util" version = "0.1.0" edition = "2021" +license = "LGPL-2.1" [lib] path = "src/lib.rs" From 2508facd82e1ed2856632c704fb0f9d33b010842 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 31 Jan 2025 07:38:11 +0100 Subject: [PATCH 141/306] feat: Improve EvalState::require_attrs_select error message (cherry picked from commit 220b6f4123966d818064f4114477ce8676214b90) --- rust/nix-expr/src/eval_state.rs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 99e60cf..64111b5 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -268,8 +268,21 @@ impl EvalState { v.raw_ptr(), self.eval_state.as_ptr(), attr_name.as_ptr() - ))?; - Ok(Value::new(v2)) + )); + match v2 { + Ok(v2) => Ok(Value::new(v2)), + Err(e) => { + // As of Nix 2.26, the error message is not helpful when it + // is simply missing, so we provide a better one. (Note that + // missing attributes requested by Nix expressions OTOH is a + // different error message which works fine.) + if e.to_string() == "missing attribute" { + bail!("attribute `{}` not found", attr_name.to_string_lossy()); + } else { + Err(e) + } + } + } } } @@ -896,8 +909,7 @@ mod tests { Ok(_) => panic!("expected an error"), Err(e) => { let s = format!("{e:#}"); - // TODO: bad error message from Nix - if !s.contains("missing attribute") { + if !s.contains("attribute `c` not found") { eprintln!("unexpected error message: {}", s); assert!(false); } From 92fcb451544ce830d08ab89eb4688a112ac7d3f2 Mon Sep 17 00:00:00 2001 From: Hercules CI Effects Date: Sun, 2 Feb 2025 00:58:02 +0000 Subject: [PATCH 142/306] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'flake-parts': 'github:hercules-ci/flake-parts/b905f6fc23a9051a6e1b741e1438dbfc0634c6de?narHash=sha256-%2Bhu54pAoLDEZT9pjHlqL9DNzWz0NbUn8NEAHP7PQPzU%3D' (2025-01-06) → 'github:hercules-ci/flake-parts/32ea77a06711b758da0ad9bd6a844c5740a87abd?narHash=sha256-7H9XgNiGLKN1G1CgRh0vUL4AheZSYzPm%2BzmZ7vxbJdo%3D' (2025-02-01) • Updated input 'flake-parts/nixpkgs-lib': 'https://github.com/NixOS/nixpkgs/archive/e9b51731911566bbf7e4895475a87fe06961de0b.tar.gz?narHash=sha256-CewEm1o2eVAnoqb6Ml%2BQi9Gg/EfNAxbRx1lANGVyoLI%3D' (2025-01-01) → 'https://github.com/NixOS/nixpkgs/archive/072a6db25e947df2f31aab9eccd0ab75d5b2da11.tar.gz?narHash=sha256-vJzFZGaCpnmo7I6i416HaBLpC%2BhvcURh/BQwROcGIp8%3D' (2025-02-01) • Updated input 'nix': 'github:NixOS/nix/5230d3ecc4cd3a3d965902a56b5a21bcc99821c3?narHash=sha256-u6OD0BH%2BUxyfrWMMpBfM5cz/TDWU9lxJOujgzqBnN9A%3D' (2025-01-08) → 'github:NixOS/nix/d949c8de7c7e84bb7537dc772609686c37033a3b?narHash=sha256-3%2BqJBts5RTAtjCExo6bkqrttL%2BskpYZzPOVEzPSwVtc%3D' (2025-02-01) • Added input 'nix/nixfmt': 'github:NixOS/nixfmt/8d4bd690c247004d90d8554f0b746b1231fe2436?narHash=sha256-hrKhUp2V2fk/dvzTTHFqvtOg000G1e%2BjyIam%2BD4XqhA%3D' (2025-01-07) • Added input 'nix/nixfmt/flake-utils': 'github:numtide/flake-utils/b1d9ab70662946ef0850d488da1c9019f3a9752a?narHash=sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ%3D' (2024-03-11) • Added input 'nix/nixfmt/flake-utils/systems': 'github:nix-systems/default/da67096a3b9bf56a91d16901293e51ba5b49a27e?narHash=sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768%3D' (2023-04-09) • Updated input 'nix-cargo-integration': 'github:yusdacra/nix-cargo-integration/1ce1f666c955e73f65de74f3a8c3ca2c3e5d741b?narHash=sha256-nOWLP6pSblYrCipiBb7/SQpGhNe7AHT8m9f%2B%2Bb8/Ni4%3D' (2025-01-08) → 'github:yusdacra/nix-cargo-integration/718d577808e3e31f445b6564d58b755294e72f42?narHash=sha256-o8kg4q1V2xV9ZlszAnizXJK2c%2BfbhAeLbGoTUa2u1Bw%3D' (2025-02-01) • Updated input 'nix-cargo-integration/rust-overlay': 'github:oxalica/rust-overlay/a0b81d4fa349d9af1765b0f0b4a899c13776f706?narHash=sha256-IKrk7RL%2BQ/2NC6%2BQl6dwwCNZI6T6JH2grTdJaVWHF0A%3D' (2025-01-08) → 'github:oxalica/rust-overlay/83284068670d5ae4a43641c4afb150f3446be70d?narHash=sha256-S6ErHxkSm0iA7ZMsjjDaASWxbELYcdfv8BhOkkj1rHw%3D' (2025-02-01) • Updated input 'nix-cargo-integration/treefmt': 'github:numtide/treefmt-nix/13c913f5deb3a5c08bb810efd89dc8cb24dd968b?narHash=sha256-p2r8xhQZ3TYIEKBoiEhllKWQqWNJNoT9v64Vmg4q8Zw%3D' (2025-01-06) → 'github:numtide/treefmt-nix/bebf27d00f7d10ba75332a0541ac43676985dea3?narHash=sha256-j6jC12vCFsTGDmY2u1H12lMr62fnclNjuCtAdF1a4Nk%3D' (2025-01-28) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/9e4d5190a9482a1fb9d18adf0bdb83c6e506eaab?narHash=sha256-nmKOgAU48S41dTPIXAq0AHZSehWUn6ZPrUKijHAMmIk%3D' (2025-01-21) → 'github:NixOS/nixpkgs/3a228057f5b619feb3186e986dbe76278d707b6e?narHash=sha256-xvTo0Aw0%2Bveek7hvEVLzErmJyQkEcRk6PSR4zsRQFEc%3D' (2025-02-01) (cherry picked from commit 26a425f54aeac8e1f29bd13f2fc36a5238105f73) --- flake.lock | 96 +++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 74 insertions(+), 22 deletions(-) diff --git a/flake.lock b/flake.lock index 194361c..03d032a 100644 --- a/flake.lock +++ b/flake.lock @@ -77,11 +77,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1736143030, - "narHash": "sha256-+hu54pAoLDEZT9pjHlqL9DNzWz0NbUn8NEAHP7PQPzU=", + "lastModified": 1738453229, + "narHash": "sha256-7H9XgNiGLKN1G1CgRh0vUL4AheZSYzPm+zmZ7vxbJdo=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "b905f6fc23a9051a6e1b741e1438dbfc0634c6de", + "rev": "32ea77a06711b758da0ad9bd6a844c5740a87abd", "type": "github" }, "original": { @@ -111,6 +111,24 @@ "type": "github" } }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, "git-hooks-nix": { "inputs": { "flake-compat": [ @@ -163,6 +181,7 @@ "flake-compat": "flake-compat", "flake-parts": "flake-parts_2", "git-hooks-nix": "git-hooks-nix", + "nixfmt": "nixfmt", "nixpkgs": [ "nixpkgs" ], @@ -170,11 +189,11 @@ "nixpkgs-regression": "nixpkgs-regression" }, "locked": { - "lastModified": 1736342444, - "narHash": "sha256-u6OD0BH+UxyfrWMMpBfM5cz/TDWU9lxJOujgzqBnN9A=", + "lastModified": 1738382221, + "narHash": "sha256-3+qJBts5RTAtjCExo6bkqrttL+skpYZzPOVEzPSwVtc=", "owner": "NixOS", "repo": "nix", - "rev": "5230d3ecc4cd3a3d965902a56b5a21bcc99821c3", + "rev": "d949c8de7c7e84bb7537dc772609686c37033a3b", "type": "github" }, "original": { @@ -197,11 +216,11 @@ "treefmt": "treefmt" }, "locked": { - "lastModified": 1736316962, - "narHash": "sha256-nOWLP6pSblYrCipiBb7/SQpGhNe7AHT8m9f++b8/Ni4=", + "lastModified": 1738390452, + "narHash": "sha256-o8kg4q1V2xV9ZlszAnizXJK2c+fbhAeLbGoTUa2u1Bw=", "owner": "yusdacra", "repo": "nix-cargo-integration", - "rev": "1ce1f666c955e73f65de74f3a8c3ca2c3e5d741b", + "rev": "718d577808e3e31f445b6564d58b755294e72f42", "type": "github" }, "original": { @@ -210,13 +229,31 @@ "type": "github" } }, + "nixfmt": { + "inputs": { + "flake-utils": "flake-utils" + }, + "locked": { + "lastModified": 1736283758, + "narHash": "sha256-hrKhUp2V2fk/dvzTTHFqvtOg000G1e+jyIam+D4XqhA=", + "owner": "NixOS", + "repo": "nixfmt", + "rev": "8d4bd690c247004d90d8554f0b746b1231fe2436", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixfmt", + "type": "github" + } + }, "nixpkgs": { "locked": { - "lastModified": 1737469691, - "narHash": "sha256-nmKOgAU48S41dTPIXAq0AHZSehWUn6ZPrUKijHAMmIk=", + "lastModified": 1738410390, + "narHash": "sha256-xvTo0Aw0+veek7hvEVLzErmJyQkEcRk6PSR4zsRQFEc=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "9e4d5190a9482a1fb9d18adf0bdb83c6e506eaab", + "rev": "3a228057f5b619feb3186e986dbe76278d707b6e", "type": "github" }, "original": { @@ -244,14 +281,14 @@ }, "nixpkgs-lib": { "locked": { - "lastModified": 1735774519, - "narHash": "sha256-CewEm1o2eVAnoqb6Ml+Qi9Gg/EfNAxbRx1lANGVyoLI=", + "lastModified": 1738452942, + "narHash": "sha256-vJzFZGaCpnmo7I6i416HaBLpC+hvcURh/BQwROcGIp8=", "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/e9b51731911566bbf7e4895475a87fe06961de0b.tar.gz" + "url": "https://github.com/NixOS/nixpkgs/archive/072a6db25e947df2f31aab9eccd0ab75d5b2da11.tar.gz" }, "original": { "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/e9b51731911566bbf7e4895475a87fe06961de0b.tar.gz" + "url": "https://github.com/NixOS/nixpkgs/archive/072a6db25e947df2f31aab9eccd0ab75d5b2da11.tar.gz" } }, "nixpkgs-regression": { @@ -348,11 +385,11 @@ ] }, "locked": { - "lastModified": 1736303309, - "narHash": "sha256-IKrk7RL+Q/2NC6+Ql6dwwCNZI6T6JH2grTdJaVWHF0A=", + "lastModified": 1738376888, + "narHash": "sha256-S6ErHxkSm0iA7ZMsjjDaASWxbELYcdfv8BhOkkj1rHw=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "a0b81d4fa349d9af1765b0f0b4a899c13776f706", + "rev": "83284068670d5ae4a43641c4afb150f3446be70d", "type": "github" }, "original": { @@ -384,6 +421,21 @@ "type": "github" } }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, "treefmt": { "inputs": { "nixpkgs": [ @@ -392,11 +444,11 @@ ] }, "locked": { - "lastModified": 1736154270, - "narHash": "sha256-p2r8xhQZ3TYIEKBoiEhllKWQqWNJNoT9v64Vmg4q8Zw=", + "lastModified": 1738070913, + "narHash": "sha256-j6jC12vCFsTGDmY2u1H12lMr62fnclNjuCtAdF1a4Nk=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "13c913f5deb3a5c08bb810efd89dc8cb24dd968b", + "rev": "bebf27d00f7d10ba75332a0541ac43676985dea3", "type": "github" }, "original": { From debff217c62f75876abd3dd4daa652aafabe097b Mon Sep 17 00:00:00 2001 From: Hercules CI Effects Date: Sun, 2 Feb 2025 00:58:04 +0000 Subject: [PATCH 143/306] dev/flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'hercules-ci-effects': 'github:hercules-ci/hercules-ci-effects/f6233b5cfbada692d93a73d6ed35bdbfd0fdb9c4?narHash=sha256-cwk53OX1S1bCFY09zydubZNmmwcx9l5XEba8mVYuNE4%3D' (2025-01-01) → 'github:hercules-ci/hercules-ci-effects/6d1b6d5d59758b4f5f05745f774fc13cdc59da43?narHash=sha256-oJN/yvRL7G0WlR/hTkQIjFbPkzCV%2BsFnNB/38Tb9RL4%3D' (2025-01-30) • Updated input 'hercules-ci-effects/flake-parts': 'github:hercules-ci/flake-parts/205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9?narHash=sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c%3D' (2024-12-04) → 'github:hercules-ci/flake-parts/b905f6fc23a9051a6e1b741e1438dbfc0634c6de?narHash=sha256-%2Bhu54pAoLDEZT9pjHlqL9DNzWz0NbUn8NEAHP7PQPzU%3D' (2025-01-06) • Updated input 'hercules-ci-effects/nixpkgs': 'github:NixOS/nixpkgs/55d15ad12a74eb7d4646254e13638ad0c4128776?narHash=sha256-M1%2BuCoV5igihRfcUKrr1riygbe73/dzNnzPsmaLCmpo%3D' (2024-12-03) → 'github:NixOS/nixpkgs/9abb87b552b7f55ac8916b6fc9e5cb486656a2f3?narHash=sha256-qwpCtZhSsSNQtK4xYGzMiyEDhkNzOCz/Vfu4oL2ETsQ%3D' (2025-01-13) • Updated input 'nix-unit': 'github:nix-community/nix-unit/6d373fdb344760d9c2e45994420b165b0202556b?narHash=sha256-Zu31IAdl2Hp4bvsuyGkeuBp0igyvVHrmwN6bMfYTsIo%3D' (2024-12-04) → 'github:nix-community/nix-unit/d867d72d21da3b7d83f0feef73b0ac7f72b16437?narHash=sha256-stdx1z86yj66bChYswqjRw8BpdJRwxkH2XIJrsygpiM%3D' (2024-12-09) • Updated input 'nix-unit/flake-parts': 'github:hercules-ci/flake-parts/506278e768c2a08bec68eb62932193e341f55c90?narHash=sha256-hgmguH29K2fvs9szpq2r3pz2/8cJd2LPS%2Bb4tfNFCwE%3D' (2024-11-01) → 'github:hercules-ci/flake-parts/205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9?narHash=sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c%3D' (2024-12-04) • Updated input 'nix-unit/nix-github-actions': 'github:nix-community/nix-github-actions/e04df33f62cdcf93d73e9a04142464753a16db67?narHash=sha256-B4mzTcQ0FZHdpeWcpDYPERtyjJd/NIuaQ9%2BBV1h%2BMpA%3D' (2024-10-24) → 'github:nix-community/nix-github-actions/7b5f051df789b6b20d259924d349a9ba3319b226?narHash=sha256-p4gB3Rhw8R6Ak4eMl8pqjCPOLCZRqaehZxdZ/mbFClM%3D' (2024-11-18) • Updated input 'nix-unit/nixpkgs': 'github:NixOS/nixpkgs/85f7e662eda4fa3a995556527c87b2524b691933?narHash=sha256-JwQZIGSYnRNOgDDoIgqKITrPVil%2BRMWHsZH1eE1VGN0%3D' (2024-11-07) → 'github:NixOS/nixpkgs/929116e316068c7318c54eb4d827f7d9756d5e9c?narHash=sha256-aLJxoTDDSqB%2B/3orsulE6/qdlX6MzDLIITLZqdgMpqo%3D' (2024-12-05) • Updated input 'nix-unit/treefmt-nix': 'github:numtide/treefmt-nix/746901bb8dba96d154b66492a29f5db0693dbfcc?narHash=sha256-vK%2Ba09qq19QNu2MlLcvN4qcRctJbqWkX7ahgPZ/%2BmaI%3D' (2024-10-30) → 'github:numtide/treefmt-nix/357cda84af1d74626afb7fb3bc12d6957167cda9?narHash=sha256-9qOp6jNdezzLMxwwXaXZWPXosHbNqno%2Bf7Ii/xftqZ8%3D' (2024-12-08) • Updated input 'pre-commit-hooks-nix': 'github:cachix/pre-commit-hooks.nix/f0f0dc4920a903c3e08f5bdb9246bb572fcae498?narHash=sha256-ulZN7ps8nBV31SE%2BdwkDvKIzvN6hroRY8sYOT0w%2BE28%3D' (2024-12-21) → 'github:cachix/pre-commit-hooks.nix/9364dc02281ce2d37a1f55b6e51f7c0f65a75f17?narHash=sha256-R10v2hoJRLq8jcL4syVFag7nIGE7m13qO48wRIukWNg%3D' (2025-01-21) • Removed input 'pre-commit-hooks-nix/nixpkgs-stable' (cherry picked from commit a60100276bbf87098487b08389c8260308bb9ad8) --- dev/flake.lock | 119 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 101 insertions(+), 18 deletions(-) diff --git a/dev/flake.lock b/dev/flake.lock index 39cb762..cee8e88 100644 --- a/dev/flake.lock +++ b/dev/flake.lock @@ -23,6 +23,26 @@ "nixpkgs" ] }, + "locked": { + "lastModified": 1736143030, + "narHash": "sha256-+hu54pAoLDEZT9pjHlqL9DNzWz0NbUn8NEAHP7PQPzU=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "b905f6fc23a9051a6e1b741e1438dbfc0634c6de", + "type": "github" + }, + "original": { + "id": "flake-parts", + "type": "indirect" + } + }, + "flake-parts_2": { + "inputs": { + "nixpkgs-lib": [ + "nix-unit", + "nixpkgs" + ] + }, "locked": { "lastModified": 1733312601, "narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=", @@ -32,8 +52,9 @@ "type": "github" }, "original": { - "id": "flake-parts", - "type": "indirect" + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" } }, "gitignore": { @@ -63,11 +84,11 @@ "nixpkgs": "nixpkgs" }, "locked": { - "lastModified": 1735695978, - "narHash": "sha256-cwk53OX1S1bCFY09zydubZNmmwcx9l5XEba8mVYuNE4=", + "lastModified": 1738237977, + "narHash": "sha256-oJN/yvRL7G0WlR/hTkQIjFbPkzCV+sFnNB/38Tb9RL4=", "owner": "hercules-ci", "repo": "hercules-ci-effects", - "rev": "f6233b5cfbada692d93a73d6ed35bdbfd0fdb9c4", + "rev": "6d1b6d5d59758b4f5f05745f774fc13cdc59da43", "type": "github" }, "original": { @@ -76,13 +97,55 @@ "type": "github" } }, + "nix-github-actions": { + "inputs": { + "nixpkgs": [ + "nix-unit", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1731952509, + "narHash": "sha256-p4gB3Rhw8R6Ak4eMl8pqjCPOLCZRqaehZxdZ/mbFClM=", + "owner": "nix-community", + "repo": "nix-github-actions", + "rev": "7b5f051df789b6b20d259924d349a9ba3319b226", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nix-github-actions", + "type": "github" + } + }, + "nix-unit": { + "inputs": { + "flake-parts": "flake-parts_2", + "nix-github-actions": "nix-github-actions", + "nixpkgs": "nixpkgs_2", + "treefmt-nix": "treefmt-nix" + }, + "locked": { + "lastModified": 1733705700, + "narHash": "sha256-stdx1z86yj66bChYswqjRw8BpdJRwxkH2XIJrsygpiM=", + "owner": "nix-community", + "repo": "nix-unit", + "rev": "d867d72d21da3b7d83f0feef73b0ac7f72b16437", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nix-unit", + "type": "github" + } + }, "nixpkgs": { "locked": { - "lastModified": 1733212471, - "narHash": "sha256-M1+uCoV5igihRfcUKrr1riygbe73/dzNnzPsmaLCmpo=", + "lastModified": 1736798957, + "narHash": "sha256-qwpCtZhSsSNQtK4xYGzMiyEDhkNzOCz/Vfu4oL2ETsQ=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "55d15ad12a74eb7d4646254e13638ad0c4128776", + "rev": "9abb87b552b7f55ac8916b6fc9e5cb486656a2f3", "type": "github" }, "original": { @@ -92,18 +155,18 @@ "type": "github" } }, - "nixpkgs-stable": { + "nixpkgs_2": { "locked": { - "lastModified": 1730741070, - "narHash": "sha256-edm8WG19kWozJ/GqyYx2VjW99EdhjKwbY3ZwdlPAAlo=", + "lastModified": 1733376361, + "narHash": "sha256-aLJxoTDDSqB+/3orsulE6/qdlX6MzDLIITLZqdgMpqo=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "d063c1dd113c91ab27959ba540c0d9753409edf3", + "rev": "929116e316068c7318c54eb4d827f7d9756d5e9c", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-24.05", + "ref": "nixpkgs-unstable", "repo": "nixpkgs", "type": "github" } @@ -112,15 +175,14 @@ "inputs": { "flake-compat": "flake-compat", "gitignore": "gitignore", - "nixpkgs": [], - "nixpkgs-stable": "nixpkgs-stable" + "nixpkgs": [] }, "locked": { - "lastModified": 1734797603, - "narHash": "sha256-ulZN7ps8nBV31SE+dwkDvKIzvN6hroRY8sYOT0w+E28=", + "lastModified": 1737465171, + "narHash": "sha256-R10v2hoJRLq8jcL4syVFag7nIGE7m13qO48wRIukWNg=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "f0f0dc4920a903c3e08f5bdb9246bb572fcae498", + "rev": "9364dc02281ce2d37a1f55b6e51f7c0f65a75f17", "type": "github" }, "original": { @@ -134,6 +196,27 @@ "hercules-ci-effects": "hercules-ci-effects", "pre-commit-hooks-nix": "pre-commit-hooks-nix" } + }, + "treefmt-nix": { + "inputs": { + "nixpkgs": [ + "nix-unit", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1733662930, + "narHash": "sha256-9qOp6jNdezzLMxwwXaXZWPXosHbNqno+f7Ii/xftqZ8=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "357cda84af1d74626afb7fb3bc12d6957167cda9", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" + } } }, "root": "root", From 27b4fcb1670c84a9e71f61d2ceb85ac2ccdabe65 Mon Sep 17 00:00:00 2001 From: Hercules CI Effects Date: Sun, 2 Mar 2025 00:58:05 +0000 Subject: [PATCH 144/306] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'flake-parts': 'github:hercules-ci/flake-parts/32ea77a06711b758da0ad9bd6a844c5740a87abd?narHash=sha256-7H9XgNiGLKN1G1CgRh0vUL4AheZSYzPm%2BzmZ7vxbJdo%3D' (2025-02-01) → 'github:hercules-ci/flake-parts/3876f6b87db82f33775b1ef5ea343986105db764?narHash=sha256-ZaMw0pdoUKigLpv9HiNDH2Pjnosg7NBYMJlHTIsHEUo%3D' (2025-03-01) • Updated input 'flake-parts/nixpkgs-lib': 'https://github.com/NixOS/nixpkgs/archive/072a6db25e947df2f31aab9eccd0ab75d5b2da11.tar.gz?narHash=sha256-vJzFZGaCpnmo7I6i416HaBLpC%2BhvcURh/BQwROcGIp8%3D' (2025-02-01) → 'https://github.com/NixOS/nixpkgs/archive/6d3702243441165a03f699f64416f635220f4f15.tar.gz?narHash=sha256-3wHafybyRfpUCLoE8M%2BuPVZinImg3xX%2BNm6gEfN3G8I%3D' (2025-03-01) • Updated input 'nix': 'github:NixOS/nix/d949c8de7c7e84bb7537dc772609686c37033a3b?narHash=sha256-3%2BqJBts5RTAtjCExo6bkqrttL%2BskpYZzPOVEzPSwVtc%3D' (2025-02-01) → 'github:NixOS/nix/77f22db567c3377989b8127fc0b28c948f0559a1?narHash=sha256-I/68odvSD6t2DK6j003f57H5WlKtKkVQfy0P%2BVCKb/w%3D' (2025-02-28) • Removed input 'nix/nixfmt' • Removed input 'nix/nixfmt/flake-utils' • Removed input 'nix/nixfmt/flake-utils/systems' • Updated input 'nix-cargo-integration': 'github:yusdacra/nix-cargo-integration/718d577808e3e31f445b6564d58b755294e72f42?narHash=sha256-o8kg4q1V2xV9ZlszAnizXJK2c%2BfbhAeLbGoTUa2u1Bw%3D' (2025-02-01) → 'github:yusdacra/nix-cargo-integration/d60df90181bf3b9dbbb8d4440f827afb7ad9fe22?narHash=sha256-0jq3H5nsU6leI%2B2pBEtqxqZWopaSrfhkw%2Bq%2BGrtMGtA%3D' (2025-03-01) • Updated input 'nix-cargo-integration/parts': 'github:hercules-ci/flake-parts/b905f6fc23a9051a6e1b741e1438dbfc0634c6de?narHash=sha256-%2Bhu54pAoLDEZT9pjHlqL9DNzWz0NbUn8NEAHP7PQPzU%3D' (2025-01-06) → 'github:hercules-ci/flake-parts/32ea77a06711b758da0ad9bd6a844c5740a87abd?narHash=sha256-7H9XgNiGLKN1G1CgRh0vUL4AheZSYzPm%2BzmZ7vxbJdo%3D' (2025-02-01) • Updated input 'nix-cargo-integration/rust-overlay': 'github:oxalica/rust-overlay/83284068670d5ae4a43641c4afb150f3446be70d?narHash=sha256-S6ErHxkSm0iA7ZMsjjDaASWxbELYcdfv8BhOkkj1rHw%3D' (2025-02-01) → 'github:oxalica/rust-overlay/bbac9527bc6b28b6330b13043d0e76eac11720dc?narHash=sha256-FuoXrXZPoJEZQ3PF7t85tEpfBVID9JQIOnVKMNfTAb0%3D' (2025-03-01) • Updated input 'nix-cargo-integration/treefmt': 'github:numtide/treefmt-nix/bebf27d00f7d10ba75332a0541ac43676985dea3?narHash=sha256-j6jC12vCFsTGDmY2u1H12lMr62fnclNjuCtAdF1a4Nk%3D' (2025-01-28) → 'github:numtide/treefmt-nix/3d0579f5cc93436052d94b73925b48973a104204?narHash=sha256-mL1szCeIsjh6Khn3nH2cYtwO5YXG6gBiTw1A30iGeDU%3D' (2025-02-17) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/3a228057f5b619feb3186e986dbe76278d707b6e?narHash=sha256-xvTo0Aw0%2Bveek7hvEVLzErmJyQkEcRk6PSR4zsRQFEc%3D' (2025-02-01) → 'github:NixOS/nixpkgs/6313551cd05425cd5b3e63fe47dbc324eabb15e4?narHash=sha256-D%2BR%2BkFxy1KsheiIzkkx/6L63wEHBYX21OIwlFV8JvDs%3D' (2025-02-27) (cherry picked from commit e01d1ac193a55ee5229f7e09cc5f9eba4c6a0a1c) --- flake.lock | 102 +++++++++++++---------------------------------------- 1 file changed, 25 insertions(+), 77 deletions(-) diff --git a/flake.lock b/flake.lock index 03d032a..798519f 100644 --- a/flake.lock +++ b/flake.lock @@ -77,11 +77,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1738453229, - "narHash": "sha256-7H9XgNiGLKN1G1CgRh0vUL4AheZSYzPm+zmZ7vxbJdo=", + "lastModified": 1740872218, + "narHash": "sha256-ZaMw0pdoUKigLpv9HiNDH2Pjnosg7NBYMJlHTIsHEUo=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "32ea77a06711b758da0ad9bd6a844c5740a87abd", + "rev": "3876f6b87db82f33775b1ef5ea343986105db764", "type": "github" }, "original": { @@ -111,24 +111,6 @@ "type": "github" } }, - "flake-utils": { - "inputs": { - "systems": "systems" - }, - "locked": { - "lastModified": 1710146030, - "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, "git-hooks-nix": { "inputs": { "flake-compat": [ @@ -181,7 +163,6 @@ "flake-compat": "flake-compat", "flake-parts": "flake-parts_2", "git-hooks-nix": "git-hooks-nix", - "nixfmt": "nixfmt", "nixpkgs": [ "nixpkgs" ], @@ -189,11 +170,11 @@ "nixpkgs-regression": "nixpkgs-regression" }, "locked": { - "lastModified": 1738382221, - "narHash": "sha256-3+qJBts5RTAtjCExo6bkqrttL+skpYZzPOVEzPSwVtc=", + "lastModified": 1740764831, + "narHash": "sha256-I/68odvSD6t2DK6j003f57H5WlKtKkVQfy0P+VCKb/w=", "owner": "NixOS", "repo": "nix", - "rev": "d949c8de7c7e84bb7537dc772609686c37033a3b", + "rev": "77f22db567c3377989b8127fc0b28c948f0559a1", "type": "github" }, "original": { @@ -216,11 +197,11 @@ "treefmt": "treefmt" }, "locked": { - "lastModified": 1738390452, - "narHash": "sha256-o8kg4q1V2xV9ZlszAnizXJK2c+fbhAeLbGoTUa2u1Bw=", + "lastModified": 1740809725, + "narHash": "sha256-0jq3H5nsU6leI+2pBEtqxqZWopaSrfhkw+q+GrtMGtA=", "owner": "yusdacra", "repo": "nix-cargo-integration", - "rev": "718d577808e3e31f445b6564d58b755294e72f42", + "rev": "d60df90181bf3b9dbbb8d4440f827afb7ad9fe22", "type": "github" }, "original": { @@ -229,31 +210,13 @@ "type": "github" } }, - "nixfmt": { - "inputs": { - "flake-utils": "flake-utils" - }, - "locked": { - "lastModified": 1736283758, - "narHash": "sha256-hrKhUp2V2fk/dvzTTHFqvtOg000G1e+jyIam+D4XqhA=", - "owner": "NixOS", - "repo": "nixfmt", - "rev": "8d4bd690c247004d90d8554f0b746b1231fe2436", - "type": "github" - }, - "original": { - "owner": "NixOS", - "repo": "nixfmt", - "type": "github" - } - }, "nixpkgs": { "locked": { - "lastModified": 1738410390, - "narHash": "sha256-xvTo0Aw0+veek7hvEVLzErmJyQkEcRk6PSR4zsRQFEc=", + "lastModified": 1740695751, + "narHash": "sha256-D+R+kFxy1KsheiIzkkx/6L63wEHBYX21OIwlFV8JvDs=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "3a228057f5b619feb3186e986dbe76278d707b6e", + "rev": "6313551cd05425cd5b3e63fe47dbc324eabb15e4", "type": "github" }, "original": { @@ -281,14 +244,14 @@ }, "nixpkgs-lib": { "locked": { - "lastModified": 1738452942, - "narHash": "sha256-vJzFZGaCpnmo7I6i416HaBLpC+hvcURh/BQwROcGIp8=", + "lastModified": 1740872140, + "narHash": "sha256-3wHafybyRfpUCLoE8M+uPVZinImg3xX+Nm6gEfN3G8I=", "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/072a6db25e947df2f31aab9eccd0ab75d5b2da11.tar.gz" + "url": "https://github.com/NixOS/nixpkgs/archive/6d3702243441165a03f699f64416f635220f4f15.tar.gz" }, "original": { "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/072a6db25e947df2f31aab9eccd0ab75d5b2da11.tar.gz" + "url": "https://github.com/NixOS/nixpkgs/archive/6d3702243441165a03f699f64416f635220f4f15.tar.gz" } }, "nixpkgs-regression": { @@ -315,11 +278,11 @@ ] }, "locked": { - "lastModified": 1736143030, - "narHash": "sha256-+hu54pAoLDEZT9pjHlqL9DNzWz0NbUn8NEAHP7PQPzU=", + "lastModified": 1738453229, + "narHash": "sha256-7H9XgNiGLKN1G1CgRh0vUL4AheZSYzPm+zmZ7vxbJdo=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "b905f6fc23a9051a6e1b741e1438dbfc0634c6de", + "rev": "32ea77a06711b758da0ad9bd6a844c5740a87abd", "type": "github" }, "original": { @@ -385,11 +348,11 @@ ] }, "locked": { - "lastModified": 1738376888, - "narHash": "sha256-S6ErHxkSm0iA7ZMsjjDaASWxbELYcdfv8BhOkkj1rHw=", + "lastModified": 1740796337, + "narHash": "sha256-FuoXrXZPoJEZQ3PF7t85tEpfBVID9JQIOnVKMNfTAb0=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "83284068670d5ae4a43641c4afb150f3446be70d", + "rev": "bbac9527bc6b28b6330b13043d0e76eac11720dc", "type": "github" }, "original": { @@ -421,21 +384,6 @@ "type": "github" } }, - "systems": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, "treefmt": { "inputs": { "nixpkgs": [ @@ -444,11 +392,11 @@ ] }, "locked": { - "lastModified": 1738070913, - "narHash": "sha256-j6jC12vCFsTGDmY2u1H12lMr62fnclNjuCtAdF1a4Nk=", + "lastModified": 1739829690, + "narHash": "sha256-mL1szCeIsjh6Khn3nH2cYtwO5YXG6gBiTw1A30iGeDU=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "bebf27d00f7d10ba75332a0541ac43676985dea3", + "rev": "3d0579f5cc93436052d94b73925b48973a104204", "type": "github" }, "original": { From 42d6b64ee09c574b460ebd383d7169899babce0b Mon Sep 17 00:00:00 2001 From: Hercules CI Effects Date: Sun, 2 Mar 2025 00:58:08 +0000 Subject: [PATCH 145/306] dev/flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'hercules-ci-effects': 'github:hercules-ci/hercules-ci-effects/6d1b6d5d59758b4f5f05745f774fc13cdc59da43?narHash=sha256-oJN/yvRL7G0WlR/hTkQIjFbPkzCV%2BsFnNB/38Tb9RL4%3D' (2025-01-30) → 'github:hercules-ci/hercules-ci-effects/06519cec8fb32d219006da6eacd255504a9996af?narHash=sha256-0CjCfbq0yHWexOrpO06e2WU1r5JAqR6ffy1zgM3NksI%3D' (2025-02-15) • Updated input 'hercules-ci-effects/flake-parts': 'github:hercules-ci/flake-parts/b905f6fc23a9051a6e1b741e1438dbfc0634c6de?narHash=sha256-%2Bhu54pAoLDEZT9pjHlqL9DNzWz0NbUn8NEAHP7PQPzU%3D' (2025-01-06) → 'github:hercules-ci/flake-parts/32ea77a06711b758da0ad9bd6a844c5740a87abd?narHash=sha256-7H9XgNiGLKN1G1CgRh0vUL4AheZSYzPm%2BzmZ7vxbJdo%3D' (2025-02-01) • Updated input 'hercules-ci-effects/nixpkgs': 'github:NixOS/nixpkgs/9abb87b552b7f55ac8916b6fc9e5cb486656a2f3?narHash=sha256-qwpCtZhSsSNQtK4xYGzMiyEDhkNzOCz/Vfu4oL2ETsQ%3D' (2025-01-13) → 'github:NixOS/nixpkgs/2ff53fe64443980e139eaa286017f53f88336dd0?narHash=sha256-%2B/bYK3DbPxMIvSL4zArkMX0LQvS7rzBKXnDXLfKyRVc%3D' (2025-02-13) • Updated input 'pre-commit-hooks-nix': 'github:cachix/pre-commit-hooks.nix/9364dc02281ce2d37a1f55b6e51f7c0f65a75f17?narHash=sha256-R10v2hoJRLq8jcL4syVFag7nIGE7m13qO48wRIukWNg%3D' (2025-01-21) → 'github:cachix/pre-commit-hooks.nix/25d4946dfc2021584f5bde1fbd2aa97353384a95?narHash=sha256-LWDIJvKWMW0tiih1jTcAK0ncTi3S9IF3gOhpCT1ydik%3D' (2025-03-01) (cherry picked from commit cd5f64e9c967f30f90f3df5d58a4dd2b7eb311fb) --- dev/flake.lock | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/dev/flake.lock b/dev/flake.lock index cee8e88..d977c06 100644 --- a/dev/flake.lock +++ b/dev/flake.lock @@ -24,11 +24,11 @@ ] }, "locked": { - "lastModified": 1736143030, - "narHash": "sha256-+hu54pAoLDEZT9pjHlqL9DNzWz0NbUn8NEAHP7PQPzU=", + "lastModified": 1738453229, + "narHash": "sha256-7H9XgNiGLKN1G1CgRh0vUL4AheZSYzPm+zmZ7vxbJdo=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "b905f6fc23a9051a6e1b741e1438dbfc0634c6de", + "rev": "32ea77a06711b758da0ad9bd6a844c5740a87abd", "type": "github" }, "original": { @@ -84,11 +84,11 @@ "nixpkgs": "nixpkgs" }, "locked": { - "lastModified": 1738237977, - "narHash": "sha256-oJN/yvRL7G0WlR/hTkQIjFbPkzCV+sFnNB/38Tb9RL4=", + "lastModified": 1739595404, + "narHash": "sha256-0CjCfbq0yHWexOrpO06e2WU1r5JAqR6ffy1zgM3NksI=", "owner": "hercules-ci", "repo": "hercules-ci-effects", - "rev": "6d1b6d5d59758b4f5f05745f774fc13cdc59da43", + "rev": "06519cec8fb32d219006da6eacd255504a9996af", "type": "github" }, "original": { @@ -141,11 +141,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1736798957, - "narHash": "sha256-qwpCtZhSsSNQtK4xYGzMiyEDhkNzOCz/Vfu4oL2ETsQ=", + "lastModified": 1739446958, + "narHash": "sha256-+/bYK3DbPxMIvSL4zArkMX0LQvS7rzBKXnDXLfKyRVc=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "9abb87b552b7f55ac8916b6fc9e5cb486656a2f3", + "rev": "2ff53fe64443980e139eaa286017f53f88336dd0", "type": "github" }, "original": { @@ -178,11 +178,11 @@ "nixpkgs": [] }, "locked": { - "lastModified": 1737465171, - "narHash": "sha256-R10v2hoJRLq8jcL4syVFag7nIGE7m13qO48wRIukWNg=", + "lastModified": 1740870877, + "narHash": "sha256-LWDIJvKWMW0tiih1jTcAK0ncTi3S9IF3gOhpCT1ydik=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "9364dc02281ce2d37a1f55b6e51f7c0f65a75f17", + "rev": "25d4946dfc2021584f5bde1fbd2aa97353384a95", "type": "github" }, "original": { From 155b37bd2a17ccfa5297ba714d26319b3adce7f9 Mon Sep 17 00:00:00 2001 From: Hercules CI Effects Date: Wed, 2 Apr 2025 00:58:59 +0000 Subject: [PATCH 146/306] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'flake-parts': 'github:hercules-ci/flake-parts/3876f6b87db82f33775b1ef5ea343986105db764?narHash=sha256-ZaMw0pdoUKigLpv9HiNDH2Pjnosg7NBYMJlHTIsHEUo%3D' (2025-03-01) → 'github:hercules-ci/flake-parts/c621e8422220273271f52058f618c94e405bb0f5?narHash=sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY%3D' (2025-04-01) • Updated input 'flake-parts/nixpkgs-lib': 'https://github.com/NixOS/nixpkgs/archive/6d3702243441165a03f699f64416f635220f4f15.tar.gz?narHash=sha256-3wHafybyRfpUCLoE8M%2BuPVZinImg3xX%2BNm6gEfN3G8I%3D' (2025-03-01) → 'github:nix-community/nixpkgs.lib/e4822aea2a6d1cdd36653c134cacfd64c97ff4fa?narHash=sha256-b1EdN3cULCqtorQ4QeWgLMrd5ZGOjLSLemfa00heasc%3D' (2025-03-30) • Updated input 'nix': 'github:NixOS/nix/77f22db567c3377989b8127fc0b28c948f0559a1?narHash=sha256-I/68odvSD6t2DK6j003f57H5WlKtKkVQfy0P%2BVCKb/w%3D' (2025-02-28) → 'github:NixOS/nix/cf409fd250db6e8e07d80b9871bceb5defdaf6e1?narHash=sha256-/YsfAN3uBmP29krurNlLxs3fIdwDSG21kqeKWzQlm9Q%3D' (2025-04-01) • Updated input 'nix-cargo-integration': 'github:yusdacra/nix-cargo-integration/d60df90181bf3b9dbbb8d4440f827afb7ad9fe22?narHash=sha256-0jq3H5nsU6leI%2B2pBEtqxqZWopaSrfhkw%2Bq%2BGrtMGtA%3D' (2025-03-01) → 'github:yusdacra/nix-cargo-integration/be686c02a3fbad61448abcc2049a9178a4cd903c?narHash=sha256-G4IRYK7vG2kA9Xr1KwXJH1SVc57qNPlQXBl6d/V6LSc%3D' (2025-04-01) • Updated input 'nix-cargo-integration/parts': 'github:hercules-ci/flake-parts/32ea77a06711b758da0ad9bd6a844c5740a87abd?narHash=sha256-7H9XgNiGLKN1G1CgRh0vUL4AheZSYzPm%2BzmZ7vxbJdo%3D' (2025-02-01) → 'github:hercules-ci/flake-parts/f4330d22f1c5d2ba72d3d22df5597d123fdb60a9?narHash=sha256-%2Bu2UunDA4Cl5Fci3m7S643HzKmIDAe%2BfiXrLqYsR2fs%3D' (2025-03-07) • Updated input 'nix-cargo-integration/rust-overlay': 'github:oxalica/rust-overlay/bbac9527bc6b28b6330b13043d0e76eac11720dc?narHash=sha256-FuoXrXZPoJEZQ3PF7t85tEpfBVID9JQIOnVKMNfTAb0%3D' (2025-03-01) → 'github:oxalica/rust-overlay/bee11c51c2cda3ac57c9e0149d94b86cc1b00d13?narHash=sha256-uLjVsb4Rxnp1zmFdPCDmdODd4RY6ETOeRj0IkC0ij/4%3D' (2025-04-01) • Updated input 'nix-cargo-integration/treefmt': 'github:numtide/treefmt-nix/3d0579f5cc93436052d94b73925b48973a104204?narHash=sha256-mL1szCeIsjh6Khn3nH2cYtwO5YXG6gBiTw1A30iGeDU%3D' (2025-02-17) → 'github:numtide/treefmt-nix/29a3d7b768c70addce17af0869f6e2bd8f5be4b7?narHash=sha256-WRAylyYptt6OX5eCEBWyTwOEqEtD6zt33rlUkr6u3cE%3D' (2025-03-27) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/6313551cd05425cd5b3e63fe47dbc324eabb15e4?narHash=sha256-D%2BR%2BkFxy1KsheiIzkkx/6L63wEHBYX21OIwlFV8JvDs%3D' (2025-02-27) → 'github:NixOS/nixpkgs/52faf482a3889b7619003c0daec593a1912fddc1?narHash=sha256-6hl6L/tRnwubHcA4pfUUtk542wn2Om%2BD4UnDhlDW9BE%3D' (2025-03-30) (cherry picked from commit d229c7e80acee1a7c87eddae7a893bce4078eb30) --- flake.lock | 57 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/flake.lock b/flake.lock index 798519f..b55f13c 100644 --- a/flake.lock +++ b/flake.lock @@ -77,11 +77,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1740872218, - "narHash": "sha256-ZaMw0pdoUKigLpv9HiNDH2Pjnosg7NBYMJlHTIsHEUo=", + "lastModified": 1743550720, + "narHash": "sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "3876f6b87db82f33775b1ef5ea343986105db764", + "rev": "c621e8422220273271f52058f618c94e405bb0f5", "type": "github" }, "original": { @@ -170,11 +170,11 @@ "nixpkgs-regression": "nixpkgs-regression" }, "locked": { - "lastModified": 1740764831, - "narHash": "sha256-I/68odvSD6t2DK6j003f57H5WlKtKkVQfy0P+VCKb/w=", + "lastModified": 1743546210, + "narHash": "sha256-/YsfAN3uBmP29krurNlLxs3fIdwDSG21kqeKWzQlm9Q=", "owner": "NixOS", "repo": "nix", - "rev": "77f22db567c3377989b8127fc0b28c948f0559a1", + "rev": "cf409fd250db6e8e07d80b9871bceb5defdaf6e1", "type": "github" }, "original": { @@ -197,11 +197,11 @@ "treefmt": "treefmt" }, "locked": { - "lastModified": 1740809725, - "narHash": "sha256-0jq3H5nsU6leI+2pBEtqxqZWopaSrfhkw+q+GrtMGtA=", + "lastModified": 1743488269, + "narHash": "sha256-G4IRYK7vG2kA9Xr1KwXJH1SVc57qNPlQXBl6d/V6LSc=", "owner": "yusdacra", "repo": "nix-cargo-integration", - "rev": "d60df90181bf3b9dbbb8d4440f827afb7ad9fe22", + "rev": "be686c02a3fbad61448abcc2049a9178a4cd903c", "type": "github" }, "original": { @@ -212,11 +212,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1740695751, - "narHash": "sha256-D+R+kFxy1KsheiIzkkx/6L63wEHBYX21OIwlFV8JvDs=", + "lastModified": 1743315132, + "narHash": "sha256-6hl6L/tRnwubHcA4pfUUtk542wn2Om+D4UnDhlDW9BE=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "6313551cd05425cd5b3e63fe47dbc324eabb15e4", + "rev": "52faf482a3889b7619003c0daec593a1912fddc1", "type": "github" }, "original": { @@ -244,14 +244,17 @@ }, "nixpkgs-lib": { "locked": { - "lastModified": 1740872140, - "narHash": "sha256-3wHafybyRfpUCLoE8M+uPVZinImg3xX+Nm6gEfN3G8I=", - "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/6d3702243441165a03f699f64416f635220f4f15.tar.gz" + "lastModified": 1743296961, + "narHash": "sha256-b1EdN3cULCqtorQ4QeWgLMrd5ZGOjLSLemfa00heasc=", + "owner": "nix-community", + "repo": "nixpkgs.lib", + "rev": "e4822aea2a6d1cdd36653c134cacfd64c97ff4fa", + "type": "github" }, "original": { - "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/6d3702243441165a03f699f64416f635220f4f15.tar.gz" + "owner": "nix-community", + "repo": "nixpkgs.lib", + "type": "github" } }, "nixpkgs-regression": { @@ -278,11 +281,11 @@ ] }, "locked": { - "lastModified": 1738453229, - "narHash": "sha256-7H9XgNiGLKN1G1CgRh0vUL4AheZSYzPm+zmZ7vxbJdo=", + "lastModified": 1741352980, + "narHash": "sha256-+u2UunDA4Cl5Fci3m7S643HzKmIDAe+fiXrLqYsR2fs=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "32ea77a06711b758da0ad9bd6a844c5740a87abd", + "rev": "f4330d22f1c5d2ba72d3d22df5597d123fdb60a9", "type": "github" }, "original": { @@ -348,11 +351,11 @@ ] }, "locked": { - "lastModified": 1740796337, - "narHash": "sha256-FuoXrXZPoJEZQ3PF7t85tEpfBVID9JQIOnVKMNfTAb0=", + "lastModified": 1743475035, + "narHash": "sha256-uLjVsb4Rxnp1zmFdPCDmdODd4RY6ETOeRj0IkC0ij/4=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "bbac9527bc6b28b6330b13043d0e76eac11720dc", + "rev": "bee11c51c2cda3ac57c9e0149d94b86cc1b00d13", "type": "github" }, "original": { @@ -392,11 +395,11 @@ ] }, "locked": { - "lastModified": 1739829690, - "narHash": "sha256-mL1szCeIsjh6Khn3nH2cYtwO5YXG6gBiTw1A30iGeDU=", + "lastModified": 1743081648, + "narHash": "sha256-WRAylyYptt6OX5eCEBWyTwOEqEtD6zt33rlUkr6u3cE=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "3d0579f5cc93436052d94b73925b48973a104204", + "rev": "29a3d7b768c70addce17af0869f6e2bd8f5be4b7", "type": "github" }, "original": { From 2cab738e58530f1301886904c2940fc7a22ad3c2 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 2 Apr 2025 12:22:48 +0200 Subject: [PATCH 147/306] feat: EvalStateBuilder This supports the more "advanced" `nix_eval_state_builder`, which has more methods that we'll want to use. (cherry picked from commit a96047000df6b7022d166a8c35bb6e3075e5eddb) --- rust/nix-expr/src/eval_state.rs | 112 +++++++++++++++++++++----------- 1 file changed, 74 insertions(+), 38 deletions(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 64111b5..e383fd4 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -78,6 +78,76 @@ impl Drop for EvalStateRef { } } } +pub struct EvalStateBuilder { + eval_state_builder: *mut raw::eval_state_builder, + lookup_path: Vec, + store: Store, +} +impl Drop for EvalStateBuilder { + fn drop(&mut self) { + unsafe { + raw::eval_state_builder_free(self.eval_state_builder); + } + } +} +impl EvalStateBuilder { + pub fn new(store: Store) -> Result { + let mut context = Context::new(); + let eval_state_builder = + unsafe { check_call!(raw::eval_state_builder_new(&mut context, store.raw_ptr())) }?; + Ok(EvalStateBuilder { + store, + eval_state_builder, + lookup_path: Vec::new(), + }) + } + pub fn lookup_path<'a>(mut self, path: impl IntoIterator) -> Result { + let lookup_path: Vec = path + .into_iter() + .map(|path| { + CString::new(path).with_context(|| { + format!("EvalStateBuilder::lookup_path: path `{path}` contains null byte") + }) + }) + .collect::>()?; + self.lookup_path = lookup_path; + Ok(self) + } + pub fn build(&self) -> Result { + // Make sure the library is initialized + init()?; + + let mut context = Context::new(); + + // Note: these raw C string pointers borrow from self.lookup_path + let mut lookup_path: Vec<*const c_char> = self + .lookup_path + .iter() + .map(|s| s.as_ptr()) + .chain(std::iter::once(null())) // signal the end of the array + .collect(); + + unsafe { + check_call!(raw::eval_state_builder_set_lookup_path( + &mut context, + self.eval_state_builder, + lookup_path.as_mut_ptr() + ))?; + } + + let eval_state = + unsafe { check_call!(raw::eval_state_build(&mut context, self.eval_state_builder)) }?; + Ok(EvalState { + eval_state: Arc::new(EvalStateRef { + eval_state: NonNull::new(eval_state).unwrap_or_else(|| { + panic!("nix_state_create returned a null pointer without an error") + }), + }), + store: self.store.clone(), + context, + }) + } +} pub struct EvalState { eval_state: Arc, @@ -85,45 +155,11 @@ pub struct EvalState { pub(crate) context: Context, } impl EvalState { + /// For more options, use [EvalStateBuilder]. pub fn new<'a>(store: Store, lookup_path: impl IntoIterator) -> Result { - let mut context = Context::new(); - - // this intermediate value must be here and must not be moved - // because it owns the data the `*const c_char` pointers point to. - let lookup_path: Vec = lookup_path - .into_iter() - .map(|path| { - CString::new(path).with_context(|| { - format!("EvalState::new: lookup_path `{path}` contains null byte") - }) - }) - .collect::>()?; - - // this intermediate value owns the data the `*mut *const c_char` pointer points to. - let mut lookup_path: Vec<*const c_char> = lookup_path - .iter() - .map(|s| s.as_ptr()) - .chain(std::iter::once(null())) // signal the end of the array - .collect(); - - init()?; - - let eval_state = unsafe { - check_call!(raw::state_create( - &mut context, - lookup_path.as_mut_ptr(), - store.raw_ptr() - )) - }?; - Ok(EvalState { - eval_state: Arc::new(EvalStateRef { - eval_state: NonNull::new(eval_state).unwrap_or_else(|| { - panic!("nix_state_create returned a null pointer without an error") - }), - }), - store, - context, - }) + EvalStateBuilder::new(store)? + .lookup_path(lookup_path)? + .build() } /// # Safety From 8f7d3d538562c79711c1f36d5b244fec193cab65 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 2 Apr 2025 12:43:28 +0200 Subject: [PATCH 148/306] maint: Add EvalStateBuilder.flakes(settings) (cherry picked from commit ee55ef51a60abc692fdef69614c9a8ccfa9358e0) --- rust/nix-expr/src/eval_state.rs | 3 +++ rust/nix-flake/src/lib.rs | 26 ++++++++++++++++++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index e383fd4..af1718c 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -147,6 +147,9 @@ impl EvalStateBuilder { context, }) } + pub fn raw_ptr(&self) -> *mut raw::eval_state_builder { + self.eval_state_builder + } } pub struct EvalState { diff --git a/rust/nix-flake/src/lib.rs b/rust/nix-flake/src/lib.rs index 359b2c7..a93fb80 100644 --- a/rust/nix-flake/src/lib.rs +++ b/rust/nix-flake/src/lib.rs @@ -18,9 +18,31 @@ impl FlakeSettings { let s = unsafe { context::check_call!(raw::flake_settings_new(&mut ctx)) }?; Ok(FlakeSettings { ptr: s }) } - pub fn init_globally(&mut self) -> Result<()> { + fn add_to_eval_state_builder( + &self, + builder: &mut nix_expr::eval_state::EvalStateBuilder, + ) -> Result<()> { let mut ctx = Context::new(); - unsafe { context::check_call!(raw::flake_init_global(&mut ctx, self.ptr)) }?; + unsafe { + context::check_call!(raw::flake_settings_add_to_eval_state_builder( + &mut ctx, + self.ptr, + builder.raw_ptr() + )) + }?; Ok(()) } } + +pub trait EvalStateBuilderExt { + fn flakes(self, settings: &FlakeSettings) -> Result; +} +impl EvalStateBuilderExt for nix_expr::eval_state::EvalStateBuilder { + fn flakes( + mut self, + settings: &FlakeSettings, + ) -> Result { + settings.add_to_eval_state_builder(&mut self)?; + Ok(self) + } +} From 5a6cd501773f5c38324abcf869b35c013342de95 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 2 Apr 2025 18:54:19 +0200 Subject: [PATCH 149/306] maint: Update nix to 2.29pre (cherry picked from commit 81dee2336145d5fb4f32c95ff0319f1f8fdcfd96) --- flake.lock | 7 +++---- flake.nix | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/flake.lock b/flake.lock index b55f13c..15da0d9 100644 --- a/flake.lock +++ b/flake.lock @@ -170,16 +170,15 @@ "nixpkgs-regression": "nixpkgs-regression" }, "locked": { - "lastModified": 1743546210, - "narHash": "sha256-/YsfAN3uBmP29krurNlLxs3fIdwDSG21kqeKWzQlm9Q=", + "lastModified": 1744095711, + "narHash": "sha256-Aqnj5+sA7B4ZRympuyfWPPK83iomKHEHMYhlwslI8iA=", "owner": "NixOS", "repo": "nix", - "rev": "cf409fd250db6e8e07d80b9871bceb5defdaf6e1", + "rev": "e76bbe413e86e3208bb9824e339d59af25327101", "type": "github" }, "original": { "owner": "NixOS", - "ref": "master", "repo": "nix", "type": "github" } diff --git a/flake.nix b/flake.nix index 63d7b5a..f75342b 100644 --- a/flake.nix +++ b/flake.nix @@ -3,7 +3,7 @@ inputs = { flake-parts.url = "github:hercules-ci/flake-parts"; - nix.url = "github:NixOS/nix/master"; + nix.url = "github:NixOS/nix"; nix.inputs.nixpkgs.follows = "nixpkgs"; nix-cargo-integration.url = "github:yusdacra/nix-cargo-integration"; nix-cargo-integration.inputs.nixpkgs.follows = "nixpkgs"; From 3b7a14f62b2915790dc155f58ab9404acce3df6b Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 2 Apr 2025 13:30:57 +0200 Subject: [PATCH 150/306] feat: EvalState.require_bool (cherry picked from commit e0d451f22cedae383fa2f18809f3494e674371b4) --- rust/nix-expr/src/eval_state.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index af1718c..af24045 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -255,6 +255,14 @@ impl EvalState { unsafe { check_call!(raw::get_int(&mut self.context, v.raw_ptr())) } } + pub fn require_bool(&mut self, v: &Value) -> Result { + let t = self.value_type(v)?; + if t != ValueType::Bool { + bail!("expected a bool, but got a {:?}", t); + } + unsafe { check_call!(raw::get_bool(&mut self.context, v.raw_ptr())) } + } + /// Evaluate, and require that the value is an attrset. /// Returns a list of the keys in the attrset. /// @@ -813,6 +821,13 @@ mod tests { es.force(&v).unwrap(); let t = es.value_type_unforced(&v); assert!(t == Some(ValueType::Bool)); + let b = es.require_bool(&v).unwrap(); + assert!(b); + + let v = es.eval_from_string("false", "").unwrap(); + es.require_bool(&v).unwrap(); + let b = es.require_bool(&v).unwrap(); + assert!(!b); }) .unwrap(); } @@ -848,6 +863,22 @@ mod tests { .unwrap(); } + #[test] + fn eval_state_require_bool_forces_thunk() { + gc_registering_current_thread(|| { + let store = Store::open(None, HashMap::new()).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); + let f = es.eval_from_string("x: !x", "").unwrap(); + let a = es.eval_from_string("true", "").unwrap(); + let v = es.new_value_apply(&f, &a).unwrap(); + let t = es.value_type_unforced(&v); + assert!(t == None); + let i = es.require_bool(&v).unwrap(); + assert!(i == false); + }) + .unwrap(); + } + /// A helper that turns an expression into a thunk. fn make_thunk(es: &mut EvalState, expr: &str) -> Value { // This would be silly in real code, but it works for the current Nix implementation. From 7ae38f296fc2d8de3011563ece5d1060d81fb9a1 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 2 Apr 2025 13:34:22 +0200 Subject: [PATCH 151/306] test: Make sure getFlake exists (cherry picked from commit 3c1bb4af0009104e836f9110687a1da4e0ee3bb7) --- rust/nix-flake/src/lib.rs | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/rust/nix-flake/src/lib.rs b/rust/nix-flake/src/lib.rs index a93fb80..2c86450 100644 --- a/rust/nix-flake/src/lib.rs +++ b/rust/nix-flake/src/lib.rs @@ -46,3 +46,38 @@ impl EvalStateBuilderExt for nix_expr::eval_state::EvalStateBuilder { Ok(self) } } + +#[cfg(test)] +mod tests { + use nix_expr::eval_state::{gc_register_my_thread, EvalStateBuilder}; + use nix_store::store::Store; + + use super::*; + + fn init() { + nix_util::settings::set("experimental-features", "flakes").unwrap(); + } + + #[test] + fn flake_settings_getflake_exists() { + init(); + let gc_registration = gc_register_my_thread(); + let store = Store::open(None, []).unwrap(); + let mut eval_state = EvalStateBuilder::new(store) + .unwrap() + .flakes(&FlakeSettings::new().unwrap()) + .unwrap() + .build() + .unwrap(); + + let v = eval_state + .eval_from_string("builtins?getFlake", "") + .unwrap(); + + let b = eval_state.require_bool(&v).unwrap(); + + assert_eq!(b, true); + + drop(gc_registration); + } +} From bbf245ef1a1642300d669c92d00ebe23c3505ec4 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 2 Apr 2025 18:38:20 +0200 Subject: [PATCH 152/306] feat: nix-fetchers crate (cherry picked from commit 27d572403ac98d83d652481da6c22ad50bb00168) --- rust/Cargo.lock | 14 +++++++++++++ rust/Cargo.toml | 5 +++-- rust/nix-fetchers/Cargo.toml | 17 ++++++++++++++++ rust/nix-fetchers/src/lib.rs | 38 ++++++++++++++++++++++++++++++++++++ rust/nix-flake/Cargo.toml | 1 + 5 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 rust/nix-fetchers/Cargo.toml create mode 100644 rust/nix-fetchers/src/lib.rs diff --git a/rust/Cargo.lock b/rust/Cargo.lock index eaa0409..5368463 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -323,6 +323,19 @@ dependencies = [ "tempfile", ] +[[package]] +name = "nix-fetchers" +version = "0.1.0" +dependencies = [ + "anyhow", + "cstr", + "ctor", + "nix-c-raw", + "nix-store", + "nix-util", + "tempfile", +] + [[package]] name = "nix-flake" version = "0.1.0" @@ -333,6 +346,7 @@ dependencies = [ "lazy_static", "nix-c-raw", "nix-expr", + "nix-fetchers", "nix-store", "nix-util", "tempfile", diff --git a/rust/Cargo.toml b/rust/Cargo.toml index d234fbd..f71d8a8 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,9 +1,10 @@ [workspace] members = [ "nix-c-raw", - "nix-flake", "nix-expr", - "nix-util", + "nix-fetchers", + "nix-flake", "nix-store", + "nix-util", ] resolver = "2" diff --git a/rust/nix-fetchers/Cargo.toml b/rust/nix-fetchers/Cargo.toml new file mode 100644 index 0000000..31d12c5 --- /dev/null +++ b/rust/nix-fetchers/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "nix-fetchers" +version = "0.1.0" +edition = "2021" +license = "LGPL-2.1" + +[lib] +path = "src/lib.rs" + +[dependencies] +anyhow = "1.0.79" +nix-store = { path = "../nix-store" } +nix-util = { path = "../nix-util" } +nix-c-raw = { path = "../nix-c-raw" } +ctor = "0.2.7" +tempfile = "3.10.1" +cstr = "0.2.12" diff --git a/rust/nix-fetchers/src/lib.rs b/rust/nix-fetchers/src/lib.rs new file mode 100644 index 0000000..091830c --- /dev/null +++ b/rust/nix-fetchers/src/lib.rs @@ -0,0 +1,38 @@ +use anyhow::{Context as _, Result}; +use nix_c_raw as raw; +use nix_util::context::{self, Context}; +use std::ptr::NonNull; + +pub struct FetchersSettings { + pub(crate) ptr: NonNull, +} +impl Drop for FetchersSettings { + fn drop(&mut self) { + unsafe { + raw::fetchers_settings_free(self.ptr.as_ptr()); + } + } +} +impl FetchersSettings { + pub fn new() -> Result { + let mut ctx = Context::new(); + let ptr = unsafe { context::check_call!(raw::fetchers_settings_new(&mut ctx))? }; + Ok(FetchersSettings { + ptr: NonNull::new(ptr).context("fetchers_settings_new unexpectedly returned null")?, + }) + } + + pub fn raw_ptr(&self) -> *mut raw::fetchers_settings { + self.ptr.as_ptr() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn fetchers_settings_new() { + let _ = FetchersSettings::new().unwrap(); + } +} diff --git a/rust/nix-flake/Cargo.toml b/rust/nix-flake/Cargo.toml index 5b7f2d4..0121902 100644 --- a/rust/nix-flake/Cargo.toml +++ b/rust/nix-flake/Cargo.toml @@ -10,6 +10,7 @@ path = "src/lib.rs" [dependencies] anyhow = "1.0.79" nix-expr = { path = "../nix-expr" } +nix-fetchers = { path = "../nix-fetchers" } nix-store = { path = "../nix-store" } nix-util = { path = "../nix-util" } nix-c-raw = { path = "../nix-c-raw" } From e6b993a42ba1a479cdcec32f8c1e0c123676ef1a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 2 Apr 2025 18:49:03 +0200 Subject: [PATCH 153/306] feat: nix_expr::value::__private Exposes the Value constructors, for use by other bindings like nix-flake, which needs to construct e.g. a flake outputs Value. See https://github.com/nixops4/nixops4/issues/25 > We have two related issues: > A bunch of implementation details cannot be made private, since they must be used from one of the other crates (e.g. Values are defined in the value module but used from eval_state > While we don't want users to need to use these features, it may be good to provide escape hatches so they can interface with the raw API if they need more control. > Problem (1) has been solved in other crates with a __private module with #[doc(hidden)] set. See for instance: I'm leaving docs turned on for (2). The issue has more thoughts about alternatives. (cherry picked from commit eb6744d1519febe5b6aa6233eb3f3e8a049f12d4) --- rust/nix-expr/src/value.rs | 2 ++ rust/nix-expr/src/value/__private.rs | 13 +++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 rust/nix-expr/src/value/__private.rs diff --git a/rust/nix-expr/src/value.rs b/rust/nix-expr/src/value.rs index ca7b5a4..bfa7011 100644 --- a/rust/nix-expr/src/value.rs +++ b/rust/nix-expr/src/value.rs @@ -1,3 +1,5 @@ +pub mod __private; + use nix_c_raw as raw; use nix_util::{check_call, context::Context}; use std::ptr::{null_mut, NonNull}; diff --git a/rust/nix-expr/src/value/__private.rs b/rust/nix-expr/src/value/__private.rs new file mode 100644 index 0000000..6b5b2e8 --- /dev/null +++ b/rust/nix-expr/src/value/__private.rs @@ -0,0 +1,13 @@ +//! Functions that are relevant for other bindings modules, but normally not end users. +use super::Value; +use nix_c_raw as raw; + +/// See [Value::new]. +pub unsafe fn raw_value_new(ptr: *mut raw::Value) -> Value { + Value::new(ptr) +} + +/// See [Value::new_borrowed]. +pub unsafe fn raw_value_new_borrowed(ptr: *mut raw::Value) -> Value { + Value::new_borrowed(ptr) +} From b77a3a20858c579e6c7a9abdfe3e1ce8aacb0c0e Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 2 Apr 2025 18:52:29 +0200 Subject: [PATCH 154/306] feat: nix-flake: Basic locking (cherry picked from commit cfd374f9deda7d40229198911516b22bc3d82626) --- rust/nix-flake/src/lib.rs | 218 +++++++++++++++++++++++++++++++++++++- 1 file changed, 216 insertions(+), 2 deletions(-) diff --git a/rust/nix-flake/src/lib.rs b/rust/nix-flake/src/lib.rs index 2c86450..fc7ae72 100644 --- a/rust/nix-flake/src/lib.rs +++ b/rust/nix-flake/src/lib.rs @@ -1,6 +1,14 @@ -use anyhow::Result; +use std::ptr::NonNull; + +use anyhow::{Context as _, Result}; use nix_c_raw as raw; -use nix_util::context::{self, Context}; +use nix_expr::eval_state::EvalState; +use nix_fetchers::FetchersSettings; +use nix_util::{ + context::{self, Context}, + result_string_init, + string_return::{callback_get_result_string, callback_get_result_string_data}, +}; pub struct FlakeSettings { pub(crate) ptr: *mut raw::flake_settings, @@ -47,6 +55,149 @@ impl EvalStateBuilderExt for nix_expr::eval_state::EvalStateBuilder { } } +pub struct FlakeReferenceParseFlags { + pub(crate) ptr: NonNull, +} +impl Drop for FlakeReferenceParseFlags { + fn drop(&mut self) { + unsafe { + raw::flake_reference_parse_flags_free(self.ptr.as_ptr()); + } + } +} +impl FlakeReferenceParseFlags { + pub fn new(settings: &FlakeSettings) -> Result { + let mut ctx = Context::new(); + let ptr = unsafe { + context::check_call!(raw::flake_reference_parse_flags_new(&mut ctx, settings.ptr)) + }?; + let ptr = NonNull::new(ptr) + .context("flake_reference_parse_flags_new unexpectedly returned null")?; + Ok(FlakeReferenceParseFlags { ptr }) + } + pub fn set_base_directory(&mut self, base_directory: &str) -> Result<()> { + let mut ctx = Context::new(); + unsafe { + context::check_call!(raw::flake_reference_parse_flags_set_base_directory( + &mut ctx, + self.ptr.as_ptr(), + base_directory.as_ptr() as *const i8, + base_directory.len() + )) + }?; + Ok(()) + } +} + +pub struct FlakeReference { + pub(crate) ptr: NonNull, +} +impl Drop for FlakeReference { + fn drop(&mut self) { + unsafe { + raw::flake_reference_free(self.ptr.as_ptr()); + } + } +} +impl FlakeReference { + pub fn parse_with_fragment( + fetch_settings: &FetchersSettings, + flake_settings: &FlakeSettings, + flags: &FlakeReferenceParseFlags, + reference: &str, + ) -> Result<(FlakeReference, String)> { + let mut ctx = Context::new(); + let mut r = result_string_init!(); + let mut ptr: *mut raw::flake_reference = std::ptr::null_mut(); + unsafe { + context::check_call!(raw::flake_reference_and_fragment_from_string( + &mut ctx, + fetch_settings.raw_ptr(), + flake_settings.ptr, + flags.ptr.as_ptr(), + reference.as_ptr() as *const i8, + reference.len(), + // pointer to ptr + &mut ptr, + Some(callback_get_result_string), + callback_get_result_string_data(&mut r) + )) + }?; + let ptr = NonNull::new(ptr) + .context("flake_reference_and_fragment_from_string unexpectedly returned null")?; + Ok((FlakeReference { ptr: ptr }, r?)) + } +} + +pub struct FlakeLockFlags { + pub(crate) ptr: *mut raw::flake_lock_flags, +} +impl Drop for FlakeLockFlags { + fn drop(&mut self) { + unsafe { + raw::flake_lock_flags_free(self.ptr); + } + } +} +impl FlakeLockFlags { + pub fn new(settings: &FlakeSettings) -> Result { + let mut ctx = Context::new(); + let s = unsafe { context::check_call!(raw::flake_lock_flags_new(&mut ctx, settings.ptr)) }?; + Ok(FlakeLockFlags { ptr: s }) + } +} + +pub struct LockedFlake { + pub(crate) ptr: NonNull, +} +impl Drop for LockedFlake { + fn drop(&mut self) { + unsafe { + raw::locked_flake_free(self.ptr.as_ptr()); + } + } +} +impl LockedFlake { + pub fn lock( + fetch_settings: &FetchersSettings, + flake_settings: &FlakeSettings, + eval_state: &EvalState, + flags: &FlakeLockFlags, + flake_ref: &FlakeReference, + ) -> Result { + let mut ctx = Context::new(); + let ptr = unsafe { + context::check_call!(raw::flake_lock( + &mut ctx, + fetch_settings.raw_ptr(), + flake_settings.ptr, + eval_state.raw_ptr(), + flags.ptr, + flake_ref.ptr.as_ptr() + )) + }?; + let ptr = NonNull::new(ptr).context("flake_lock unexpectedly returned null")?; + Ok(LockedFlake { ptr }) + } + + pub fn outputs( + &self, + flake_settings: &FlakeSettings, + eval_state: &mut EvalState, + ) -> Result { + let mut ctx = Context::new(); + unsafe { + let r = context::check_call!(raw::locked_flake_get_output_attrs( + &mut ctx, + flake_settings.ptr, + eval_state.raw_ptr(), + self.ptr.as_ptr() + ))?; + Ok(nix_expr::value::__private::raw_value_new(r)) + } + } +} + #[cfg(test)] mod tests { use nix_expr::eval_state::{gc_register_my_thread, EvalStateBuilder}; @@ -80,4 +231,67 @@ mod tests { drop(gc_registration); } + + #[test] + fn flake_lock_load_flake() { + init(); + let gc_registration = gc_register_my_thread(); + let store = Store::open(None, []).unwrap(); + let flake_settings = FlakeSettings::new().unwrap(); + let mut eval_state = EvalStateBuilder::new(store) + .unwrap() + .flakes(&flake_settings) + .unwrap() + .build() + .unwrap(); + + let tmp_dir = tempfile::tempdir().unwrap(); + + // Create flake.nix + let flake_nix = tmp_dir.path().join("flake.nix"); + std::fs::write( + &flake_nix, + r#" +{ + outputs = { ... }: { + hello = "potato"; + }; +} + "#, + ) + .unwrap(); + + let flake_lock_flags = FlakeLockFlags::new(&flake_settings).unwrap(); + + let (flake_ref, fragment) = FlakeReference::parse_with_fragment( + &FetchersSettings::new().unwrap(), + &flake_settings, + &FlakeReferenceParseFlags::new(&flake_settings).unwrap(), + &format!("path:{}#subthing", tmp_dir.path().display()), + ) + .unwrap(); + + assert_eq!(fragment, "subthing"); + + let locked_flake = LockedFlake::lock( + &FetchersSettings::new().unwrap(), + &flake_settings, + &eval_state, + &flake_lock_flags, + &flake_ref, + ) + .unwrap(); + + let outputs = locked_flake + .outputs(&flake_settings, &mut eval_state) + .unwrap(); + + let hello = eval_state.require_attrs_select(&outputs, &"hello").unwrap(); + let hello = eval_state.require_string(&hello).unwrap(); + + assert_eq!(hello, "potato"); + + drop(tmp_dir); + drop(gc_registration); + } } From 30bcd71527d63fe19119eec51f72d83591a4051b Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 4 Apr 2025 14:38:50 +0200 Subject: [PATCH 155/306] feat: nix-flake: Flake overriding (cherry picked from commit e1fa32fc40f2520aab96fae3bb1700b2242cb548) --- rust/nix-flake/src/lib.rs | 287 +++++++++++++++++++++++++++++++++++++- 1 file changed, 286 insertions(+), 1 deletion(-) diff --git a/rust/nix-flake/src/lib.rs b/rust/nix-flake/src/lib.rs index fc7ae72..77ccca7 100644 --- a/rust/nix-flake/src/lib.rs +++ b/rust/nix-flake/src/lib.rs @@ -1,4 +1,4 @@ -use std::ptr::NonNull; +use std::{ffi::CString, ptr::NonNull}; use anyhow::{Context as _, Result}; use nix_c_raw as raw; @@ -145,6 +145,45 @@ impl FlakeLockFlags { let s = unsafe { context::check_call!(raw::flake_lock_flags_new(&mut ctx, settings.ptr)) }?; Ok(FlakeLockFlags { ptr: s }) } + pub fn set_mode_write_as_needed(&mut self) -> Result<()> { + let mut ctx = Context::new(); + unsafe { + context::check_call!(raw::flake_lock_flags_set_mode_write_as_needed( + &mut ctx, self.ptr + )) + }?; + Ok(()) + } + pub fn set_mode_check(&mut self) -> Result<()> { + let mut ctx = Context::new(); + unsafe { context::check_call!(raw::flake_lock_flags_set_mode_check(&mut ctx, self.ptr)) }?; + Ok(()) + } + pub fn set_mode_virtual(&mut self) -> Result<()> { + let mut ctx = Context::new(); + unsafe { + context::check_call!(raw::flake_lock_flags_set_mode_virtual(&mut ctx, self.ptr)) + }?; + Ok(()) + } + pub fn add_input_override( + &mut self, + override_path: &str, + override_ref: &FlakeReference, + ) -> Result<()> { + let mut ctx = Context::new(); + unsafe { + context::check_call!(raw::flake_lock_flags_add_input_override( + &mut ctx, + self.ptr, + CString::new(override_path) + .context("Failed to create CString for override_path")? + .as_ptr(), + override_ref.ptr.as_ptr() + )) + }?; + Ok(()) + } } pub struct LockedFlake { @@ -294,4 +333,250 @@ mod tests { drop(tmp_dir); drop(gc_registration); } + + #[test] + fn flake_lock_load_flake_with_flags() { + init(); + let gc_registration = gc_register_my_thread(); + let store = Store::open(None, []).unwrap(); + let flake_settings = FlakeSettings::new().unwrap(); + let mut eval_state = EvalStateBuilder::new(store) + .unwrap() + .flakes(&flake_settings) + .unwrap() + .build() + .unwrap(); + + let tmp_dir = tempfile::tempdir().unwrap(); + + let flake_dir_a = tmp_dir.path().join("a"); + let flake_dir_b = tmp_dir.path().join("b"); + let flake_dir_c = tmp_dir.path().join("c"); + + std::fs::create_dir_all(&flake_dir_a).unwrap(); + std::fs::create_dir_all(&flake_dir_b).unwrap(); + std::fs::create_dir_all(&flake_dir_c).unwrap(); + + let flake_dir_a_str = flake_dir_a.to_str().unwrap(); + let flake_dir_c_str = flake_dir_c.to_str().unwrap(); + + // a + std::fs::write( + &tmp_dir.path().join("a/flake.nix"), + r#" + { + inputs.b.url = "@flake_dir_b@"; + outputs = { b, ... }: { + hello = b.hello; + }; + } + "# + .replace("@flake_dir_b@", flake_dir_b.to_str().unwrap()), + ) + .unwrap(); + + // b + std::fs::write( + &tmp_dir.path().join("b/flake.nix"), + r#" + { + outputs = { ... }: { + hello = "BOB"; + }; + } + "#, + ) + .unwrap(); + + // c + std::fs::write( + &tmp_dir.path().join("c/flake.nix"), + r#" + { + outputs = { ... }: { + hello = "Claire"; + }; + } + "#, + ) + .unwrap(); + + let mut flake_lock_flags = FlakeLockFlags::new(&flake_settings).unwrap(); + + let mut flake_reference_parse_flags = + FlakeReferenceParseFlags::new(&flake_settings).unwrap(); + + flake_reference_parse_flags + .set_base_directory(tmp_dir.path().to_str().unwrap()) + .unwrap(); + + let (flake_ref_a, fragment) = FlakeReference::parse_with_fragment( + &FetchersSettings::new().unwrap(), + &flake_settings, + &flake_reference_parse_flags, + &format!("path:{}", &flake_dir_a_str), + ) + .unwrap(); + + assert_eq!(fragment, ""); + + // Step 1: Do not update (check), fails + + flake_lock_flags.set_mode_check().unwrap(); + + let locked_flake = LockedFlake::lock( + &FetchersSettings::new().unwrap(), + &flake_settings, + &eval_state, + &flake_lock_flags, + &flake_ref_a, + ); + // Has not been locked and would need to write a lock file. + assert!(locked_flake.is_err()); + let saved_err = match locked_flake { + Ok(_) => panic!("Expected error, but got Ok"), + Err(e) => e, + }; + + // Step 2: Update but do not write, succeeds + flake_lock_flags.set_mode_virtual().unwrap(); + + let locked_flake = LockedFlake::lock( + &FetchersSettings::new().unwrap(), + &flake_settings, + &eval_state, + &flake_lock_flags, + &flake_ref_a, + ) + .unwrap(); + + let outputs = locked_flake + .outputs(&flake_settings, &mut eval_state) + .unwrap(); + + let hello = eval_state.require_attrs_select(&outputs, &"hello").unwrap(); + let hello = eval_state.require_string(&hello).unwrap(); + + assert_eq!(hello, "BOB"); + + // Step 3: The lock was not written, so Step 1 would fail again + + flake_lock_flags.set_mode_check().unwrap(); + + let locked_flake = LockedFlake::lock( + &FetchersSettings::new().unwrap(), + &flake_settings, + &eval_state, + &flake_lock_flags, + &flake_ref_a, + ); + // Has not been locked and would need to write a lock file. + assert!(locked_flake.is_err()); + match locked_flake { + Ok(_) => panic!("Expected error, but got Ok"), + Err(e) => { + assert_eq!(e.to_string(), saved_err.to_string()); + } + }; + + // Step 4: Update and write, succeeds + + flake_lock_flags.set_mode_write_as_needed().unwrap(); + + let locked_flake = LockedFlake::lock( + &FetchersSettings::new().unwrap(), + &flake_settings, + &eval_state, + &flake_lock_flags, + &flake_ref_a, + ) + .unwrap(); + + let outputs = locked_flake + .outputs(&flake_settings, &mut eval_state) + .unwrap(); + let hello = eval_state.require_attrs_select(&outputs, &"hello").unwrap(); + let hello = eval_state.require_string(&hello).unwrap(); + assert_eq!(hello, "BOB"); + + // Step 5: Lock was written, so Step 1 succeeds + + flake_lock_flags.set_mode_check().unwrap(); + + let locked_flake = LockedFlake::lock( + &FetchersSettings::new().unwrap(), + &flake_settings, + &eval_state, + &flake_lock_flags, + &flake_ref_a, + ) + .unwrap(); + + let outputs = locked_flake + .outputs(&flake_settings, &mut eval_state) + .unwrap(); + let hello = eval_state.require_attrs_select(&outputs, &"hello").unwrap(); + let hello = eval_state.require_string(&hello).unwrap(); + assert_eq!(hello, "BOB"); + + // Step 6: Lock with override, do not write + + // This shouldn't matter; write_as_needed will be overridden + flake_lock_flags.set_mode_write_as_needed().unwrap(); + + let (flake_ref_c, fragment) = FlakeReference::parse_with_fragment( + &FetchersSettings::new().unwrap(), + &flake_settings, + &flake_reference_parse_flags, + &format!("path:{}", &flake_dir_c_str), + ) + .unwrap(); + assert_eq!(fragment, ""); + + flake_lock_flags + .add_input_override("b", &flake_ref_c) + .unwrap(); + + let locked_flake = LockedFlake::lock( + &FetchersSettings::new().unwrap(), + &flake_settings, + &eval_state, + &flake_lock_flags, + &flake_ref_a, + ) + .unwrap(); + + let outputs = locked_flake + .outputs(&flake_settings, &mut eval_state) + .unwrap(); + let hello = eval_state.require_attrs_select(&outputs, &"hello").unwrap(); + let hello = eval_state.require_string(&hello).unwrap(); + assert_eq!(hello, "Claire"); + + // Can't delete overrides, so trash it + let mut flake_lock_flags = FlakeLockFlags::new(&flake_settings).unwrap(); + + // Step 7: Override was not written; lock still points to b + + flake_lock_flags.set_mode_check().unwrap(); + + let locked_flake = LockedFlake::lock( + &FetchersSettings::new().unwrap(), + &flake_settings, + &eval_state, + &flake_lock_flags, + &flake_ref_a, + ) + .unwrap(); + + let outputs = locked_flake + .outputs(&flake_settings, &mut eval_state) + .unwrap(); + let hello = eval_state.require_attrs_select(&outputs, &"hello").unwrap(); + let hello = eval_state.require_string(&hello).unwrap(); + assert_eq!(hello, "BOB"); + + drop(tmp_dir); + drop(gc_registration); + } } From 0514ad3433a69e94792ba7b3fb1e3327172aa9a6 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 9 Apr 2025 15:10:49 +0200 Subject: [PATCH 156/306] doc: Basic doc comments for nix-flake (cherry picked from commit c8f7d9ba5860114c4d602d089c26c05283d312a6) --- rust/nix-flake/src/lib.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/rust/nix-flake/src/lib.rs b/rust/nix-flake/src/lib.rs index 77ccca7..b7ea7ba 100644 --- a/rust/nix-flake/src/lib.rs +++ b/rust/nix-flake/src/lib.rs @@ -10,6 +10,7 @@ use nix_util::{ string_return::{callback_get_result_string, callback_get_result_string_data}, }; +/// Store settings for the flakes feature. pub struct FlakeSettings { pub(crate) ptr: *mut raw::flake_settings, } @@ -43,9 +44,11 @@ impl FlakeSettings { } pub trait EvalStateBuilderExt { + /// Configures the eval state to provide flakes features such as `builtins.getFlake`. fn flakes(self, settings: &FlakeSettings) -> Result; } impl EvalStateBuilderExt for nix_expr::eval_state::EvalStateBuilder { + /// Configures the eval state to provide flakes features such as `builtins.getFlake`. fn flakes( mut self, settings: &FlakeSettings, @@ -55,6 +58,7 @@ impl EvalStateBuilderExt for nix_expr::eval_state::EvalStateBuilder { } } +/// Parameters for parsing a flake reference. pub struct FlakeReferenceParseFlags { pub(crate) ptr: NonNull, } @@ -75,6 +79,8 @@ impl FlakeReferenceParseFlags { .context("flake_reference_parse_flags_new unexpectedly returned null")?; Ok(FlakeReferenceParseFlags { ptr }) } + /// Sets the [base directory](https://nix.dev/manual/nix/latest/glossary#gloss-base-directory) + /// for resolving local flake references. pub fn set_base_directory(&mut self, base_directory: &str) -> Result<()> { let mut ctx = Context::new(); unsafe { @@ -100,6 +106,10 @@ impl Drop for FlakeReference { } } impl FlakeReference { + /// Parse a flake reference from a string. + /// The string must be a valid flake reference, such as `github:owner/repo`. + /// It may also be suffixed with a `#` and a fragment, such as `github:owner/repo#something`, + /// in which case, the returned string will contain the fragment. pub fn parse_with_fragment( fetch_settings: &FetchersSettings, flake_settings: &FlakeSettings, @@ -129,6 +139,7 @@ impl FlakeReference { } } +/// Parameters that affect the locking of a flake. pub struct FlakeLockFlags { pub(crate) ptr: *mut raw::flake_lock_flags, } @@ -145,6 +156,7 @@ impl FlakeLockFlags { let s = unsafe { context::check_call!(raw::flake_lock_flags_new(&mut ctx, settings.ptr)) }?; Ok(FlakeLockFlags { ptr: s }) } + /// Configures [LockedFlake::lock] to make incremental changes to the lock file as needed. Changes are written to file. pub fn set_mode_write_as_needed(&mut self) -> Result<()> { let mut ctx = Context::new(); unsafe { @@ -154,11 +166,13 @@ impl FlakeLockFlags { }?; Ok(()) } + /// Make [LockedFlake::lock] check if the lock file is up to date. If not, an error is returned. pub fn set_mode_check(&mut self) -> Result<()> { let mut ctx = Context::new(); unsafe { context::check_call!(raw::flake_lock_flags_set_mode_check(&mut ctx, self.ptr)) }?; Ok(()) } + /// Like `set_mode_write_as_needed`, but does not write to the lock file. pub fn set_mode_virtual(&mut self) -> Result<()> { let mut ctx = Context::new(); unsafe { @@ -166,6 +180,7 @@ impl FlakeLockFlags { }?; Ok(()) } + /// Adds an input override to the lock file that will be produced. The [LockedFlake::lock] operation will not write to the lock file. pub fn add_input_override( &mut self, override_path: &str, @@ -219,6 +234,7 @@ impl LockedFlake { Ok(LockedFlake { ptr }) } + /// Returns the outputs of the flake - the result of calling the `outputs` attribute. pub fn outputs( &self, flake_settings: &FlakeSettings, From d2d92e38983d5d7c1ecede9933f41105e26e81e0 Mon Sep 17 00:00:00 2001 From: Hercules CI Effects Date: Wed, 16 Apr 2025 10:05:10 +0000 Subject: [PATCH 157/306] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nix': 'github:NixOS/nix/e76bbe413e86e3208bb9824e339d59af25327101?narHash=sha256-Aqnj5%2BsA7B4ZRympuyfWPPK83iomKHEHMYhlwslI8iA%3D' (2025-04-08) → 'github:NixOS/nix/89ba6dff66d34337e3d16866578be5093bb9beda?narHash=sha256-Ty836z5jy53A8loT7XIrfg2I//sgj0G/Ev7yBdwCZLI%3D' (2025-04-15) • Updated input 'nix-cargo-integration': 'github:yusdacra/nix-cargo-integration/be686c02a3fbad61448abcc2049a9178a4cd903c?narHash=sha256-G4IRYK7vG2kA9Xr1KwXJH1SVc57qNPlQXBl6d/V6LSc%3D' (2025-04-01) → 'github:yusdacra/nix-cargo-integration/2b6ba726da45d80205a5d636d5abb137be701ed8?narHash=sha256-DgvMekzi/%2BQCVwKyvLdxabgAyxpi%2BiANADyoR9yTpIk%3D' (2025-04-16) • Updated input 'nix-cargo-integration/parts': 'github:hercules-ci/flake-parts/f4330d22f1c5d2ba72d3d22df5597d123fdb60a9?narHash=sha256-%2Bu2UunDA4Cl5Fci3m7S643HzKmIDAe%2BfiXrLqYsR2fs%3D' (2025-03-07) → 'github:hercules-ci/flake-parts/c621e8422220273271f52058f618c94e405bb0f5?narHash=sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY%3D' (2025-04-01) • Updated input 'nix-cargo-integration/rust-overlay': 'github:oxalica/rust-overlay/bee11c51c2cda3ac57c9e0149d94b86cc1b00d13?narHash=sha256-uLjVsb4Rxnp1zmFdPCDmdODd4RY6ETOeRj0IkC0ij/4%3D' (2025-04-01) → 'github:oxalica/rust-overlay/1633514603fc0ed15ea0aef7327e26736ec003c0?narHash=sha256-RMyTyFHN3w8zwfpgvcfRHQ4vX4zTqhuZbif/MXROtx8%3D' (2025-04-16) • Updated input 'nix-cargo-integration/treefmt': 'github:numtide/treefmt-nix/29a3d7b768c70addce17af0869f6e2bd8f5be4b7?narHash=sha256-WRAylyYptt6OX5eCEBWyTwOEqEtD6zt33rlUkr6u3cE%3D' (2025-03-27) → 'github:numtide/treefmt-nix/49d05555ccdd2592300099d6a657cc33571f4fe0?narHash=sha256-IPFcShGro/UUp8BmwMBkq%2B6KscPlWQevZi9qqIwVUWg%3D' (2025-04-15) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/52faf482a3889b7619003c0daec593a1912fddc1?narHash=sha256-6hl6L/tRnwubHcA4pfUUtk542wn2Om%2BD4UnDhlDW9BE%3D' (2025-03-30) → 'github:NixOS/nixpkgs/2631b0b7abcea6e640ce31cd78ea58910d31e650?narHash=sha256-LWqduOgLHCFxiTNYi3Uj5Lgz0SR%2BXhw3kr/3Xd0GPTM%3D' (2025-04-12) (cherry picked from commit af25ad333c46a8409bf4872bf174fed6993c649e) --- flake.lock | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/flake.lock b/flake.lock index 15da0d9..29d49f7 100644 --- a/flake.lock +++ b/flake.lock @@ -170,11 +170,11 @@ "nixpkgs-regression": "nixpkgs-regression" }, "locked": { - "lastModified": 1744095711, - "narHash": "sha256-Aqnj5+sA7B4ZRympuyfWPPK83iomKHEHMYhlwslI8iA=", + "lastModified": 1744736915, + "narHash": "sha256-Ty836z5jy53A8loT7XIrfg2I//sgj0G/Ev7yBdwCZLI=", "owner": "NixOS", "repo": "nix", - "rev": "e76bbe413e86e3208bb9824e339d59af25327101", + "rev": "89ba6dff66d34337e3d16866578be5093bb9beda", "type": "github" }, "original": { @@ -196,11 +196,11 @@ "treefmt": "treefmt" }, "locked": { - "lastModified": 1743488269, - "narHash": "sha256-G4IRYK7vG2kA9Xr1KwXJH1SVc57qNPlQXBl6d/V6LSc=", + "lastModified": 1744784266, + "narHash": "sha256-DgvMekzi/+QCVwKyvLdxabgAyxpi+iANADyoR9yTpIk=", "owner": "yusdacra", "repo": "nix-cargo-integration", - "rev": "be686c02a3fbad61448abcc2049a9178a4cd903c", + "rev": "2b6ba726da45d80205a5d636d5abb137be701ed8", "type": "github" }, "original": { @@ -211,11 +211,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1743315132, - "narHash": "sha256-6hl6L/tRnwubHcA4pfUUtk542wn2Om+D4UnDhlDW9BE=", + "lastModified": 1744463964, + "narHash": "sha256-LWqduOgLHCFxiTNYi3Uj5Lgz0SR+Xhw3kr/3Xd0GPTM=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "52faf482a3889b7619003c0daec593a1912fddc1", + "rev": "2631b0b7abcea6e640ce31cd78ea58910d31e650", "type": "github" }, "original": { @@ -280,11 +280,11 @@ ] }, "locked": { - "lastModified": 1741352980, - "narHash": "sha256-+u2UunDA4Cl5Fci3m7S643HzKmIDAe+fiXrLqYsR2fs=", + "lastModified": 1743550720, + "narHash": "sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "f4330d22f1c5d2ba72d3d22df5597d123fdb60a9", + "rev": "c621e8422220273271f52058f618c94e405bb0f5", "type": "github" }, "original": { @@ -350,11 +350,11 @@ ] }, "locked": { - "lastModified": 1743475035, - "narHash": "sha256-uLjVsb4Rxnp1zmFdPCDmdODd4RY6ETOeRj0IkC0ij/4=", + "lastModified": 1744770893, + "narHash": "sha256-RMyTyFHN3w8zwfpgvcfRHQ4vX4zTqhuZbif/MXROtx8=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "bee11c51c2cda3ac57c9e0149d94b86cc1b00d13", + "rev": "1633514603fc0ed15ea0aef7327e26736ec003c0", "type": "github" }, "original": { @@ -394,11 +394,11 @@ ] }, "locked": { - "lastModified": 1743081648, - "narHash": "sha256-WRAylyYptt6OX5eCEBWyTwOEqEtD6zt33rlUkr6u3cE=", + "lastModified": 1744707583, + "narHash": "sha256-IPFcShGro/UUp8BmwMBkq+6KscPlWQevZi9qqIwVUWg=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "29a3d7b768c70addce17af0869f6e2bd8f5be4b7", + "rev": "49d05555ccdd2592300099d6a657cc33571f4fe0", "type": "github" }, "original": { From 334251f2668a73ddf110bb943f2d56a6a2e3edc4 Mon Sep 17 00:00:00 2001 From: Hercules CI Effects Date: Wed, 16 Apr 2025 10:05:13 +0000 Subject: [PATCH 158/306] dev/flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'hercules-ci-effects': 'github:hercules-ci/hercules-ci-effects/524637ef84c177661690b924bf64a1ce18072a2c?narHash=sha256-I6fG1zrfdLFcp/imGZElig0BJO3YU0QEXLgvwWoOpJ8%3D' (2025-03-15) → 'github:hercules-ci/hercules-ci-effects/5b6cec51c9ec095a0d3fd4c8eeb53eb5c59ae33e?narHash=sha256-1Z4WPGVky4w3lrhrgs89OKsLzPdtkbi1bPLNFWsoLfY%3D' (2025-04-15) • Updated input 'hercules-ci-effects/flake-parts': 'github:hercules-ci/flake-parts/f4330d22f1c5d2ba72d3d22df5597d123fdb60a9?narHash=sha256-%2Bu2UunDA4Cl5Fci3m7S643HzKmIDAe%2BfiXrLqYsR2fs%3D' (2025-03-07) → 'github:hercules-ci/flake-parts/c621e8422220273271f52058f618c94e405bb0f5?narHash=sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY%3D' (2025-04-01) • Updated input 'hercules-ci-effects/nixpkgs': 'github:NixOS/nixpkgs/6607cf789e541e7873d40d3a8f7815ea92204f32?narHash=sha256-cPfs8qMccim2RBgtKGF%2Bx9IBCduRvd/N5F4nYpU0TVE%3D' (2025-03-13) → 'github:NixOS/nixpkgs/2631b0b7abcea6e640ce31cd78ea58910d31e650?narHash=sha256-LWqduOgLHCFxiTNYi3Uj5Lgz0SR%2BXhw3kr/3Xd0GPTM%3D' (2025-04-12) (cherry picked from commit c4820abd671ed207bf785bdcfbcbb1f7c016dde1) --- dev/flake.lock | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/dev/flake.lock b/dev/flake.lock index d977c06..ea2fd23 100644 --- a/dev/flake.lock +++ b/dev/flake.lock @@ -24,11 +24,11 @@ ] }, "locked": { - "lastModified": 1738453229, - "narHash": "sha256-7H9XgNiGLKN1G1CgRh0vUL4AheZSYzPm+zmZ7vxbJdo=", + "lastModified": 1743550720, + "narHash": "sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "32ea77a06711b758da0ad9bd6a844c5740a87abd", + "rev": "c621e8422220273271f52058f618c94e405bb0f5", "type": "github" }, "original": { @@ -84,11 +84,11 @@ "nixpkgs": "nixpkgs" }, "locked": { - "lastModified": 1739595404, - "narHash": "sha256-0CjCfbq0yHWexOrpO06e2WU1r5JAqR6ffy1zgM3NksI=", + "lastModified": 1744693102, + "narHash": "sha256-1Z4WPGVky4w3lrhrgs89OKsLzPdtkbi1bPLNFWsoLfY=", "owner": "hercules-ci", "repo": "hercules-ci-effects", - "rev": "06519cec8fb32d219006da6eacd255504a9996af", + "rev": "5b6cec51c9ec095a0d3fd4c8eeb53eb5c59ae33e", "type": "github" }, "original": { @@ -126,11 +126,11 @@ "treefmt-nix": "treefmt-nix" }, "locked": { - "lastModified": 1733705700, - "narHash": "sha256-stdx1z86yj66bChYswqjRw8BpdJRwxkH2XIJrsygpiM=", + "lastModified": 1741624954, + "narHash": "sha256-VjLS010BEfwuK343Dst08NnQNS8SRtVCDkz1zTsHuvI=", "owner": "nix-community", "repo": "nix-unit", - "rev": "d867d72d21da3b7d83f0feef73b0ac7f72b16437", + "rev": "e9d81f6cffe67681e7c04a967d29f18c2c540af5", "type": "github" }, "original": { @@ -141,11 +141,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1739446958, - "narHash": "sha256-+/bYK3DbPxMIvSL4zArkMX0LQvS7rzBKXnDXLfKyRVc=", + "lastModified": 1744463964, + "narHash": "sha256-LWqduOgLHCFxiTNYi3Uj5Lgz0SR+Xhw3kr/3Xd0GPTM=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "2ff53fe64443980e139eaa286017f53f88336dd0", + "rev": "2631b0b7abcea6e640ce31cd78ea58910d31e650", "type": "github" }, "original": { @@ -178,11 +178,11 @@ "nixpkgs": [] }, "locked": { - "lastModified": 1740870877, - "narHash": "sha256-LWDIJvKWMW0tiih1jTcAK0ncTi3S9IF3gOhpCT1ydik=", + "lastModified": 1742649964, + "narHash": "sha256-DwOTp7nvfi8mRfuL1escHDXabVXFGT1VlPD1JHrtrco=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "25d4946dfc2021584f5bde1fbd2aa97353384a95", + "rev": "dcf5072734cb576d2b0c59b2ac44f5050b5eac82", "type": "github" }, "original": { @@ -194,6 +194,7 @@ "root": { "inputs": { "hercules-ci-effects": "hercules-ci-effects", + "nix-unit": "nix-unit", "pre-commit-hooks-nix": "pre-commit-hooks-nix" } }, From d97e4d7fffe17fc7a3d8b423f47d527fc308f135 Mon Sep 17 00:00:00 2001 From: Hercules CI Effects Date: Sat, 26 Apr 2025 16:48:49 +0000 Subject: [PATCH 159/306] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nix': 'github:NixOS/nix/89ba6dff66d34337e3d16866578be5093bb9beda?narHash=sha256-Ty836z5jy53A8loT7XIrfg2I//sgj0G/Ev7yBdwCZLI%3D' (2025-04-15) → 'github:NixOS/nix/ee59af99f8619e17db4289843da62a24302d20b7?narHash=sha256-yzyW8CNxPjys%2BeqbnyxICi3W5zESTpWehBAmMKDU6Ws%3D' (2025-04-26) • Updated input 'nix-cargo-integration': 'github:yusdacra/nix-cargo-integration/2b6ba726da45d80205a5d636d5abb137be701ed8?narHash=sha256-DgvMekzi/%2BQCVwKyvLdxabgAyxpi%2BiANADyoR9yTpIk%3D' (2025-04-16) → 'github:yusdacra/nix-cargo-integration/3651a63811d1301e49b581cc0534f78319fcd0ca?narHash=sha256-3K7u0SEmJTOzp1kKAngUIp9hgwafJCyjntFeXJQu6Pw%3D' (2025-04-26) • Updated input 'nix-cargo-integration/rust-overlay': 'github:oxalica/rust-overlay/1633514603fc0ed15ea0aef7327e26736ec003c0?narHash=sha256-RMyTyFHN3w8zwfpgvcfRHQ4vX4zTqhuZbif/MXROtx8%3D' (2025-04-16) → 'github:oxalica/rust-overlay/f1aeaeb91ba9c88f235ab82bd23d7a4931fe736c?narHash=sha256-8AuOyfLNlcbLy0AqERSNUUoDdY%2B3THZI7%2B9VrXUfGqg%3D' (2025-04-26) • Updated input 'nix-cargo-integration/treefmt': 'github:numtide/treefmt-nix/49d05555ccdd2592300099d6a657cc33571f4fe0?narHash=sha256-IPFcShGro/UUp8BmwMBkq%2B6KscPlWQevZi9qqIwVUWg%3D' (2025-04-15) → 'github:numtide/treefmt-nix/8d404a69efe76146368885110f29a2ca3700bee6?narHash=sha256-aRmUh0AMwcbdjJHnytg1e5h5ECcaWtIFQa6d9gI85AI%3D' (2025-04-18) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/2631b0b7abcea6e640ce31cd78ea58910d31e650?narHash=sha256-LWqduOgLHCFxiTNYi3Uj5Lgz0SR%2BXhw3kr/3Xd0GPTM%3D' (2025-04-12) → 'github:NixOS/nixpkgs/f771eb401a46846c1aebd20552521b233dd7e18b?narHash=sha256-ITSpPDwvLBZBnPRS2bUcHY3gZSwis/uTe255QgMtTLA%3D' (2025-04-24) (cherry picked from commit 2093f208d79bfa4510b12993b1a6cfa374b5a1b0) --- flake.lock | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/flake.lock b/flake.lock index 29d49f7..4896b63 100644 --- a/flake.lock +++ b/flake.lock @@ -170,11 +170,11 @@ "nixpkgs-regression": "nixpkgs-regression" }, "locked": { - "lastModified": 1744736915, - "narHash": "sha256-Ty836z5jy53A8loT7XIrfg2I//sgj0G/Ev7yBdwCZLI=", + "lastModified": 1745685971, + "narHash": "sha256-yzyW8CNxPjys+eqbnyxICi3W5zESTpWehBAmMKDU6Ws=", "owner": "NixOS", "repo": "nix", - "rev": "89ba6dff66d34337e3d16866578be5093bb9beda", + "rev": "ee59af99f8619e17db4289843da62a24302d20b7", "type": "github" }, "original": { @@ -196,11 +196,11 @@ "treefmt": "treefmt" }, "locked": { - "lastModified": 1744784266, - "narHash": "sha256-DgvMekzi/+QCVwKyvLdxabgAyxpi+iANADyoR9yTpIk=", + "lastModified": 1745648139, + "narHash": "sha256-3K7u0SEmJTOzp1kKAngUIp9hgwafJCyjntFeXJQu6Pw=", "owner": "yusdacra", "repo": "nix-cargo-integration", - "rev": "2b6ba726da45d80205a5d636d5abb137be701ed8", + "rev": "3651a63811d1301e49b581cc0534f78319fcd0ca", "type": "github" }, "original": { @@ -211,11 +211,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1744463964, - "narHash": "sha256-LWqduOgLHCFxiTNYi3Uj5Lgz0SR+Xhw3kr/3Xd0GPTM=", + "lastModified": 1745526057, + "narHash": "sha256-ITSpPDwvLBZBnPRS2bUcHY3gZSwis/uTe255QgMtTLA=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "2631b0b7abcea6e640ce31cd78ea58910d31e650", + "rev": "f771eb401a46846c1aebd20552521b233dd7e18b", "type": "github" }, "original": { @@ -350,11 +350,11 @@ ] }, "locked": { - "lastModified": 1744770893, - "narHash": "sha256-RMyTyFHN3w8zwfpgvcfRHQ4vX4zTqhuZbif/MXROtx8=", + "lastModified": 1745634793, + "narHash": "sha256-8AuOyfLNlcbLy0AqERSNUUoDdY+3THZI7+9VrXUfGqg=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "1633514603fc0ed15ea0aef7327e26736ec003c0", + "rev": "f1aeaeb91ba9c88f235ab82bd23d7a4931fe736c", "type": "github" }, "original": { @@ -394,11 +394,11 @@ ] }, "locked": { - "lastModified": 1744707583, - "narHash": "sha256-IPFcShGro/UUp8BmwMBkq+6KscPlWQevZi9qqIwVUWg=", + "lastModified": 1744961264, + "narHash": "sha256-aRmUh0AMwcbdjJHnytg1e5h5ECcaWtIFQa6d9gI85AI=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "49d05555ccdd2592300099d6a657cc33571f4fe0", + "rev": "8d404a69efe76146368885110f29a2ca3700bee6", "type": "github" }, "original": { From 23387adc642bc5bfc4d38a069929dcb055c303b3 Mon Sep 17 00:00:00 2001 From: Hercules CI Effects Date: Sat, 26 Apr 2025 16:48:52 +0000 Subject: [PATCH 160/306] dev/flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nix-unit': 'github:nix-community/nix-unit/e9d81f6cffe67681e7c04a967d29f18c2c540af5?narHash=sha256-VjLS010BEfwuK343Dst08NnQNS8SRtVCDkz1zTsHuvI%3D' (2025-03-10) → 'github:nix-community/nix-unit/be0d299e89a31e246c5472bf0e1005d4cc1e9e55?narHash=sha256-FV8uIBumYYmqOMEa6WR3lFxs0ocANT7bbawEDg%2BvWjo%3D' (2025-04-24) (cherry picked from commit 66f39526de476fb95f1b3d96c631be739ada1e3d) --- dev/flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dev/flake.lock b/dev/flake.lock index ea2fd23..fad577d 100644 --- a/dev/flake.lock +++ b/dev/flake.lock @@ -126,11 +126,11 @@ "treefmt-nix": "treefmt-nix" }, "locked": { - "lastModified": 1741624954, - "narHash": "sha256-VjLS010BEfwuK343Dst08NnQNS8SRtVCDkz1zTsHuvI=", + "lastModified": 1745514172, + "narHash": "sha256-FV8uIBumYYmqOMEa6WR3lFxs0ocANT7bbawEDg+vWjo=", "owner": "nix-community", "repo": "nix-unit", - "rev": "e9d81f6cffe67681e7c04a967d29f18c2c540af5", + "rev": "be0d299e89a31e246c5472bf0e1005d4cc1e9e55", "type": "github" }, "original": { From 1f9c8f4eb5e031745ae087658540bec3bbf9f6d4 Mon Sep 17 00:00:00 2001 From: Hercules CI Effects Date: Fri, 2 May 2025 00:58:02 +0000 Subject: [PATCH 161/306] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nix': 'github:NixOS/nix/ee59af99f8619e17db4289843da62a24302d20b7?narHash=sha256-yzyW8CNxPjys%2BeqbnyxICi3W5zESTpWehBAmMKDU6Ws%3D' (2025-04-26) → 'github:NixOS/nix/9fe3077d4728892e9167d9f7bfdc3f4d01a52c16?narHash=sha256-y81HHYkGse27WcgAoUKttfFzKg73pS2JFV32WNK5wJ0%3D' (2025-05-01) • Updated input 'nix-cargo-integration': 'github:yusdacra/nix-cargo-integration/3651a63811d1301e49b581cc0534f78319fcd0ca?narHash=sha256-3K7u0SEmJTOzp1kKAngUIp9hgwafJCyjntFeXJQu6Pw%3D' (2025-04-26) → 'github:yusdacra/nix-cargo-integration/966051761a99348f3b30bc0dcdf7215eb73660bc?narHash=sha256-jFX6CoIlURF%2BcT/9WwRsnOyK1vyCLN6KLCcppMMx0L0%3D' (2025-05-01) • Updated input 'nix-cargo-integration/rust-overlay': 'github:oxalica/rust-overlay/f1aeaeb91ba9c88f235ab82bd23d7a4931fe736c?narHash=sha256-8AuOyfLNlcbLy0AqERSNUUoDdY%2B3THZI7%2B9VrXUfGqg%3D' (2025-04-26) → 'github:oxalica/rust-overlay/026e8fedefd6b167d92ed04b195c658d95ffc7a5?narHash=sha256-6JeEbboDvRjLwB9kzCnmWj%2Bf%2BZnMtKOe5c2F1VBpaTs%3D' (2025-05-01) • Updated input 'nix-cargo-integration/treefmt': 'github:numtide/treefmt-nix/8d404a69efe76146368885110f29a2ca3700bee6?narHash=sha256-aRmUh0AMwcbdjJHnytg1e5h5ECcaWtIFQa6d9gI85AI%3D' (2025-04-18) → 'github:numtide/treefmt-nix/82bf32e541b30080d94e46af13d46da0708609ea?narHash=sha256-k5ELLpTwRP/OElcLpNaFWLNf8GRDq4/eHBmFy06gGko%3D' (2025-04-29) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/f771eb401a46846c1aebd20552521b233dd7e18b?narHash=sha256-ITSpPDwvLBZBnPRS2bUcHY3gZSwis/uTe255QgMtTLA%3D' (2025-04-24) → 'github:NixOS/nixpkgs/46e634be05ce9dc6d4db8e664515ba10b78151ae?narHash=sha256-y3h3NLnzRSiUkYpnfvnS669zWZLoqqI6NprtLQ%2B5dck%3D' (2025-04-29) (cherry picked from commit 989d253311ec860391329e0da2f1c5eed71243b5) --- flake.lock | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/flake.lock b/flake.lock index 4896b63..6c15578 100644 --- a/flake.lock +++ b/flake.lock @@ -170,11 +170,11 @@ "nixpkgs-regression": "nixpkgs-regression" }, "locked": { - "lastModified": 1745685971, - "narHash": "sha256-yzyW8CNxPjys+eqbnyxICi3W5zESTpWehBAmMKDU6Ws=", + "lastModified": 1746105072, + "narHash": "sha256-y81HHYkGse27WcgAoUKttfFzKg73pS2JFV32WNK5wJ0=", "owner": "NixOS", "repo": "nix", - "rev": "ee59af99f8619e17db4289843da62a24302d20b7", + "rev": "9fe3077d4728892e9167d9f7bfdc3f4d01a52c16", "type": "github" }, "original": { @@ -196,11 +196,11 @@ "treefmt": "treefmt" }, "locked": { - "lastModified": 1745648139, - "narHash": "sha256-3K7u0SEmJTOzp1kKAngUIp9hgwafJCyjntFeXJQu6Pw=", + "lastModified": 1746080308, + "narHash": "sha256-jFX6CoIlURF+cT/9WwRsnOyK1vyCLN6KLCcppMMx0L0=", "owner": "yusdacra", "repo": "nix-cargo-integration", - "rev": "3651a63811d1301e49b581cc0534f78319fcd0ca", + "rev": "966051761a99348f3b30bc0dcdf7215eb73660bc", "type": "github" }, "original": { @@ -211,11 +211,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1745526057, - "narHash": "sha256-ITSpPDwvLBZBnPRS2bUcHY3gZSwis/uTe255QgMtTLA=", + "lastModified": 1745930157, + "narHash": "sha256-y3h3NLnzRSiUkYpnfvnS669zWZLoqqI6NprtLQ+5dck=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "f771eb401a46846c1aebd20552521b233dd7e18b", + "rev": "46e634be05ce9dc6d4db8e664515ba10b78151ae", "type": "github" }, "original": { @@ -350,11 +350,11 @@ ] }, "locked": { - "lastModified": 1745634793, - "narHash": "sha256-8AuOyfLNlcbLy0AqERSNUUoDdY+3THZI7+9VrXUfGqg=", + "lastModified": 1746067100, + "narHash": "sha256-6JeEbboDvRjLwB9kzCnmWj+f+ZnMtKOe5c2F1VBpaTs=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "f1aeaeb91ba9c88f235ab82bd23d7a4931fe736c", + "rev": "026e8fedefd6b167d92ed04b195c658d95ffc7a5", "type": "github" }, "original": { @@ -394,11 +394,11 @@ ] }, "locked": { - "lastModified": 1744961264, - "narHash": "sha256-aRmUh0AMwcbdjJHnytg1e5h5ECcaWtIFQa6d9gI85AI=", + "lastModified": 1745929750, + "narHash": "sha256-k5ELLpTwRP/OElcLpNaFWLNf8GRDq4/eHBmFy06gGko=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "8d404a69efe76146368885110f29a2ca3700bee6", + "rev": "82bf32e541b30080d94e46af13d46da0708609ea", "type": "github" }, "original": { From 839c33b1431698b4d24589a098a8920b7ea4b951 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 12 May 2025 16:54:16 +0200 Subject: [PATCH 162/306] maint: Fix ctor warnings (cherry picked from commit 37540fb348b0e4ffe69759afb1d604666f2292f5) --- rust/Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 5368463..8a22246 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -156,9 +156,9 @@ dependencies = [ [[package]] name = "ctor" -version = "0.2.7" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad291aa74992b9b7a7e88c38acbbf6ad7e107f1d90ee8775b7bc1fc3394f485c" +checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" dependencies = [ "quote", "syn", From 2e36084c2ec698f87847fe6e9f9136b2d59d2c57 Mon Sep 17 00:00:00 2001 From: Hercules CI Effects Date: Mon, 2 Jun 2025 00:58:41 +0000 Subject: [PATCH 163/306] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'flake-parts': 'github:hercules-ci/flake-parts/c621e8422220273271f52058f618c94e405bb0f5?narHash=sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY%3D' (2025-04-01) → 'github:hercules-ci/flake-parts/49f0870db23e8c1ca0b5259734a02cd9e1e371a1?narHash=sha256-F82%2BgS044J1APL0n4hH50GYdPRv/5JWm34oCJYmVKdE%3D' (2025-06-01) • Updated input 'flake-parts/nixpkgs-lib': 'github:nix-community/nixpkgs.lib/e4822aea2a6d1cdd36653c134cacfd64c97ff4fa?narHash=sha256-b1EdN3cULCqtorQ4QeWgLMrd5ZGOjLSLemfa00heasc%3D' (2025-03-30) → 'github:nix-community/nixpkgs.lib/656a64127e9d791a334452c6b6606d17539476e2?narHash=sha256-rQaysilft1aVMwF14xIdGS3sj1yHlI6oKQNBRTF40cc%3D' (2025-06-01) • Updated input 'nix': 'github:NixOS/nix/9fe3077d4728892e9167d9f7bfdc3f4d01a52c16?narHash=sha256-y81HHYkGse27WcgAoUKttfFzKg73pS2JFV32WNK5wJ0%3D' (2025-05-01) → 'github:NixOS/nix/587b5f53618cf72e5ee725e2237227fbcc7b0f34?narHash=sha256-qMLM19XLxjUFKbPO12f47guvioAHXkxlhGyH8Kh9jMk%3D' (2025-05-30) • Updated input 'nix-cargo-integration': 'github:yusdacra/nix-cargo-integration/966051761a99348f3b30bc0dcdf7215eb73660bc?narHash=sha256-jFX6CoIlURF%2BcT/9WwRsnOyK1vyCLN6KLCcppMMx0L0%3D' (2025-05-01) → 'github:yusdacra/nix-cargo-integration/e1b777f69b0f5d6bbbf70c72fd7d7037df3aa9a4?narHash=sha256-K3Ytc2ELSCt3x3wMVr%2B%2BVHBa3FY1xA9k5IS7aks5zDM%3D' (2025-06-01) • Updated input 'nix-cargo-integration/dream2nix': 'github:nix-community/dream2nix/8ce6284ff58208ed8961681276f82c2f8f978ef4?narHash=sha256-n5CwhmqKxifuD4Sq4WuRP/h5LO6f23cGnSAuJemnd/4%3D' (2024-12-25) → 'github:nix-community/dream2nix/852a02488001f73526b94f880f4fb6dd8656ed4e?narHash=sha256-987t1pyspFbJ8TFf1aXR3/vdsJX09r7C3vW8Ojsy%2B14%3D' (2025-06-01) • Updated input 'nix-cargo-integration/rust-overlay': 'github:oxalica/rust-overlay/026e8fedefd6b167d92ed04b195c658d95ffc7a5?narHash=sha256-6JeEbboDvRjLwB9kzCnmWj%2Bf%2BZnMtKOe5c2F1VBpaTs%3D' (2025-05-01) → 'github:oxalica/rust-overlay/12a0d94a2f2b06714f747ab97b2fa546f46b460c?narHash=sha256-bwkCAK9pOyI2Ww4Q4oO1Ynv7O9aZPrsIAMMASmhVGp4%3D' (2025-06-01) • Updated input 'nix-cargo-integration/treefmt': 'github:numtide/treefmt-nix/82bf32e541b30080d94e46af13d46da0708609ea?narHash=sha256-k5ELLpTwRP/OElcLpNaFWLNf8GRDq4/eHBmFy06gGko%3D' (2025-04-29) → 'github:numtide/treefmt-nix/1f3f7b784643d488ba4bf315638b2b0a4c5fb007?narHash=sha256-9YzfeN8CB6SzNPyPm2XjRRqSixDopTapaRsnTpXUEY8%3D' (2025-05-26) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/46e634be05ce9dc6d4db8e664515ba10b78151ae?narHash=sha256-y3h3NLnzRSiUkYpnfvnS669zWZLoqqI6NprtLQ%2B5dck%3D' (2025-04-29) → 'github:NixOS/nixpkgs/910796cabe436259a29a72e8d3f5e180fc6dfacc?narHash=sha256-StSrWhklmDuXT93yc3GrTlb0cKSS0agTAxMGjLKAsY8%3D' (2025-05-31) (cherry picked from commit e5e26736e310fe8325c6064b4d7b4618ab4d28c9) --- flake.lock | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/flake.lock b/flake.lock index 6c15578..1ee9690 100644 --- a/flake.lock +++ b/flake.lock @@ -27,11 +27,11 @@ "pyproject-nix": "pyproject-nix" }, "locked": { - "lastModified": 1735160684, - "narHash": "sha256-n5CwhmqKxifuD4Sq4WuRP/h5LO6f23cGnSAuJemnd/4=", + "lastModified": 1748754808, + "narHash": "sha256-987t1pyspFbJ8TFf1aXR3/vdsJX09r7C3vW8Ojsy+14=", "owner": "nix-community", "repo": "dream2nix", - "rev": "8ce6284ff58208ed8961681276f82c2f8f978ef4", + "rev": "852a02488001f73526b94f880f4fb6dd8656ed4e", "type": "github" }, "original": { @@ -77,11 +77,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1743550720, - "narHash": "sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY=", + "lastModified": 1748821116, + "narHash": "sha256-F82+gS044J1APL0n4hH50GYdPRv/5JWm34oCJYmVKdE=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "c621e8422220273271f52058f618c94e405bb0f5", + "rev": "49f0870db23e8c1ca0b5259734a02cd9e1e371a1", "type": "github" }, "original": { @@ -170,11 +170,11 @@ "nixpkgs-regression": "nixpkgs-regression" }, "locked": { - "lastModified": 1746105072, - "narHash": "sha256-y81HHYkGse27WcgAoUKttfFzKg73pS2JFV32WNK5wJ0=", + "lastModified": 1748601301, + "narHash": "sha256-qMLM19XLxjUFKbPO12f47guvioAHXkxlhGyH8Kh9jMk=", "owner": "NixOS", "repo": "nix", - "rev": "9fe3077d4728892e9167d9f7bfdc3f4d01a52c16", + "rev": "587b5f53618cf72e5ee725e2237227fbcc7b0f34", "type": "github" }, "original": { @@ -196,11 +196,11 @@ "treefmt": "treefmt" }, "locked": { - "lastModified": 1746080308, - "narHash": "sha256-jFX6CoIlURF+cT/9WwRsnOyK1vyCLN6KLCcppMMx0L0=", + "lastModified": 1748758650, + "narHash": "sha256-K3Ytc2ELSCt3x3wMVr++VHBa3FY1xA9k5IS7aks5zDM=", "owner": "yusdacra", "repo": "nix-cargo-integration", - "rev": "966051761a99348f3b30bc0dcdf7215eb73660bc", + "rev": "e1b777f69b0f5d6bbbf70c72fd7d7037df3aa9a4", "type": "github" }, "original": { @@ -211,11 +211,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1745930157, - "narHash": "sha256-y3h3NLnzRSiUkYpnfvnS669zWZLoqqI6NprtLQ+5dck=", + "lastModified": 1748693115, + "narHash": "sha256-StSrWhklmDuXT93yc3GrTlb0cKSS0agTAxMGjLKAsY8=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "46e634be05ce9dc6d4db8e664515ba10b78151ae", + "rev": "910796cabe436259a29a72e8d3f5e180fc6dfacc", "type": "github" }, "original": { @@ -243,11 +243,11 @@ }, "nixpkgs-lib": { "locked": { - "lastModified": 1743296961, - "narHash": "sha256-b1EdN3cULCqtorQ4QeWgLMrd5ZGOjLSLemfa00heasc=", + "lastModified": 1748740939, + "narHash": "sha256-rQaysilft1aVMwF14xIdGS3sj1yHlI6oKQNBRTF40cc=", "owner": "nix-community", "repo": "nixpkgs.lib", - "rev": "e4822aea2a6d1cdd36653c134cacfd64c97ff4fa", + "rev": "656a64127e9d791a334452c6b6606d17539476e2", "type": "github" }, "original": { @@ -350,11 +350,11 @@ ] }, "locked": { - "lastModified": 1746067100, - "narHash": "sha256-6JeEbboDvRjLwB9kzCnmWj+f+ZnMtKOe5c2F1VBpaTs=", + "lastModified": 1748746145, + "narHash": "sha256-bwkCAK9pOyI2Ww4Q4oO1Ynv7O9aZPrsIAMMASmhVGp4=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "026e8fedefd6b167d92ed04b195c658d95ffc7a5", + "rev": "12a0d94a2f2b06714f747ab97b2fa546f46b460c", "type": "github" }, "original": { @@ -394,11 +394,11 @@ ] }, "locked": { - "lastModified": 1745929750, - "narHash": "sha256-k5ELLpTwRP/OElcLpNaFWLNf8GRDq4/eHBmFy06gGko=", + "lastModified": 1748243702, + "narHash": "sha256-9YzfeN8CB6SzNPyPm2XjRRqSixDopTapaRsnTpXUEY8=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "82bf32e541b30080d94e46af13d46da0708609ea", + "rev": "1f3f7b784643d488ba4bf315638b2b0a4c5fb007", "type": "github" }, "original": { From ba59ec753da1eaa4b1cb31002dd87a94c009f1bc Mon Sep 17 00:00:00 2001 From: Hercules CI Effects Date: Mon, 2 Jun 2025 00:58:44 +0000 Subject: [PATCH 164/306] dev/flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'hercules-ci-effects': 'github:hercules-ci/hercules-ci-effects/5b6cec51c9ec095a0d3fd4c8eeb53eb5c59ae33e?narHash=sha256-1Z4WPGVky4w3lrhrgs89OKsLzPdtkbi1bPLNFWsoLfY%3D' (2025-04-15) → 'github:hercules-ci/hercules-ci-effects/231726642197817d20310b9d39dd4afb9e899489?narHash=sha256-EaAJhwfJGBncgIV/0NlJviid2DP93cTMc9h0q6P6xXk%3D' (2025-05-23) • Updated input 'hercules-ci-effects/nixpkgs': 'github:NixOS/nixpkgs/2631b0b7abcea6e640ce31cd78ea58910d31e650?narHash=sha256-LWqduOgLHCFxiTNYi3Uj5Lgz0SR%2BXhw3kr/3Xd0GPTM%3D' (2025-04-12) → 'github:NixOS/nixpkgs/adaa24fbf46737f3f1b5497bf64bae750f82942e?narHash=sha256-qhFMmDkeJX9KJwr5H32f1r7Prs7XbQWtO0h3V0a0rFY%3D' (2025-05-13) • Updated input 'pre-commit-hooks-nix': 'github:cachix/pre-commit-hooks.nix/dcf5072734cb576d2b0c59b2ac44f5050b5eac82?narHash=sha256-DwOTp7nvfi8mRfuL1escHDXabVXFGT1VlPD1JHrtrco%3D' (2025-03-22) → 'github:cachix/pre-commit-hooks.nix/80479b6ec16fefd9c1db3ea13aeb038c60530f46?narHash=sha256-2Y53NGIX2vxfie1rOW0Qb86vjRZ7ngizoo%2BbnXU9D9k%3D' (2025-05-16) (cherry picked from commit 1bc74c291d915a60ef7208b6be0bca3fb9f20a5d) --- dev/flake.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/dev/flake.lock b/dev/flake.lock index fad577d..b09048c 100644 --- a/dev/flake.lock +++ b/dev/flake.lock @@ -84,11 +84,11 @@ "nixpkgs": "nixpkgs" }, "locked": { - "lastModified": 1744693102, - "narHash": "sha256-1Z4WPGVky4w3lrhrgs89OKsLzPdtkbi1bPLNFWsoLfY=", + "lastModified": 1748000383, + "narHash": "sha256-EaAJhwfJGBncgIV/0NlJviid2DP93cTMc9h0q6P6xXk=", "owner": "hercules-ci", "repo": "hercules-ci-effects", - "rev": "5b6cec51c9ec095a0d3fd4c8eeb53eb5c59ae33e", + "rev": "231726642197817d20310b9d39dd4afb9e899489", "type": "github" }, "original": { @@ -141,11 +141,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1744463964, - "narHash": "sha256-LWqduOgLHCFxiTNYi3Uj5Lgz0SR+Xhw3kr/3Xd0GPTM=", + "lastModified": 1747179050, + "narHash": "sha256-qhFMmDkeJX9KJwr5H32f1r7Prs7XbQWtO0h3V0a0rFY=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "2631b0b7abcea6e640ce31cd78ea58910d31e650", + "rev": "adaa24fbf46737f3f1b5497bf64bae750f82942e", "type": "github" }, "original": { @@ -178,11 +178,11 @@ "nixpkgs": [] }, "locked": { - "lastModified": 1742649964, - "narHash": "sha256-DwOTp7nvfi8mRfuL1escHDXabVXFGT1VlPD1JHrtrco=", + "lastModified": 1747372754, + "narHash": "sha256-2Y53NGIX2vxfie1rOW0Qb86vjRZ7ngizoo+bnXU9D9k=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "dcf5072734cb576d2b0c59b2ac44f5050b5eac82", + "rev": "80479b6ec16fefd9c1db3ea13aeb038c60530f46", "type": "github" }, "original": { From a219e0d67e427d38254fe489ad864a6a131d7998 Mon Sep 17 00:00:00 2001 From: Hercules CI Effects Date: Wed, 2 Jul 2025 00:58:16 +0000 Subject: [PATCH 165/306] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'flake-parts': 'github:hercules-ci/flake-parts/49f0870db23e8c1ca0b5259734a02cd9e1e371a1?narHash=sha256-F82%2BgS044J1APL0n4hH50GYdPRv/5JWm34oCJYmVKdE%3D' (2025-06-01) → 'github:hercules-ci/flake-parts/77826244401ea9de6e3bac47c2db46005e1f30b5?narHash=sha256-Tyw1RjYEsp5scoigs1384gIg6e0GoBVjms4aXFfRssQ%3D' (2025-07-01) • Updated input 'flake-parts/nixpkgs-lib': 'github:nix-community/nixpkgs.lib/656a64127e9d791a334452c6b6606d17539476e2?narHash=sha256-rQaysilft1aVMwF14xIdGS3sj1yHlI6oKQNBRTF40cc%3D' (2025-06-01) → 'github:nix-community/nixpkgs.lib/14a40a1d7fb9afa4739275ac642ed7301a9ba1ab?narHash=sha256-urW/Ylk9FIfvXfliA1ywh75yszAbiTEVgpPeinFyVZo%3D' (2025-06-29) • Updated input 'nix': 'github:NixOS/nix/587b5f53618cf72e5ee725e2237227fbcc7b0f34?narHash=sha256-qMLM19XLxjUFKbPO12f47guvioAHXkxlhGyH8Kh9jMk%3D' (2025-05-30) → 'github:NixOS/nix/5879ab15771ee04fa963603373767d312fcde6cb?narHash=sha256-0vMwBjalzDqhFHpQf7/z1LHyIyN7vy5XEfTy4vlE87M%3D' (2025-07-01) • Updated input 'nix-cargo-integration': 'github:yusdacra/nix-cargo-integration/e1b777f69b0f5d6bbbf70c72fd7d7037df3aa9a4?narHash=sha256-K3Ytc2ELSCt3x3wMVr%2B%2BVHBa3FY1xA9k5IS7aks5zDM%3D' (2025-06-01) → 'github:yusdacra/nix-cargo-integration/92538b4f2233e68468f31f964a4514bd4b7b03ff?narHash=sha256-5Eql1AwM%2BpTUUbBNbjhB/lYZ4RRn3m9xvDx7EQYVwHw%3D' (2025-07-01) • Updated input 'nix-cargo-integration/dream2nix': 'github:nix-community/dream2nix/852a02488001f73526b94f880f4fb6dd8656ed4e?narHash=sha256-987t1pyspFbJ8TFf1aXR3/vdsJX09r7C3vW8Ojsy%2B14%3D' (2025-06-01) → 'github:nix-community/dream2nix/bd83ed026b859acb34c52c3fdc15631c8766af8e?narHash=sha256-udleaLnEHK3A5RZVCEQnE6Zeww6yq03NUcqJ/TcUQVc%3D' (2025-06-18) • Updated input 'nix-cargo-integration/parts': 'github:hercules-ci/flake-parts/c621e8422220273271f52058f618c94e405bb0f5?narHash=sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY%3D' (2025-04-01) → 'github:hercules-ci/flake-parts/9305fe4e5c2a6fcf5ba6a3ff155720fbe4076569?narHash=sha256-tYBdgS56eXYaWVW3fsnPQ/nFlgWi/Z2Ymhyu21zVM98%3D' (2025-06-08) • Updated input 'nix-cargo-integration/rust-overlay': 'github:oxalica/rust-overlay/12a0d94a2f2b06714f747ab97b2fa546f46b460c?narHash=sha256-bwkCAK9pOyI2Ww4Q4oO1Ynv7O9aZPrsIAMMASmhVGp4%3D' (2025-06-01) → 'github:oxalica/rust-overlay/6cfb7821732dac2d3e2dea857a5613d3b856c20c?narHash=sha256-/yd9nPcTfUZPFtwjRbdB5yGLdt3LTPqz6Ja63Joiahs%3D' (2025-07-01) • Updated input 'nix-cargo-integration/treefmt': 'github:numtide/treefmt-nix/1f3f7b784643d488ba4bf315638b2b0a4c5fb007?narHash=sha256-9YzfeN8CB6SzNPyPm2XjRRqSixDopTapaRsnTpXUEY8%3D' (2025-05-26) → 'github:numtide/treefmt-nix/ac8e6f32e11e9c7f153823abc3ab007f2a65d3e1?narHash=sha256-0IEdQB1nS%2BuViQw4k3VGUXntjkDp7aAlqcxdewb/hAc%3D' (2025-06-26) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/910796cabe436259a29a72e8d3f5e180fc6dfacc?narHash=sha256-StSrWhklmDuXT93yc3GrTlb0cKSS0agTAxMGjLKAsY8%3D' (2025-05-31) → 'github:NixOS/nixpkgs/3016b4b15d13f3089db8a41ef937b13a9e33a8df?narHash=sha256-P/SQmKDu06x8yv7i0s8bvnnuJYkxVGBWLWHaU%2Btt4YY%3D' (2025-06-30) (cherry picked from commit 2ef18d095833208830ba1bbb36381988df2907ba) --- flake.lock | 54 +++++++++++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/flake.lock b/flake.lock index 1ee9690..ed3b71c 100644 --- a/flake.lock +++ b/flake.lock @@ -27,11 +27,11 @@ "pyproject-nix": "pyproject-nix" }, "locked": { - "lastModified": 1748754808, - "narHash": "sha256-987t1pyspFbJ8TFf1aXR3/vdsJX09r7C3vW8Ojsy+14=", + "lastModified": 1750272463, + "narHash": "sha256-udleaLnEHK3A5RZVCEQnE6Zeww6yq03NUcqJ/TcUQVc=", "owner": "nix-community", "repo": "dream2nix", - "rev": "852a02488001f73526b94f880f4fb6dd8656ed4e", + "rev": "bd83ed026b859acb34c52c3fdc15631c8766af8e", "type": "github" }, "original": { @@ -77,11 +77,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1748821116, - "narHash": "sha256-F82+gS044J1APL0n4hH50GYdPRv/5JWm34oCJYmVKdE=", + "lastModified": 1751413152, + "narHash": "sha256-Tyw1RjYEsp5scoigs1384gIg6e0GoBVjms4aXFfRssQ=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "49f0870db23e8c1ca0b5259734a02cd9e1e371a1", + "rev": "77826244401ea9de6e3bac47c2db46005e1f30b5", "type": "github" }, "original": { @@ -170,11 +170,11 @@ "nixpkgs-regression": "nixpkgs-regression" }, "locked": { - "lastModified": 1748601301, - "narHash": "sha256-qMLM19XLxjUFKbPO12f47guvioAHXkxlhGyH8Kh9jMk=", + "lastModified": 1751391295, + "narHash": "sha256-0vMwBjalzDqhFHpQf7/z1LHyIyN7vy5XEfTy4vlE87M=", "owner": "NixOS", "repo": "nix", - "rev": "587b5f53618cf72e5ee725e2237227fbcc7b0f34", + "rev": "5879ab15771ee04fa963603373767d312fcde6cb", "type": "github" }, "original": { @@ -196,11 +196,11 @@ "treefmt": "treefmt" }, "locked": { - "lastModified": 1748758650, - "narHash": "sha256-K3Ytc2ELSCt3x3wMVr++VHBa3FY1xA9k5IS7aks5zDM=", + "lastModified": 1751350797, + "narHash": "sha256-5Eql1AwM+pTUUbBNbjhB/lYZ4RRn3m9xvDx7EQYVwHw=", "owner": "yusdacra", "repo": "nix-cargo-integration", - "rev": "e1b777f69b0f5d6bbbf70c72fd7d7037df3aa9a4", + "rev": "92538b4f2233e68468f31f964a4514bd4b7b03ff", "type": "github" }, "original": { @@ -211,11 +211,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1748693115, - "narHash": "sha256-StSrWhklmDuXT93yc3GrTlb0cKSS0agTAxMGjLKAsY8=", + "lastModified": 1751271578, + "narHash": "sha256-P/SQmKDu06x8yv7i0s8bvnnuJYkxVGBWLWHaU+tt4YY=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "910796cabe436259a29a72e8d3f5e180fc6dfacc", + "rev": "3016b4b15d13f3089db8a41ef937b13a9e33a8df", "type": "github" }, "original": { @@ -243,11 +243,11 @@ }, "nixpkgs-lib": { "locked": { - "lastModified": 1748740939, - "narHash": "sha256-rQaysilft1aVMwF14xIdGS3sj1yHlI6oKQNBRTF40cc=", + "lastModified": 1751159883, + "narHash": "sha256-urW/Ylk9FIfvXfliA1ywh75yszAbiTEVgpPeinFyVZo=", "owner": "nix-community", "repo": "nixpkgs.lib", - "rev": "656a64127e9d791a334452c6b6606d17539476e2", + "rev": "14a40a1d7fb9afa4739275ac642ed7301a9ba1ab", "type": "github" }, "original": { @@ -280,11 +280,11 @@ ] }, "locked": { - "lastModified": 1743550720, - "narHash": "sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY=", + "lastModified": 1749398372, + "narHash": "sha256-tYBdgS56eXYaWVW3fsnPQ/nFlgWi/Z2Ymhyu21zVM98=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "c621e8422220273271f52058f618c94e405bb0f5", + "rev": "9305fe4e5c2a6fcf5ba6a3ff155720fbe4076569", "type": "github" }, "original": { @@ -350,11 +350,11 @@ ] }, "locked": { - "lastModified": 1748746145, - "narHash": "sha256-bwkCAK9pOyI2Ww4Q4oO1Ynv7O9aZPrsIAMMASmhVGp4=", + "lastModified": 1751338093, + "narHash": "sha256-/yd9nPcTfUZPFtwjRbdB5yGLdt3LTPqz6Ja63Joiahs=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "12a0d94a2f2b06714f747ab97b2fa546f46b460c", + "rev": "6cfb7821732dac2d3e2dea857a5613d3b856c20c", "type": "github" }, "original": { @@ -394,11 +394,11 @@ ] }, "locked": { - "lastModified": 1748243702, - "narHash": "sha256-9YzfeN8CB6SzNPyPm2XjRRqSixDopTapaRsnTpXUEY8=", + "lastModified": 1750931469, + "narHash": "sha256-0IEdQB1nS+uViQw4k3VGUXntjkDp7aAlqcxdewb/hAc=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "1f3f7b784643d488ba4bf315638b2b0a4c5fb007", + "rev": "ac8e6f32e11e9c7f153823abc3ab007f2a65d3e1", "type": "github" }, "original": { From 2a599676de44302fd82283ad4a7c868b02c49dff Mon Sep 17 00:00:00 2001 From: Hercules CI Effects Date: Wed, 2 Jul 2025 00:58:19 +0000 Subject: [PATCH 166/306] dev/flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'pre-commit-hooks-nix': 'github:cachix/pre-commit-hooks.nix/80479b6ec16fefd9c1db3ea13aeb038c60530f46?narHash=sha256-2Y53NGIX2vxfie1rOW0Qb86vjRZ7ngizoo%2BbnXU9D9k%3D' (2025-05-16) → 'github:cachix/pre-commit-hooks.nix/16ec914f6fb6f599ce988427d9d94efddf25fe6d?narHash=sha256-wibppH3g/E2lxU43ZQHC5yA/7kIKLGxVEnsnVK1BtRg%3D' (2025-06-24) (cherry picked from commit 539876a0bd9dc72a40326489cdf4a08efc224dc6) --- dev/flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dev/flake.lock b/dev/flake.lock index b09048c..a94d8b0 100644 --- a/dev/flake.lock +++ b/dev/flake.lock @@ -178,11 +178,11 @@ "nixpkgs": [] }, "locked": { - "lastModified": 1747372754, - "narHash": "sha256-2Y53NGIX2vxfie1rOW0Qb86vjRZ7ngizoo+bnXU9D9k=", + "lastModified": 1750779888, + "narHash": "sha256-wibppH3g/E2lxU43ZQHC5yA/7kIKLGxVEnsnVK1BtRg=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "80479b6ec16fefd9c1db3ea13aeb038c60530f46", + "rev": "16ec914f6fb6f599ce988427d9d94efddf25fe6d", "type": "github" }, "original": { From d7c425a8c93228c98b8bbbf8d2c4b8572e4ee273 Mon Sep 17 00:00:00 2001 From: Hercules CI Effects Date: Sat, 2 Aug 2025 00:58:51 +0000 Subject: [PATCH 167/306] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'flake-parts': 'github:hercules-ci/flake-parts/77826244401ea9de6e3bac47c2db46005e1f30b5?narHash=sha256-Tyw1RjYEsp5scoigs1384gIg6e0GoBVjms4aXFfRssQ%3D' (2025-07-01) → 'github:hercules-ci/flake-parts/67df8c627c2c39c41dbec76a1f201929929ab0bd?narHash=sha256-XKqDMN1/Qj1DKivQvscI4vmHfDfvYR2pfuFOJiCeewM%3D' (2025-08-01) • Updated input 'flake-parts/nixpkgs-lib': 'github:nix-community/nixpkgs.lib/14a40a1d7fb9afa4739275ac642ed7301a9ba1ab?narHash=sha256-urW/Ylk9FIfvXfliA1ywh75yszAbiTEVgpPeinFyVZo%3D' (2025-06-29) → 'github:nix-community/nixpkgs.lib/0f36c44e01a6129be94e3ade315a5883f0228a6e?narHash=sha256-zvaMGVn14/Zz8hnp4VWT9xVnhc8vuL3TStRqwk22biA%3D' (2025-07-27) • Updated input 'nix': 'github:NixOS/nix/5879ab15771ee04fa963603373767d312fcde6cb?narHash=sha256-0vMwBjalzDqhFHpQf7/z1LHyIyN7vy5XEfTy4vlE87M%3D' (2025-07-01) → 'github:NixOS/nix/bbd14173b5c4677d098686be9605c88b40149684?narHash=sha256-Ba/vN5XBc%2BjWRfSl%2BYIEfWTjG4JSrQdhpOL9KDZ%2B1Jk%3D' (2025-07-30) • Updated input 'nix-cargo-integration': 'github:yusdacra/nix-cargo-integration/92538b4f2233e68468f31f964a4514bd4b7b03ff?narHash=sha256-5Eql1AwM%2BpTUUbBNbjhB/lYZ4RRn3m9xvDx7EQYVwHw%3D' (2025-07-01) → 'github:yusdacra/nix-cargo-integration/862a0888898e7714a52445a73e5b4feb090c685e?narHash=sha256-QgMGFq6cFKImxdupUW2l3PJBD3lflG%2B5YVI1h0UUQYI%3D' (2025-08-01) • Updated input 'nix-cargo-integration/dream2nix': 'github:nix-community/dream2nix/bd83ed026b859acb34c52c3fdc15631c8766af8e?narHash=sha256-udleaLnEHK3A5RZVCEQnE6Zeww6yq03NUcqJ/TcUQVc%3D' (2025-06-18) → 'github:nix-community/dream2nix/e6566e4ce924a8258499c379ee9552dba1883bce?narHash=sha256-jsoTEhkmn3weESMNRMLNk/ROW3fcHCr8Wgf5amzs5z8%3D' (2025-07-24) • Updated input 'nix-cargo-integration/dream2nix/pyproject-nix': 'github:davhau/pyproject.nix/5a06a2697b228c04dd2f35659b4b659ca74f7aeb?narHash=sha256-hFg5s/hoJFv7tDpiGvEvXP0UfFvFEDgTdyHIjDVHu1I%3D' (2023-12-13) → 'github:pyproject-nix/pyproject.nix/16ee295c25107a94e59a7fc7f2e5322851781162?narHash=sha256-luVj97hIMpCbwhx3hWiRwjP2YvljWy8FM%2B4W9njDhLA%3D' (2025-07-14) • Added input 'nix-cargo-integration/dream2nix/pyproject-nix/nixpkgs': follows 'nix-cargo-integration/dream2nix/nixpkgs' • Updated input 'nix-cargo-integration/parts': 'github:hercules-ci/flake-parts/9305fe4e5c2a6fcf5ba6a3ff155720fbe4076569?narHash=sha256-tYBdgS56eXYaWVW3fsnPQ/nFlgWi/Z2Ymhyu21zVM98%3D' (2025-06-08) → 'github:hercules-ci/flake-parts/644e0fc48951a860279da645ba77fe4a6e814c5e?narHash=sha256-TVcTNvOeWWk1DXljFxVRp%2BE0tzG1LhrVjOGGoMHuXio%3D' (2025-07-21) • Updated input 'nix-cargo-integration/rust-overlay': 'github:oxalica/rust-overlay/6cfb7821732dac2d3e2dea857a5613d3b856c20c?narHash=sha256-/yd9nPcTfUZPFtwjRbdB5yGLdt3LTPqz6Ja63Joiahs%3D' (2025-07-01) → 'github:oxalica/rust-overlay/ddd488184f01603b712ddbb6dc9fe0b8447eb7fc?narHash=sha256-mRB5OOx7H5kFwW8Qtc/7dO3qHsBQtZ/eYQEj93/Noo8%3D' (2025-08-01) • Updated input 'nix-cargo-integration/treefmt': 'github:numtide/treefmt-nix/ac8e6f32e11e9c7f153823abc3ab007f2a65d3e1?narHash=sha256-0IEdQB1nS%2BuViQw4k3VGUXntjkDp7aAlqcxdewb/hAc%3D' (2025-06-26) → 'github:numtide/treefmt-nix/6b9214fffbcf3f1e608efa15044431651635ca83?narHash=sha256-8rkd13WfClfZUBIYpX5dvG3O9V9w3K9FPQ9rY14VtBE%3D' (2025-07-29) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/3016b4b15d13f3089db8a41ef937b13a9e33a8df?narHash=sha256-P/SQmKDu06x8yv7i0s8bvnnuJYkxVGBWLWHaU%2Btt4YY%3D' (2025-06-30) → 'github:NixOS/nixpkgs/94def634a20494ee057c76998843c015909d6311?narHash=sha256-K2ViRJfdVGE8tpJejs8Qpvvejks1%2BA4GQej/lBk5y7I%3D' (2025-07-31) (cherry picked from commit c965d370e6590fba1d83d5a9fa91029e5bf9f640) --- flake.lock | 73 +++++++++++++++++++++++++++++------------------------- 1 file changed, 39 insertions(+), 34 deletions(-) diff --git a/flake.lock b/flake.lock index ed3b71c..b5dead0 100644 --- a/flake.lock +++ b/flake.lock @@ -27,11 +27,11 @@ "pyproject-nix": "pyproject-nix" }, "locked": { - "lastModified": 1750272463, - "narHash": "sha256-udleaLnEHK3A5RZVCEQnE6Zeww6yq03NUcqJ/TcUQVc=", + "lastModified": 1753366881, + "narHash": "sha256-jsoTEhkmn3weESMNRMLNk/ROW3fcHCr8Wgf5amzs5z8=", "owner": "nix-community", "repo": "dream2nix", - "rev": "bd83ed026b859acb34c52c3fdc15631c8766af8e", + "rev": "e6566e4ce924a8258499c379ee9552dba1883bce", "type": "github" }, "original": { @@ -77,11 +77,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1751413152, - "narHash": "sha256-Tyw1RjYEsp5scoigs1384gIg6e0GoBVjms4aXFfRssQ=", + "lastModified": 1754091436, + "narHash": "sha256-XKqDMN1/Qj1DKivQvscI4vmHfDfvYR2pfuFOJiCeewM=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "77826244401ea9de6e3bac47c2db46005e1f30b5", + "rev": "67df8c627c2c39c41dbec76a1f201929929ab0bd", "type": "github" }, "original": { @@ -170,11 +170,11 @@ "nixpkgs-regression": "nixpkgs-regression" }, "locked": { - "lastModified": 1751391295, - "narHash": "sha256-0vMwBjalzDqhFHpQf7/z1LHyIyN7vy5XEfTy4vlE87M=", + "lastModified": 1753906454, + "narHash": "sha256-Ba/vN5XBc+jWRfSl+YIEfWTjG4JSrQdhpOL9KDZ+1Jk=", "owner": "NixOS", "repo": "nix", - "rev": "5879ab15771ee04fa963603373767d312fcde6cb", + "rev": "bbd14173b5c4677d098686be9605c88b40149684", "type": "github" }, "original": { @@ -196,11 +196,11 @@ "treefmt": "treefmt" }, "locked": { - "lastModified": 1751350797, - "narHash": "sha256-5Eql1AwM+pTUUbBNbjhB/lYZ4RRn3m9xvDx7EQYVwHw=", + "lastModified": 1754029359, + "narHash": "sha256-QgMGFq6cFKImxdupUW2l3PJBD3lflG+5YVI1h0UUQYI=", "owner": "yusdacra", "repo": "nix-cargo-integration", - "rev": "92538b4f2233e68468f31f964a4514bd4b7b03ff", + "rev": "862a0888898e7714a52445a73e5b4feb090c685e", "type": "github" }, "original": { @@ -211,11 +211,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1751271578, - "narHash": "sha256-P/SQmKDu06x8yv7i0s8bvnnuJYkxVGBWLWHaU+tt4YY=", + "lastModified": 1753939845, + "narHash": "sha256-K2ViRJfdVGE8tpJejs8Qpvvejks1+A4GQej/lBk5y7I=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "3016b4b15d13f3089db8a41ef937b13a9e33a8df", + "rev": "94def634a20494ee057c76998843c015909d6311", "type": "github" }, "original": { @@ -243,11 +243,11 @@ }, "nixpkgs-lib": { "locked": { - "lastModified": 1751159883, - "narHash": "sha256-urW/Ylk9FIfvXfliA1ywh75yszAbiTEVgpPeinFyVZo=", + "lastModified": 1753579242, + "narHash": "sha256-zvaMGVn14/Zz8hnp4VWT9xVnhc8vuL3TStRqwk22biA=", "owner": "nix-community", "repo": "nixpkgs.lib", - "rev": "14a40a1d7fb9afa4739275ac642ed7301a9ba1ab", + "rev": "0f36c44e01a6129be94e3ade315a5883f0228a6e", "type": "github" }, "original": { @@ -280,11 +280,11 @@ ] }, "locked": { - "lastModified": 1749398372, - "narHash": "sha256-tYBdgS56eXYaWVW3fsnPQ/nFlgWi/Z2Ymhyu21zVM98=", + "lastModified": 1753121425, + "narHash": "sha256-TVcTNvOeWWk1DXljFxVRp+E0tzG1LhrVjOGGoMHuXio=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "9305fe4e5c2a6fcf5ba6a3ff155720fbe4076569", + "rev": "644e0fc48951a860279da645ba77fe4a6e814c5e", "type": "github" }, "original": { @@ -318,18 +318,23 @@ } }, "pyproject-nix": { - "flake": false, + "inputs": { + "nixpkgs": [ + "nix-cargo-integration", + "dream2nix", + "nixpkgs" + ] + }, "locked": { - "lastModified": 1702448246, - "narHash": "sha256-hFg5s/hoJFv7tDpiGvEvXP0UfFvFEDgTdyHIjDVHu1I=", - "owner": "davhau", + "lastModified": 1752481895, + "narHash": "sha256-luVj97hIMpCbwhx3hWiRwjP2YvljWy8FM+4W9njDhLA=", + "owner": "pyproject-nix", "repo": "pyproject.nix", - "rev": "5a06a2697b228c04dd2f35659b4b659ca74f7aeb", + "rev": "16ee295c25107a94e59a7fc7f2e5322851781162", "type": "github" }, "original": { - "owner": "davhau", - "ref": "dream2nix", + "owner": "pyproject-nix", "repo": "pyproject.nix", "type": "github" } @@ -350,11 +355,11 @@ ] }, "locked": { - "lastModified": 1751338093, - "narHash": "sha256-/yd9nPcTfUZPFtwjRbdB5yGLdt3LTPqz6Ja63Joiahs=", + "lastModified": 1754016903, + "narHash": "sha256-mRB5OOx7H5kFwW8Qtc/7dO3qHsBQtZ/eYQEj93/Noo8=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "6cfb7821732dac2d3e2dea857a5613d3b856c20c", + "rev": "ddd488184f01603b712ddbb6dc9fe0b8447eb7fc", "type": "github" }, "original": { @@ -394,11 +399,11 @@ ] }, "locked": { - "lastModified": 1750931469, - "narHash": "sha256-0IEdQB1nS+uViQw4k3VGUXntjkDp7aAlqcxdewb/hAc=", + "lastModified": 1753772294, + "narHash": "sha256-8rkd13WfClfZUBIYpX5dvG3O9V9w3K9FPQ9rY14VtBE=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "ac8e6f32e11e9c7f153823abc3ab007f2a65d3e1", + "rev": "6b9214fffbcf3f1e608efa15044431651635ca83", "type": "github" }, "original": { From 2a72f4663500bc16b0f3d7ae5b221fee509d5dac Mon Sep 17 00:00:00 2001 From: Hercules CI Effects Date: Sat, 2 Aug 2025 00:58:54 +0000 Subject: [PATCH 168/306] dev/flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'hercules-ci-effects': 'github:hercules-ci/hercules-ci-effects/231726642197817d20310b9d39dd4afb9e899489?narHash=sha256-EaAJhwfJGBncgIV/0NlJviid2DP93cTMc9h0q6P6xXk%3D' (2025-05-23) → 'github:hercules-ci/hercules-ci-effects/5f2e09654b2e70ba643e41609d9f9b6640f22113?narHash=sha256-CNBgr4OZSuklGtNOa9CnTNo9%2BXceqn/EDAC1Tc43fH8%3D' (2025-07-15) • Updated input 'hercules-ci-effects/flake-parts': 'github:hercules-ci/flake-parts/c621e8422220273271f52058f618c94e405bb0f5?narHash=sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY%3D' (2025-04-01) → 'github:hercules-ci/flake-parts/77826244401ea9de6e3bac47c2db46005e1f30b5?narHash=sha256-Tyw1RjYEsp5scoigs1384gIg6e0GoBVjms4aXFfRssQ%3D' (2025-07-01) • Updated input 'hercules-ci-effects/nixpkgs': 'github:NixOS/nixpkgs/adaa24fbf46737f3f1b5497bf64bae750f82942e?narHash=sha256-qhFMmDkeJX9KJwr5H32f1r7Prs7XbQWtO0h3V0a0rFY%3D' (2025-05-13) → 'github:NixOS/nixpkgs/9807714d6944a957c2e036f84b0ff8caf9930bc0?narHash=sha256-LwWRsENAZJKUdD3SpLluwDmdXY9F45ZEgCb0X%2BxgOL0%3D' (2025-07-08) • Updated input 'nix-unit': 'github:nix-community/nix-unit/be0d299e89a31e246c5472bf0e1005d4cc1e9e55?narHash=sha256-FV8uIBumYYmqOMEa6WR3lFxs0ocANT7bbawEDg%2BvWjo%3D' (2025-04-24) → 'github:nix-community/nix-unit/f0f20d931fa043905bc5fd50c5afa73f8eab67b3?narHash=sha256-mZbCa2eh6Iy9/PQHJytjM6U2rVHeIxQSlhDHJwTPWLk%3D' (2025-07-15) (cherry picked from commit ec52c15a7bbc84d01cfa78541538c9c2ea11fdd9) --- dev/flake.lock | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/dev/flake.lock b/dev/flake.lock index a94d8b0..ce03b65 100644 --- a/dev/flake.lock +++ b/dev/flake.lock @@ -24,11 +24,11 @@ ] }, "locked": { - "lastModified": 1743550720, - "narHash": "sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY=", + "lastModified": 1751413152, + "narHash": "sha256-Tyw1RjYEsp5scoigs1384gIg6e0GoBVjms4aXFfRssQ=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "c621e8422220273271f52058f618c94e405bb0f5", + "rev": "77826244401ea9de6e3bac47c2db46005e1f30b5", "type": "github" }, "original": { @@ -84,11 +84,11 @@ "nixpkgs": "nixpkgs" }, "locked": { - "lastModified": 1748000383, - "narHash": "sha256-EaAJhwfJGBncgIV/0NlJviid2DP93cTMc9h0q6P6xXk=", + "lastModified": 1752595130, + "narHash": "sha256-CNBgr4OZSuklGtNOa9CnTNo9+Xceqn/EDAC1Tc43fH8=", "owner": "hercules-ci", "repo": "hercules-ci-effects", - "rev": "231726642197817d20310b9d39dd4afb9e899489", + "rev": "5f2e09654b2e70ba643e41609d9f9b6640f22113", "type": "github" }, "original": { @@ -126,11 +126,11 @@ "treefmt-nix": "treefmt-nix" }, "locked": { - "lastModified": 1745514172, - "narHash": "sha256-FV8uIBumYYmqOMEa6WR3lFxs0ocANT7bbawEDg+vWjo=", + "lastModified": 1752551767, + "narHash": "sha256-mZbCa2eh6Iy9/PQHJytjM6U2rVHeIxQSlhDHJwTPWLk=", "owner": "nix-community", "repo": "nix-unit", - "rev": "be0d299e89a31e246c5472bf0e1005d4cc1e9e55", + "rev": "f0f20d931fa043905bc5fd50c5afa73f8eab67b3", "type": "github" }, "original": { @@ -141,11 +141,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1747179050, - "narHash": "sha256-qhFMmDkeJX9KJwr5H32f1r7Prs7XbQWtO0h3V0a0rFY=", + "lastModified": 1751984180, + "narHash": "sha256-LwWRsENAZJKUdD3SpLluwDmdXY9F45ZEgCb0X+xgOL0=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "adaa24fbf46737f3f1b5497bf64bae750f82942e", + "rev": "9807714d6944a957c2e036f84b0ff8caf9930bc0", "type": "github" }, "original": { From e04540d4f27b8b50cf8073bcf11d9c505a7556bb Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 21 Aug 2025 17:09:42 +0200 Subject: [PATCH 169/306] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/94def634a20494ee057c76998843c015909d6311?narHash=sha256-K2ViRJfdVGE8tpJejs8Qpvvejks1%2BA4GQej/lBk5y7I%3D' (2025-07-31) → 'github:NixOS/nixpkgs/20075955deac2583bb12f07151c2df830ef346b4?narHash=sha256-HMwfAJBdrr8wXAkbGhtcby1zGFvs%2BStOp19xNsbqdOg%3D' (2025-08-19) (cherry picked from commit 6a5e94de6460b6b27500307ec6bb14b5acef5bfb) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index b5dead0..fe639e3 100644 --- a/flake.lock +++ b/flake.lock @@ -211,11 +211,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1753939845, - "narHash": "sha256-K2ViRJfdVGE8tpJejs8Qpvvejks1+A4GQej/lBk5y7I=", + "lastModified": 1755615617, + "narHash": "sha256-HMwfAJBdrr8wXAkbGhtcby1zGFvs+StOp19xNsbqdOg=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "94def634a20494ee057c76998843c015909d6311", + "rev": "20075955deac2583bb12f07151c2df830ef346b4", "type": "github" }, "original": { From eba8c06c9d479df6ec4519f75f949e2aec266708 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 26 Aug 2025 14:49:59 +0200 Subject: [PATCH 170/306] chore: Configure nixfmt (cherry picked from commit 3ed92195dfcd4c3a86f1d77c464967531851f85c) --- dev/flake-module.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/flake-module.nix b/dev/flake-module.nix index ca5df3a..ab72d06 100644 --- a/dev/flake-module.nix +++ b/dev/flake-module.nix @@ -5,7 +5,7 @@ ]; perSystem = { config, pkgs, ... }: { - pre-commit.settings.hooks.nixpkgs-fmt.enable = true; + pre-commit.settings.hooks.nixfmt-rfc-style.enable = true; # Temporarily disable rustfmt due to configuration issues # pre-commit.settings.hooks.rustfmt.enable = true; pre-commit.settings.settings.rust.cargoManifestPath = "./rust/Cargo.toml"; @@ -52,7 +52,7 @@ ]; nativeBuildInputs = [ pkgs.rust-analyzer - pkgs.nixpkgs-fmt + pkgs.nixfmt-rfc-style pkgs.rustfmt pkgs.pkg-config pkgs.clang-tools # clangd From 83dece62b2a76134110dfd9bf97347c95fdda2d7 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 26 Aug 2025 14:52:12 +0200 Subject: [PATCH 171/306] chore: Apply nixfmt (cherry picked from commit d11c2029c5a33ec522f46dd3e00fec8db2c3729b) --- dev/flake-module.nix | 154 +++++++++++++++++++++++-------------------- flake.nix | 36 +++++++--- rust/nci.nix | 132 ++++++++++++++++++++----------------- 3 files changed, 184 insertions(+), 138 deletions(-) diff --git a/dev/flake-module.nix b/dev/flake-module.nix index ab72d06..7f9da9c 100644 --- a/dev/flake-module.nix +++ b/dev/flake-module.nix @@ -1,80 +1,94 @@ -{ lib, inputs, withSystem, ... }: { +{ + lib, + inputs, + withSystem, + ... +}: +{ imports = [ inputs.pre-commit-hooks-nix.flakeModule inputs.hercules-ci-effects.flakeModule ]; - perSystem = { config, pkgs, ... }: { + perSystem = + { config, pkgs, ... }: + { - pre-commit.settings.hooks.nixfmt-rfc-style.enable = true; - # Temporarily disable rustfmt due to configuration issues - # pre-commit.settings.hooks.rustfmt.enable = true; - pre-commit.settings.settings.rust.cargoManifestPath = "./rust/Cargo.toml"; + pre-commit.settings.hooks.nixfmt-rfc-style.enable = true; + # Temporarily disable rustfmt due to configuration issues + # pre-commit.settings.hooks.rustfmt.enable = true; + pre-commit.settings.settings.rust.cargoManifestPath = "./rust/Cargo.toml"; - # Check that we're using ///-style doc comments in Rust code. - # - # Unfortunately, rustfmt won't do this for us yet - at least not - # without nightly, and it might do too much. - pre-commit.settings.hooks.rust-doc-comments = { - enable = true; - files = "\\.rs$"; - entry = "${pkgs.writeScript "rust-doc-comments" '' - #!${pkgs.runtimeShell} - set -uxo pipefail - grep -n -C3 --color=always -F '/**' "$@" - r=$? - set -e - if [ $r -eq 0 ]; then - echo "Please replace /**-style comments by /// style comments in Rust code." - exit 1 - fi - ''}"; + # Check that we're using ///-style doc comments in Rust code. + # + # Unfortunately, rustfmt won't do this for us yet - at least not + # without nightly, and it might do too much. + pre-commit.settings.hooks.rust-doc-comments = { + enable = true; + files = "\\.rs$"; + entry = "${pkgs.writeScript "rust-doc-comments" '' + #!${pkgs.runtimeShell} + set -uxo pipefail + grep -n -C3 --color=always -F '/**' "$@" + r=$? + set -e + if [ $r -eq 0 ]; then + echo "Please replace /**-style comments by /// style comments in Rust code." + exit 1 + fi + ''}"; + }; + + devShells.default = pkgs.mkShell { + name = "nix-bindings-devshell"; + strictDeps = true; + inputsFrom = [ config.nci.outputs.nix-bindings.devShell ]; + inherit (config.nci.outputs.nix-bindings.devShell.env) + LIBCLANG_PATH + NIX_CC_UNWRAPPED + ; + NIX_DEBUG_INFO_DIRS = + let + # TODO: add to Nixpkgs lib + getDebug = + pkg: + if pkg ? debug then + pkg.debug + else if pkg ? lib then + pkg.lib + else + pkg; + in + "${getDebug config.packages.nix}/lib/debug"; + buildInputs = [ + config.packages.nix + ]; + nativeBuildInputs = [ + pkgs.rust-analyzer + pkgs.nixfmt-rfc-style + pkgs.rustfmt + pkgs.pkg-config + pkgs.clang-tools # clangd + pkgs.valgrind + pkgs.gdb + pkgs.hci + # TODO: set up cargo-valgrind in shell and build + # currently both this and `cargo install cargo-valgrind` + # produce a binary that says ENOENT. + # pkgs.cargo-valgrind + ]; + shellHook = '' + ${config.pre-commit.installationScript} + source ${../rust/bindgen-gcc.sh} + echo 1>&2 "Welcome to the development shell!" + ''; + # rust-analyzer needs a NIX_PATH for some reason + NIX_PATH = "nixpkgs=${inputs.nixpkgs}"; + }; }; - - devShells.default = pkgs.mkShell { - name = "nix-bindings-devshell"; - strictDeps = true; - inputsFrom = [ config.nci.outputs.nix-bindings.devShell ]; - inherit (config.nci.outputs.nix-bindings.devShell.env) - LIBCLANG_PATH - NIX_CC_UNWRAPPED - ; - NIX_DEBUG_INFO_DIRS = - let - # TODO: add to Nixpkgs lib - getDebug = pkg: - if pkg?debug then pkg.debug - else if pkg?lib then pkg.lib - else pkg; - in - "${getDebug config.packages.nix}/lib/debug"; - buildInputs = [ - config.packages.nix - ]; - nativeBuildInputs = [ - pkgs.rust-analyzer - pkgs.nixfmt-rfc-style - pkgs.rustfmt - pkgs.pkg-config - pkgs.clang-tools # clangd - pkgs.valgrind - pkgs.gdb - pkgs.hci - # TODO: set up cargo-valgrind in shell and build - # currently both this and `cargo install cargo-valgrind` - # produce a binary that says ENOENT. - # pkgs.cargo-valgrind - ]; - shellHook = '' - ${config.pre-commit.installationScript} - source ${../rust/bindgen-gcc.sh} - echo 1>&2 "Welcome to the development shell!" - ''; - # rust-analyzer needs a NIX_PATH for some reason - NIX_PATH = "nixpkgs=${inputs.nixpkgs}"; + herculesCI = + hci@{ config, ... }: + { + ciSystems = [ "x86_64-linux" ]; }; - }; - herculesCI = hci@{ config, ... }: { - ciSystems = [ "x86_64-linux" ]; - }; flake = { }; } diff --git a/flake.nix b/flake.nix index f75342b..af537e3 100644 --- a/flake.nix +++ b/flake.nix @@ -10,19 +10,36 @@ nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; }; - outputs = inputs@{ self, flake-parts, ... }: - flake-parts.lib.mkFlake - { inherit inputs; } - ({ lib, ... }: { + outputs = + inputs@{ self, flake-parts, ... }: + flake-parts.lib.mkFlake { inherit inputs; } ( + { + lib, + ... + }: + { imports = [ inputs.nix-cargo-integration.flakeModule inputs.flake-parts.flakeModules.partitions ./rust/nci.nix ]; - systems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ]; - perSystem = { config, self', inputs', pkgs, ... }: { - packages.nix = inputs'.nix.packages.nix; - }; + systems = [ + "x86_64-linux" + "aarch64-linux" + "x86_64-darwin" + "aarch64-darwin" + ]; + perSystem = + { + config, + self', + inputs', + pkgs, + ... + }: + { + packages.nix = inputs'.nix.packages.nix; + }; partitionedAttrs.devShells = "dev"; partitionedAttrs.checks = "dev"; @@ -31,5 +48,6 @@ partitions.dev.module = { imports = [ ./dev/flake-module.nix ]; }; - }); + } + ); } diff --git a/rust/nci.nix b/rust/nci.nix index 87392b3..fabe122 100644 --- a/rust/nci.nix +++ b/rust/nci.nix @@ -1,69 +1,83 @@ { - perSystem = { lib, config, pkgs, ... }: { - # https://flake.parts/options/nix-cargo-integration - nci.projects.nix-bindings = { - path = ./.; - drvConfig = { - mkDerivation = { - buildInputs = [ - # stdbool.h - pkgs.stdenv.cc - ] ++ - (if config.packages.nix?libs - then - let l = config.packages.nix.libs; in [ - l.nix-expr-c - l.nix-store-c - l.nix-util-c - l.nix-flake-c + perSystem = + { + lib, + config, + pkgs, + ... + }: + { + # https://flake.parts/options/nix-cargo-integration + nci.projects.nix-bindings = { + path = ./.; + drvConfig = { + mkDerivation = { + buildInputs = [ + # stdbool.h + pkgs.stdenv.cc ] - else [ config.packages.nix ]); - nativeBuildInputs = [ - pkgs.pkg-config - ]; - # bindgen uses clang to generate bindings, but it doesn't know where to - # find our stdenv cc's headers, so when it's gcc, we need to tell it. - postConfigure = lib.optionalString pkgs.stdenv.cc.isGNU '' - source ${./bindgen-gcc.sh} - ''; - # Prepare the environment for Nix to work. - # Nix does not provide a suitable environment for running itself in - # the sandbox - not by default. We configure it to use a relocated store. - preCheck = '' - # nix needs a home directory - export HOME="$(mktemp -d $TMPDIR/home.XXXXXX)" + ++ ( + if config.packages.nix ? libs then + let + l = config.packages.nix.libs; + in + [ + l.nix-expr-c + l.nix-store-c + l.nix-util-c + l.nix-flake-c + ] + else + [ config.packages.nix ] + ); + nativeBuildInputs = [ + pkgs.pkg-config + ]; + # bindgen uses clang to generate bindings, but it doesn't know where to + # find our stdenv cc's headers, so when it's gcc, we need to tell it. + postConfigure = lib.optionalString pkgs.stdenv.cc.isGNU '' + source ${./bindgen-gcc.sh} + ''; + # Prepare the environment for Nix to work. + # Nix does not provide a suitable environment for running itself in + # the sandbox - not by default. We configure it to use a relocated store. + preCheck = '' + # nix needs a home directory + export HOME="$(mktemp -d $TMPDIR/home.XXXXXX)" - # configure a relocated store - store_data=$(mktemp -d $TMPDIR/store-data.XXXXXX) - export NIX_REMOTE="$store_data" - export NIX_BUILD_HOOK= - export NIX_CONF_DIR=$store_data/etc - export NIX_LOCALSTATE_DIR=$store_data/nix/var - export NIX_LOG_DIR=$store_data/nix/var/log/nix - export NIX_STATE_DIR=$store_data/nix/var/nix + # configure a relocated store + store_data=$(mktemp -d $TMPDIR/store-data.XXXXXX) + export NIX_REMOTE="$store_data" + export NIX_BUILD_HOOK= + export NIX_CONF_DIR=$store_data/etc + export NIX_LOCALSTATE_DIR=$store_data/nix/var + export NIX_LOG_DIR=$store_data/nix/var/log/nix + export NIX_STATE_DIR=$store_data/nix/var/nix - echo "Configuring relocated store at $NIX_REMOTE..." + echo "Configuring relocated store at $NIX_REMOTE..." - # Init ahead of time, because concurrent initialization is flaky - ${# Not using nativeBuildInputs because this should (hopefully) be - # the only place where we need a nix binary. Let's stay in control. - pkgs.buildPackages.nix}/bin/nix-store --init + # Init ahead of time, because concurrent initialization is flaky + ${ + # Not using nativeBuildInputs because this should (hopefully) be + # the only place where we need a nix binary. Let's stay in control. + pkgs.buildPackages.nix + }/bin/nix-store --init - echo "Store initialized." - ''; - }; - # NOTE: duplicated in flake.nix devShell - env = { - LIBCLANG_PATH = - lib.makeLibraryPath [ pkgs.buildPackages.llvmPackages.clang-unwrapped ]; - BINDGEN_EXTRA_CLANG_ARGS = - # Work around missing [[deprecated]] in clang - "-x c++ -std=c++2a"; - } // lib.optionalAttrs pkgs.stdenv.cc.isGNU { - # Avoid cc wrapper, because we only need to add the compiler/"system" dirs - NIX_CC_UNWRAPPED = "${pkgs.stdenv.cc.cc}/bin/gcc"; + echo "Store initialized." + ''; + }; + # NOTE: duplicated in flake.nix devShell + env = { + LIBCLANG_PATH = lib.makeLibraryPath [ pkgs.buildPackages.llvmPackages.clang-unwrapped ]; + BINDGEN_EXTRA_CLANG_ARGS = + # Work around missing [[deprecated]] in clang + "-x c++ -std=c++2a"; + } + // lib.optionalAttrs pkgs.stdenv.cc.isGNU { + # Avoid cc wrapper, because we only need to add the compiler/"system" dirs + NIX_CC_UNWRAPPED = "${pkgs.stdenv.cc.cc}/bin/gcc"; + }; }; }; }; - }; } From b3a80dd36ebe8551b236a736b64ae674998923ae Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 26 Aug 2025 15:10:57 +0200 Subject: [PATCH 172/306] maint: Remove Cargo.toml patch constraints (cherry picked from commit 155550be6a8487771ddb67d09f3eba829e54e622) --- rust/nix-c-raw/Cargo.toml | 4 ++-- rust/nix-expr/Cargo.toml | 10 +++++----- rust/nix-fetchers/Cargo.toml | 8 ++++---- rust/nix-flake/Cargo.toml | 10 +++++----- rust/nix-store/Cargo.toml | 6 +++--- rust/nix-util/Cargo.toml | 4 ++-- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/rust/nix-c-raw/Cargo.toml b/rust/nix-c-raw/Cargo.toml index 4bb530a..d19b4fa 100644 --- a/rust/nix-c-raw/Cargo.toml +++ b/rust/nix-c-raw/Cargo.toml @@ -9,5 +9,5 @@ license = "LGPL-2.1" path = "src/lib.rs" [build-dependencies] -bindgen = "0.69.4" -pkg-config = "0.3.30" +bindgen = "0.69" +pkg-config = "0.3" diff --git a/rust/nix-expr/Cargo.toml b/rust/nix-expr/Cargo.toml index 08c5e37..0cd087b 100644 --- a/rust/nix-expr/Cargo.toml +++ b/rust/nix-expr/Cargo.toml @@ -8,11 +8,11 @@ license = "LGPL-2.1" path = "src/lib.rs" [dependencies] -anyhow = "1.0.79" +anyhow = "1.0" nix-store = { path = "../nix-store" } nix-util = { path = "../nix-util" } nix-c-raw = { path = "../nix-c-raw" } -lazy_static = "1.4.0" -ctor = "0.2.7" -tempfile = "3.10.1" -cstr = "0.2.12" +lazy_static = "1.4" +ctor = "0.2" +tempfile = "3.10" +cstr = "0.2" diff --git a/rust/nix-fetchers/Cargo.toml b/rust/nix-fetchers/Cargo.toml index 31d12c5..721aeb9 100644 --- a/rust/nix-fetchers/Cargo.toml +++ b/rust/nix-fetchers/Cargo.toml @@ -8,10 +8,10 @@ license = "LGPL-2.1" path = "src/lib.rs" [dependencies] -anyhow = "1.0.79" +anyhow = "1.0" nix-store = { path = "../nix-store" } nix-util = { path = "../nix-util" } nix-c-raw = { path = "../nix-c-raw" } -ctor = "0.2.7" -tempfile = "3.10.1" -cstr = "0.2.12" +ctor = "0.2" +tempfile = "3.10" +cstr = "0.2" diff --git a/rust/nix-flake/Cargo.toml b/rust/nix-flake/Cargo.toml index 0121902..78fd715 100644 --- a/rust/nix-flake/Cargo.toml +++ b/rust/nix-flake/Cargo.toml @@ -8,13 +8,13 @@ license = "LGPL-2.1" path = "src/lib.rs" [dependencies] -anyhow = "1.0.79" +anyhow = "1.0" nix-expr = { path = "../nix-expr" } nix-fetchers = { path = "../nix-fetchers" } nix-store = { path = "../nix-store" } nix-util = { path = "../nix-util" } nix-c-raw = { path = "../nix-c-raw" } -lazy_static = "1.4.0" -ctor = "0.2.7" -tempfile = "3.10.1" -cstr = "0.2.12" +lazy_static = "1.4" +ctor = "0.2" +tempfile = "3.10" +cstr = "0.2" diff --git a/rust/nix-store/Cargo.toml b/rust/nix-store/Cargo.toml index 2d3764e..a7ea036 100644 --- a/rust/nix-store/Cargo.toml +++ b/rust/nix-store/Cargo.toml @@ -9,10 +9,10 @@ license = "LGPL-2.1" path = "src/lib.rs" [dependencies] -anyhow = "1.0.79" +anyhow = "1.0" nix-util = { path = "../nix-util" } nix-c-raw = { path = "../nix-c-raw" } -lazy_static = "1.4.0" +lazy_static = "1.4" [build-dependencies] -pkg-config = "0.3.30" +pkg-config = "0.3" diff --git a/rust/nix-util/Cargo.toml b/rust/nix-util/Cargo.toml index 6293092..cefbbb8 100644 --- a/rust/nix-util/Cargo.toml +++ b/rust/nix-util/Cargo.toml @@ -8,6 +8,6 @@ license = "LGPL-2.1" path = "src/lib.rs" [dependencies] -anyhow = "1.0.79" +anyhow = "1.0" nix-c-raw = { path = "../nix-c-raw" } -ctor = "0.2.7" +ctor = "0.2" From 200dcf08915e11808b4bac9e70c414ca78cb53bb Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 26 Aug 2025 15:16:07 +0200 Subject: [PATCH 173/306] maint: Update Cargo.lock (cherry picked from commit 4329f7f120d70887b6e995cd46787e33e0ca581c) --- rust/Cargo.lock | 1788 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 1614 insertions(+), 174 deletions(-) diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 8a22246..62df232 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -2,70 +2,159 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "Inflector" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" -dependencies = [ - "lazy_static", - "regex", -] - [[package]] name = "addr2line" -version = "0.22.0" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] [[package]] -name = "adler" -version = "1.0.2" +name = "adler2" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] -name = "anyhow" -version = "1.0.79" +name = "allocator-api2" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] -name = "async-trait" -version = "0.1.83" +name = "android-tzdata" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.49", + "libc", ] [[package]] -name = "backtrace" -version = "0.3.73" +name = "ansi-parser" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +checksum = "c43e7fd8284f025d0bd143c2855618ecdf697db55bde39211e5c9faec7669173" +dependencies = [ + "heapless", + "nom", +] + +[[package]] +name = "anstream" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" +dependencies = [ + "windows-sys 0.60.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.60.2", +] + +[[package]] +name = "anyhow" +version = "1.0.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" + +[[package]] +name = "async-channel" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "backtrace" +version = "0.3.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ "addr2line", - "cc", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", + "windows-targets 0.52.6", ] [[package]] @@ -76,14 +165,14 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bindgen" -version = "0.69.4" +version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ "bitflags", "cexpr", "clang-sys", - "itertools", + "itertools 0.12.1", "lazy_static", "lazycell", "log", @@ -99,21 +188,48 @@ dependencies = [ [[package]] name = "bitflags" -version = "2.4.2" +version = "2.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +checksum = "34efbcccd345379ca2868b2b2c9d3782e9cc58ba87bc7d79d5b53d9c9ae6f25d" + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.6.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "cassowary" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" + +[[package]] +name = "castaway" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" +dependencies = [ + "rustversion", +] [[package]] name = "cc" -version = "1.1.24" +version = "1.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812acba72f0a070b003d3697490d2b55b837230ae7c6c6497f05cc2ddbb8d938" +checksum = "42bc4aea80032b7bf409b0bc7ccad88853858911b7713a8062fdc0623867bedc" dependencies = [ "shlex", ] @@ -129,21 +245,176 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chrono" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-link", +] [[package]] name = "clang-sys" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ "glob", "libc", "libloading", ] +[[package]] +name = "clap" +version = "4.5.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc0e74a703892159f5ae7d3aac52c8e6c392f5ae5f359c70b5881d60aaac318" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap-markdown" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2a2617956a06d4885b490697b5307ebb09fec10b088afc18c81762d848c2339" +dependencies = [ + "clap", +] + +[[package]] +name = "clap_builder" +version = "4.5.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3e7f4214277f3c7aa526a59dd3fbe306a370daee1f8b7b8c987069cd8e888a8" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_complete" +version = "4.5.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d9501bd3f5f09f7bbee01da9a511073ed30a80cd7a509f1214bb74eadea71ad" +dependencies = [ + "clap", +] + +[[package]] +name = "clap_derive" +version = "4.5.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14cb31bb0a7d536caef2639baa7fad459e15c3144efefa6dbd1c84562c4739f6" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" + +[[package]] +name = "clap_mangen" +version = "0.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b4c3c54b30f0d9adcb47f25f61fcce35c4dd8916638c6b82fbd5f4fb4179e2" +dependencies = [ + "clap", + "roff", +] + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "compact_str" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b79c4069c6cad78e2e0cdfcbd26275770669fb39fd308a752dc110e83b9af32" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "rustversion", + "ryu", + "static_assertions", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crossterm" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" +dependencies = [ + "bitflags", + "crossterm_winapi", + "mio", + "parking_lot", + "rustix 0.38.44", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + [[package]] name = "cstr" version = "0.2.12" @@ -165,26 +436,129 @@ dependencies = [ ] [[package]] -name = "either" -version = "1.10.0" +name = "ctrlc" +version = "3.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +checksum = "46f93780a459b7d656ef7f071fe699c4d3d2cb201c4b24d085b6ddc505276e73" +dependencies = [ + "nix 0.30.1", + "windows-sys 0.59.0", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.8" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.60.2", +] + +[[package]] +name = "event-listener" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", ] [[package]] name = "fastrand" -version = "2.1.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fd-lock" +version = "4.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce92ff622d6dadf7349484f42c93271a0d49b7cc4d466a936405bacbe10aa78" +dependencies = [ + "cfg-if", + "rustix 1.0.8", + "windows-sys 0.59.0", +] [[package]] name = "fnv" @@ -192,6 +566,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "fuchsia-cprng" version = "0.1.1" @@ -199,26 +579,152 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" [[package]] -name = "gimli" -version = "0.29.0" +name = "futures-core" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "hash32" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "home" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] +[[package]] +name = "iana-time-zone" +version = "0.1.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "indoc" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" + +[[package]] +name = "instability" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435d80800b936787d62688c927b6490e887c7ef5ff9ce922c6c6050fca75eb9a" +dependencies = [ + "darling", + "indoc", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "io-uring" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" +dependencies = [ + "bitflags", + "cfg-if", + "libc", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "itertools" version = "0.12.1" @@ -229,10 +735,57 @@ dependencies = [ ] [[package]] -name = "lazy_static" -version = "1.4.0" +name = "itertools" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "json-patch" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "159294d661a039f7644cea7e4d844e6b25aaf71c1ffe9d73a96d768c24b0faf4" +dependencies = [ + "jsonptr", + "serde", + "serde_json", + "thiserror 1.0.69", +] + +[[package]] +name = "jsonptr" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5a3cc660ba5d72bce0b3bb295bf20847ccbb40fd423f3f05b61273672e561fe" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "lazycell" @@ -242,37 +795,62 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" [[package]] name = "libloading" -version = "0.8.1" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" +checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" dependencies = [ "cfg-if", - "windows-sys 0.48.0", + "windows-targets 0.53.3", ] [[package]] name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + +[[package]] +name = "lock_api" version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +dependencies = [ + "autocfg", + "scopeguard", +] [[package]] name = "log" -version = "0.4.20" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "lru" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" +dependencies = [ + "hashbrown 0.15.5", +] [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "minimal-lexical" @@ -282,11 +860,23 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.4" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ - "adler", + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +dependencies = [ + "libc", + "log", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.59.0", ] [[package]] @@ -301,6 +891,18 @@ dependencies = [ "libc", ] +[[package]] +name = "nix" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", +] + [[package]] name = "nix-c-raw" version = "0.1.0" @@ -376,7 +978,28 @@ dependencies = [ name = "nixops4" version = "0.1.0" dependencies = [ + "ansi-parser", + "anyhow", + "async-trait", + "clap", + "clap-markdown", + "clap_complete", + "clap_mangen", + "crossterm", + "ctrlc", + "json-patch", + "nix 0.29.0", "nixops4-core", + "nixops4-resource", + "nixops4-resource-runner", + "pubsub-rs", + "ratatui", + "serde", + "serde_json", + "tokio", + "tracing", + "tracing-subscriber", + "tracing-tunnel", ] [[package]] @@ -388,15 +1011,44 @@ dependencies = [ "serde_json", ] +[[package]] +name = "nixops4-eval" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "base64", + "cstr", + "ctor", + "ctrlc", + "nix-expr", + "nix-fetchers", + "nix-flake", + "nix-store", + "nix-util", + "nixops4-core", + "serde", + "serde_json", + "tempdir", + "tokio", + "tracing", + "tracing-tunnel", +] + [[package]] name = "nixops4-resource" version = "0.1.0" dependencies = [ "anyhow", - "nix", - "schemafy", + "chrono", + "json-patch", + "nix 0.29.0", + "prettyplease", + "schemars", "serde", "serde_json", + "syn", + "typify", ] [[package]] @@ -409,9 +1061,12 @@ dependencies = [ "clap_complete", "clap_derive", "clap_mangen", + "json-patch", "nixops4-resource", "serde", "serde_json", + "tokio", + "tracing", ] [[package]] @@ -419,9 +1074,13 @@ name = "nixops4-resources-local" version = "0.1.0" dependencies = [ "anyhow", + "chrono", + "fd-lock", + "json-patch", "nixops4-resource", "serde", "serde_json", + "tempfile", ] [[package]] @@ -435,37 +1094,103 @@ dependencies = [ ] [[package]] -name = "object" -version = "0.36.4" +name = "nu-ansi-term" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.19.0" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pkg-config" -version = "0.3.30" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "prettyplease" -version = "0.2.16" +version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", "syn", @@ -473,27 +1198,111 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] [[package]] -name = "quote" -version = "1.0.35" +name = "pubsub-rs" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "9cba7b19d46ae57a9a6307681961fd24ab5e2794e4cc7e2e711cacfa733f610b" +dependencies = [ + "async-channel", + "dashmap", + "tokio", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] [[package]] -name = "regex" -version = "1.10.3" +name = "r-efi" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" +dependencies = [ + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "rdrand", + "winapi", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + +[[package]] +name = "ratatui" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b" +dependencies = [ + "bitflags", + "cassowary", + "compact_str", + "crossterm", + "indoc", + "instability", + "itertools 0.13.0", + "lru", + "paste", + "strum", + "unicode-segmentation", + "unicode-truncate", + "unicode-width 0.2.0", +] + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "redox_syscall" +version = "0.5.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" dependencies = [ "aho-corasick", "memchr", @@ -503,9 +1312,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.5" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" dependencies = [ "aho-corasick", "memchr", @@ -514,9 +1323,19 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.2" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" + +[[package]] +name = "regress" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "145bb27393fe455dd64d6cbc8d059adfa392590a45eadf079c01b11857e7b010" +dependencies = [ + "hashbrown 0.15.5", + "memchr", +] [[package]] name = "remove_dir_all" @@ -535,9 +1354,9 @@ checksum = "88f8660c1ff60292143c98d08fc6e2f654d722db50410e3f3797d40baaf9d8f3" [[package]] name = "rustc-demangle" -version = "0.1.24" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] name = "rustc-hash" @@ -547,15 +1366,143 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" -version = "0.38.31" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ "bitflags", "errno", "libc", - "linux-raw-sys", - "windows-sys 0.52.0", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys 0.9.4", + "windows-sys 0.60.2", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "schemars" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" +dependencies = [ + "dyn-clone", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" +dependencies = [ + "serde", +] + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_derive_internals" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.143" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_tokenstream" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64060d864397305347a78851c51588fd283767e7e7589829e8121d65512340f1" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "syn", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", ] [[package]] @@ -565,10 +1512,102 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] -name = "syn" -version = "2.0.49" +name = "signal-hook" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915aea9e586f80826ee59f8453c1101f9d1c4b3964cd2460185ee8e299ada496" +checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" +dependencies = [ + "libc", + "mio", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + +[[package]] +name = "syn" +version = "2.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", @@ -576,45 +1615,341 @@ dependencies = [ ] [[package]] -name = "tempfile" -version = "3.10.1" +name = "tempdir" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" +dependencies = [ + "rand", + "remove_dir_all", +] + +[[package]] +name = "tempfile" +version = "3.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b61f8f20e3a6f7e0649d825294eaf317edce30f82cf6026e7e4cb9222a7d1e" +dependencies = [ + "fastrand", + "getrandom", + "once_cell", + "rustix 1.0.8", + "windows-sys 0.60.2", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" +dependencies = [ + "thiserror-impl 2.0.16", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" dependencies = [ "cfg-if", - "fastrand", - "rustix", - "windows-sys 0.52.0", ] [[package]] name = "tokio" -version = "1.40.0" +version = "1.47.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" dependencies = [ "backtrace", "bytes", + "io-uring", + "libc", + "mio", + "parking_lot", "pin-project-lite", + "signal-hook-registry", + "slab", + "socket2", "tokio-macros", + "windows-sys 0.59.0", ] [[package]] name = "tokio-macros" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn", +] + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +dependencies = [ + "nu-ansi-term", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "tracing-tunnel" +version = "0.1.99-fork-0" +source = "git+https://github.com/roberth/tracing-toolbox?tag=0.1.99-fork-0#2b342882dd2ed36beb3ea4eed5bb28c70d13a28f" +dependencies = [ + "once_cell", + "serde", + "tracing-core", +] + +[[package]] +name = "typify" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7144144e97e987c94758a3017c920a027feac0799df325d6df4fc8f08d02068e" +dependencies = [ + "typify-impl", + "typify-macro", +] + +[[package]] +name = "typify-impl" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "062879d46aa4c9dfe0d33b035bbaf512da192131645d05deacb7033ec8581a09" +dependencies = [ + "heck", + "log", + "proc-macro2", + "quote", + "regress", + "schemars", + "semver", + "serde", + "serde_json", + "syn", + "thiserror 2.0.16", + "unicode-ident", +] + +[[package]] +name = "typify-macro" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9708a3ceb6660ba3f8d2b8f0567e7d4b8b198e2b94d093b8a6077a751425de9e" +dependencies = [ + "proc-macro2", + "quote", + "schemars", + "semver", + "serde", + "serde_json", + "serde_tokenstream", + "syn", + "typify-impl", ] [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-truncate" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" +dependencies = [ + "itertools 0.13.0", + "unicode-segmentation", + "unicode-width 0.1.14", +] + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "which" @@ -625,137 +1960,242 @@ dependencies = [ "either", "home", "once_cell", - "rustix", + "rustix 0.38.44", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link", ] [[package]] name = "windows-sys" -version = "0.48.0" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] name = "windows-sys" -version = "0.52.0" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.53.3", ] [[package]] name = "windows-targets" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.53.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows-link", + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" [[package]] name = "windows_aarch64_msvc" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" [[package]] name = "windows_i686_gnu" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" [[package]] name = "windows_i686_msvc" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" [[package]] name = "windows_x86_64_gnu" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" [[package]] name = "windows_x86_64_msvc" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags", +] From 3bef49427155fbd2dfbce13aa9999cb3102701b3 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 26 Aug 2025 16:45:25 +0200 Subject: [PATCH 174/306] fix: Add mutex to nix_util::settings to prevent concurrent access segfault Fixes #106 The Nix settings system uses global mutable state without internal synchronization. When multiple threads call settings::set concurrently (as happens in parallel test execution), this causes a segfault in the C++ std::set implementation. Changes: - Add mutex to serialize access through the Rust API - Add documentation explaining thread safety limitations - Add Once guard in nix-flake tests to minimize concurrent access The mutex provides protection between Rust callers, though it cannot completely prevent C++ Nix code from modifying settings concurrently. (cherry picked from commit 203917657b60c4e1dcbaf442bec64c37c634abc4) --- rust/nix-flake/src/lib.rs | 9 ++++++++- rust/nix-util/src/settings.rs | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/rust/nix-flake/src/lib.rs b/rust/nix-flake/src/lib.rs index b7ea7ba..e4c45e5 100644 --- a/rust/nix-flake/src/lib.rs +++ b/rust/nix-flake/src/lib.rs @@ -259,9 +259,16 @@ mod tests { use nix_store::store::Store; use super::*; + use std::sync::Once; + + static INIT: Once = Once::new(); fn init() { - nix_util::settings::set("experimental-features", "flakes").unwrap(); + // Only set experimental-features once to minimize the window where + // concurrent Nix operations might read the setting while it's being modified + INIT.call_once(|| { + nix_util::settings::set("experimental-features", "flakes").unwrap(); + }); } #[test] diff --git a/rust/nix-util/src/settings.rs b/rust/nix-util/src/settings.rs index fc29810..93d305d 100644 --- a/rust/nix-util/src/settings.rs +++ b/rust/nix-util/src/settings.rs @@ -1,22 +1,54 @@ use anyhow::Result; use nix_c_raw as raw; +use std::sync::Mutex; use crate::{ check_call, context, result_string_init, string_return::{callback_get_result_string, callback_get_result_string_data}, }; +// Global mutex to protect concurrent access to Nix settings +// See the documentation on `set()` for important thread safety information. +static SETTINGS_MUTEX: Mutex<()> = Mutex::new(()); + +/// Set a Nix setting. +/// +/// # Thread Safety +/// +/// This function uses a mutex to serialize access through the Rust API. +/// However, the underlying Nix settings system uses global mutable state +/// without internal synchronization. +/// +/// The mutex provides protection between Rust callers but cannot prevent: +/// - C++ Nix code from modifying settings concurrently +/// - Other Nix operations from reading settings during modification +/// +/// For multi-threaded applications, ensure that no other Nix operations +/// are running while changing settings. Settings are best modified during +/// single-threaded initialization. pub fn set(key: &str, value: &str) -> Result<()> { + // Lock the mutex to ensure thread-safe access to global settings + let guard = SETTINGS_MUTEX.lock().unwrap(); + let mut ctx = context::Context::new(); let key = std::ffi::CString::new(key)?; let value = std::ffi::CString::new(value)?; unsafe { check_call!(raw::setting_set(&mut ctx, key.as_ptr(), value.as_ptr()))?; } + drop(guard); Ok(()) } +/// Get a Nix setting. +/// +/// # Thread Safety +/// +/// See the documentation on [`set()`] for important thread safety information. pub fn get(key: &str) -> Result { + // Lock the mutex to ensure thread-safe access to global settings + let guard = SETTINGS_MUTEX.lock().unwrap(); + let mut ctx = context::Context::new(); let key = std::ffi::CString::new(key)?; let mut r: Result = result_string_init!(); @@ -28,6 +60,7 @@ pub fn get(key: &str) -> Result { callback_get_result_string_data(&mut r) ))?; } + drop(guard); r } From 2bd665dd647c575a62864553bc0cbf3432ac4781 Mon Sep 17 00:00:00 2001 From: Hercules CI Effects Date: Tue, 2 Sep 2025 00:58:29 +0000 Subject: [PATCH 175/306] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'flake-parts': 'github:hercules-ci/flake-parts/67df8c627c2c39c41dbec76a1f201929929ab0bd?narHash=sha256-XKqDMN1/Qj1DKivQvscI4vmHfDfvYR2pfuFOJiCeewM%3D' (2025-08-01) → 'github:hercules-ci/flake-parts/4524271976b625a4a605beefd893f270620fd751?narHash=sha256-%2BuWLQZccFHwqpGqr2Yt5VsW/PbeJVTn9Dk6SHWhNRPw%3D' (2025-09-01) • Updated input 'flake-parts/nixpkgs-lib': 'github:nix-community/nixpkgs.lib/0f36c44e01a6129be94e3ade315a5883f0228a6e?narHash=sha256-zvaMGVn14/Zz8hnp4VWT9xVnhc8vuL3TStRqwk22biA%3D' (2025-07-27) → 'github:nix-community/nixpkgs.lib/a73b9c743612e4244d865a2fdee11865283c04e6?narHash=sha256-x2rJ%2BOvzq0sCMpgfgGaaqgBSwY%2BLST%2BWbZ6TytnT9Rk%3D' (2025-08-10) • Updated input 'nix': 'github:NixOS/nix/bbd14173b5c4677d098686be9605c88b40149684?narHash=sha256-Ba/vN5XBc%2BjWRfSl%2BYIEfWTjG4JSrQdhpOL9KDZ%2B1Jk%3D' (2025-07-30) → 'github:NixOS/nix/ab095c029c7deef98b99c5249c09fe9a8a095800?narHash=sha256-JpSWFjOgPWeWb5bb%2BHMMGR%2BQ0dOH7nl2t4WgyBVaWx8%3D' (2025-09-01) • Updated input 'nix-cargo-integration': 'github:yusdacra/nix-cargo-integration/862a0888898e7714a52445a73e5b4feb090c685e?narHash=sha256-QgMGFq6cFKImxdupUW2l3PJBD3lflG%2B5YVI1h0UUQYI%3D' (2025-08-01) → 'github:yusdacra/nix-cargo-integration/529758e12981b62d6977a7bc61c86e19c3e11594?narHash=sha256-v3MasfpOtYUV4MzG5JGRS4JIo3lccjBzH9qZi7zQzSM%3D' (2025-09-01) • Updated input 'nix-cargo-integration/dream2nix': 'github:nix-community/dream2nix/e6566e4ce924a8258499c379ee9552dba1883bce?narHash=sha256-jsoTEhkmn3weESMNRMLNk/ROW3fcHCr8Wgf5amzs5z8%3D' (2025-07-24) → 'github:nix-community/dream2nix/fbec3263cb4895ac86ee9506cdc4e6919a1a2214?narHash=sha256-nrDovydywSKRbWim9Ynmgj8SBm8LK3DI2WuhIqzOHYI%3D' (2025-08-12) • Updated input 'nix-cargo-integration/parts': 'github:hercules-ci/flake-parts/644e0fc48951a860279da645ba77fe4a6e814c5e?narHash=sha256-TVcTNvOeWWk1DXljFxVRp%2BE0tzG1LhrVjOGGoMHuXio%3D' (2025-07-21) → 'github:hercules-ci/flake-parts/af66ad14b28a127c5c0f3bbb298218fc63528a18?narHash=sha256-pHYj8gUBapuUzKV/kN/tR3Zvqc7o6gdFB9XKXIp1SQ8%3D' (2025-08-06) • Updated input 'nix-cargo-integration/rust-overlay': 'github:oxalica/rust-overlay/ddd488184f01603b712ddbb6dc9fe0b8447eb7fc?narHash=sha256-mRB5OOx7H5kFwW8Qtc/7dO3qHsBQtZ/eYQEj93/Noo8%3D' (2025-08-01) → 'github:oxalica/rust-overlay/b29e5365120f344fe7161f14fc9e272fcc41ee56?narHash=sha256-z/Iy4qvcMqzhA2IAAg71Sw4BrMwbBHvCS90ZoPLsnIk%3D' (2025-09-01) • Updated input 'nix-cargo-integration/treefmt': 'github:numtide/treefmt-nix/6b9214fffbcf3f1e608efa15044431651635ca83?narHash=sha256-8rkd13WfClfZUBIYpX5dvG3O9V9w3K9FPQ9rY14VtBE%3D' (2025-07-29) → 'github:numtide/treefmt-nix/1aabc6c05ccbcbf4a635fb7a90400e44282f61c4?narHash=sha256-F1oFfV51AE259I85av%2BMAia221XwMHCOtZCMcZLK2Jk%3D' (2025-08-31) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/20075955deac2583bb12f07151c2df830ef346b4?narHash=sha256-HMwfAJBdrr8wXAkbGhtcby1zGFvs%2BStOp19xNsbqdOg%3D' (2025-08-19) → 'github:NixOS/nixpkgs/d7600c775f877cd87b4f5a831c28aa94137377aa?narHash=sha256-tlOn88coG5fzdyqz6R93SQL5Gpq%2Bm/DsWpekNFhqPQk%3D' (2025-08-30) (cherry picked from commit bb1e61291f24855b09fc50b42d22dbe5e9e6779e) --- flake.lock | 54 +++++++++++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/flake.lock b/flake.lock index fe639e3..dce5dbc 100644 --- a/flake.lock +++ b/flake.lock @@ -27,11 +27,11 @@ "pyproject-nix": "pyproject-nix" }, "locked": { - "lastModified": 1753366881, - "narHash": "sha256-jsoTEhkmn3weESMNRMLNk/ROW3fcHCr8Wgf5amzs5z8=", + "lastModified": 1754978539, + "narHash": "sha256-nrDovydywSKRbWim9Ynmgj8SBm8LK3DI2WuhIqzOHYI=", "owner": "nix-community", "repo": "dream2nix", - "rev": "e6566e4ce924a8258499c379ee9552dba1883bce", + "rev": "fbec3263cb4895ac86ee9506cdc4e6919a1a2214", "type": "github" }, "original": { @@ -77,11 +77,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1754091436, - "narHash": "sha256-XKqDMN1/Qj1DKivQvscI4vmHfDfvYR2pfuFOJiCeewM=", + "lastModified": 1756770412, + "narHash": "sha256-+uWLQZccFHwqpGqr2Yt5VsW/PbeJVTn9Dk6SHWhNRPw=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "67df8c627c2c39c41dbec76a1f201929929ab0bd", + "rev": "4524271976b625a4a605beefd893f270620fd751", "type": "github" }, "original": { @@ -170,11 +170,11 @@ "nixpkgs-regression": "nixpkgs-regression" }, "locked": { - "lastModified": 1753906454, - "narHash": "sha256-Ba/vN5XBc+jWRfSl+YIEfWTjG4JSrQdhpOL9KDZ+1Jk=", + "lastModified": 1756762578, + "narHash": "sha256-JpSWFjOgPWeWb5bb+HMMGR+Q0dOH7nl2t4WgyBVaWx8=", "owner": "NixOS", "repo": "nix", - "rev": "bbd14173b5c4677d098686be9605c88b40149684", + "rev": "ab095c029c7deef98b99c5249c09fe9a8a095800", "type": "github" }, "original": { @@ -196,11 +196,11 @@ "treefmt": "treefmt" }, "locked": { - "lastModified": 1754029359, - "narHash": "sha256-QgMGFq6cFKImxdupUW2l3PJBD3lflG+5YVI1h0UUQYI=", + "lastModified": 1756707650, + "narHash": "sha256-v3MasfpOtYUV4MzG5JGRS4JIo3lccjBzH9qZi7zQzSM=", "owner": "yusdacra", "repo": "nix-cargo-integration", - "rev": "862a0888898e7714a52445a73e5b4feb090c685e", + "rev": "529758e12981b62d6977a7bc61c86e19c3e11594", "type": "github" }, "original": { @@ -211,11 +211,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1755615617, - "narHash": "sha256-HMwfAJBdrr8wXAkbGhtcby1zGFvs+StOp19xNsbqdOg=", + "lastModified": 1756542300, + "narHash": "sha256-tlOn88coG5fzdyqz6R93SQL5Gpq+m/DsWpekNFhqPQk=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "20075955deac2583bb12f07151c2df830ef346b4", + "rev": "d7600c775f877cd87b4f5a831c28aa94137377aa", "type": "github" }, "original": { @@ -243,11 +243,11 @@ }, "nixpkgs-lib": { "locked": { - "lastModified": 1753579242, - "narHash": "sha256-zvaMGVn14/Zz8hnp4VWT9xVnhc8vuL3TStRqwk22biA=", + "lastModified": 1754788789, + "narHash": "sha256-x2rJ+Ovzq0sCMpgfgGaaqgBSwY+LST+WbZ6TytnT9Rk=", "owner": "nix-community", "repo": "nixpkgs.lib", - "rev": "0f36c44e01a6129be94e3ade315a5883f0228a6e", + "rev": "a73b9c743612e4244d865a2fdee11865283c04e6", "type": "github" }, "original": { @@ -280,11 +280,11 @@ ] }, "locked": { - "lastModified": 1753121425, - "narHash": "sha256-TVcTNvOeWWk1DXljFxVRp+E0tzG1LhrVjOGGoMHuXio=", + "lastModified": 1754487366, + "narHash": "sha256-pHYj8gUBapuUzKV/kN/tR3Zvqc7o6gdFB9XKXIp1SQ8=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "644e0fc48951a860279da645ba77fe4a6e814c5e", + "rev": "af66ad14b28a127c5c0f3bbb298218fc63528a18", "type": "github" }, "original": { @@ -355,11 +355,11 @@ ] }, "locked": { - "lastModified": 1754016903, - "narHash": "sha256-mRB5OOx7H5kFwW8Qtc/7dO3qHsBQtZ/eYQEj93/Noo8=", + "lastModified": 1756694554, + "narHash": "sha256-z/Iy4qvcMqzhA2IAAg71Sw4BrMwbBHvCS90ZoPLsnIk=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "ddd488184f01603b712ddbb6dc9fe0b8447eb7fc", + "rev": "b29e5365120f344fe7161f14fc9e272fcc41ee56", "type": "github" }, "original": { @@ -399,11 +399,11 @@ ] }, "locked": { - "lastModified": 1753772294, - "narHash": "sha256-8rkd13WfClfZUBIYpX5dvG3O9V9w3K9FPQ9rY14VtBE=", + "lastModified": 1756662192, + "narHash": "sha256-F1oFfV51AE259I85av+MAia221XwMHCOtZCMcZLK2Jk=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "6b9214fffbcf3f1e608efa15044431651635ca83", + "rev": "1aabc6c05ccbcbf4a635fb7a90400e44282f61c4", "type": "github" }, "original": { From edd9ec9e848a473d82fca774896feb3878fcb7d4 Mon Sep 17 00:00:00 2001 From: Hercules CI Effects Date: Tue, 2 Sep 2025 00:58:32 +0000 Subject: [PATCH 176/306] dev/flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'hercules-ci-effects': 'github:hercules-ci/hercules-ci-effects/5f2e09654b2e70ba643e41609d9f9b6640f22113?narHash=sha256-CNBgr4OZSuklGtNOa9CnTNo9%2BXceqn/EDAC1Tc43fH8%3D' (2025-07-15) → 'github:hercules-ci/hercules-ci-effects/99e03e72e3f7e13506f80ef9ebaedccb929d84d0?narHash=sha256-AavrbMltJKcC2Fx0lfJoZfmy7g87ebXU0ddVenhajLA%3D' (2025-08-15) • Updated input 'hercules-ci-effects/flake-parts': 'github:hercules-ci/flake-parts/77826244401ea9de6e3bac47c2db46005e1f30b5?narHash=sha256-Tyw1RjYEsp5scoigs1384gIg6e0GoBVjms4aXFfRssQ%3D' (2025-07-01) → 'github:hercules-ci/flake-parts/af66ad14b28a127c5c0f3bbb298218fc63528a18?narHash=sha256-pHYj8gUBapuUzKV/kN/tR3Zvqc7o6gdFB9XKXIp1SQ8%3D' (2025-08-06) • Updated input 'hercules-ci-effects/nixpkgs': 'github:NixOS/nixpkgs/9807714d6944a957c2e036f84b0ff8caf9930bc0?narHash=sha256-LwWRsENAZJKUdD3SpLluwDmdXY9F45ZEgCb0X%2BxgOL0%3D' (2025-07-08) → 'github:NixOS/nixpkgs/005433b926e16227259a1843015b5b2b7f7d1fc3?narHash=sha256-IVft239Bc8p8Dtvf7UAACMG5P3ZV%2B3/aO28gXpGtMXI%3D' (2025-08-12) • Updated input 'nix-unit': 'github:nix-community/nix-unit/f0f20d931fa043905bc5fd50c5afa73f8eab67b3?narHash=sha256-mZbCa2eh6Iy9/PQHJytjM6U2rVHeIxQSlhDHJwTPWLk%3D' (2025-07-15) → 'github:nix-community/nix-unit/388045c7bee5a2617cca8e94444bea1862df19ac?narHash=sha256-OLaoMtlNL5AvYuaQ5P4p7riy7M9J8XpFdWKpD5RzQfU%3D' (2025-08-12) • Updated input 'nix-unit/flake-parts': 'github:hercules-ci/flake-parts/205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9?narHash=sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c%3D' (2024-12-04) → 'github:hercules-ci/flake-parts/af66ad14b28a127c5c0f3bbb298218fc63528a18?narHash=sha256-pHYj8gUBapuUzKV/kN/tR3Zvqc7o6gdFB9XKXIp1SQ8%3D' (2025-08-06) • Removed input 'nix-unit/nix-github-actions' • Removed input 'nix-unit/nix-github-actions/nixpkgs' • Updated input 'nix-unit/nixpkgs': 'github:NixOS/nixpkgs/929116e316068c7318c54eb4d827f7d9756d5e9c?narHash=sha256-aLJxoTDDSqB%2B/3orsulE6/qdlX6MzDLIITLZqdgMpqo%3D' (2024-12-05) → 'github:NixOS/nixpkgs/641d909c4a7538f1539da9240dedb1755c907e40?narHash=sha256-HfVZCXic9XLBgybP0318ym3cDnGwBs/%2BH5MgxFVYF4I%3D' (2025-08-10) • Updated input 'nix-unit/treefmt-nix': 'github:numtide/treefmt-nix/357cda84af1d74626afb7fb3bc12d6957167cda9?narHash=sha256-9qOp6jNdezzLMxwwXaXZWPXosHbNqno%2Bf7Ii/xftqZ8%3D' (2024-12-08) → 'github:numtide/treefmt-nix/7d81f6fb2e19bf84f1c65135d1060d829fae2408?narHash=sha256-2vX8QjO5lRsDbNYvN9hVHXLU6oMl%2BV/PsmIiJREG4rE%3D' (2025-08-10) • Updated input 'pre-commit-hooks-nix': 'github:cachix/pre-commit-hooks.nix/16ec914f6fb6f599ce988427d9d94efddf25fe6d?narHash=sha256-wibppH3g/E2lxU43ZQHC5yA/7kIKLGxVEnsnVK1BtRg%3D' (2025-06-24) → 'github:cachix/pre-commit-hooks.nix/e891a93b193fcaf2fc8012d890dc7f0befe86ec2?narHash=sha256-RF7j6C1TmSTK9tYWO6CdEMtg6XZaUKcvZwOCD2SICZs%3D' (2025-08-23) • Updated input 'pre-commit-hooks-nix/flake-compat': 'github:edolstra/flake-compat/0f9255e01c2351cc7d116c072cb317785dd33b33?narHash=sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U%3D' (2023-10-04) → 'github:edolstra/flake-compat/9100a0f413b0c601e0533d1d94ffd501ce2e7885?narHash=sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX%2BfjA8Xf8PUmqCY%3D' (2025-05-12) (cherry picked from commit fe70d9ec4657a238f6d7912599e61e307d2aef87) --- dev/flake.lock | 76 ++++++++++++++++++-------------------------------- 1 file changed, 27 insertions(+), 49 deletions(-) diff --git a/dev/flake.lock b/dev/flake.lock index ce03b65..d9508ff 100644 --- a/dev/flake.lock +++ b/dev/flake.lock @@ -3,11 +3,11 @@ "flake-compat": { "flake": false, "locked": { - "lastModified": 1696426674, - "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "lastModified": 1747046372, + "narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=", "owner": "edolstra", "repo": "flake-compat", - "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885", "type": "github" }, "original": { @@ -24,11 +24,11 @@ ] }, "locked": { - "lastModified": 1751413152, - "narHash": "sha256-Tyw1RjYEsp5scoigs1384gIg6e0GoBVjms4aXFfRssQ=", + "lastModified": 1754487366, + "narHash": "sha256-pHYj8gUBapuUzKV/kN/tR3Zvqc7o6gdFB9XKXIp1SQ8=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "77826244401ea9de6e3bac47c2db46005e1f30b5", + "rev": "af66ad14b28a127c5c0f3bbb298218fc63528a18", "type": "github" }, "original": { @@ -44,11 +44,11 @@ ] }, "locked": { - "lastModified": 1733312601, - "narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=", + "lastModified": 1754487366, + "narHash": "sha256-pHYj8gUBapuUzKV/kN/tR3Zvqc7o6gdFB9XKXIp1SQ8=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9", + "rev": "af66ad14b28a127c5c0f3bbb298218fc63528a18", "type": "github" }, "original": { @@ -84,11 +84,11 @@ "nixpkgs": "nixpkgs" }, "locked": { - "lastModified": 1752595130, - "narHash": "sha256-CNBgr4OZSuklGtNOa9CnTNo9+Xceqn/EDAC1Tc43fH8=", + "lastModified": 1755233722, + "narHash": "sha256-AavrbMltJKcC2Fx0lfJoZfmy7g87ebXU0ddVenhajLA=", "owner": "hercules-ci", "repo": "hercules-ci-effects", - "rev": "5f2e09654b2e70ba643e41609d9f9b6640f22113", + "rev": "99e03e72e3f7e13506f80ef9ebaedccb929d84d0", "type": "github" }, "original": { @@ -97,40 +97,18 @@ "type": "github" } }, - "nix-github-actions": { - "inputs": { - "nixpkgs": [ - "nix-unit", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1731952509, - "narHash": "sha256-p4gB3Rhw8R6Ak4eMl8pqjCPOLCZRqaehZxdZ/mbFClM=", - "owner": "nix-community", - "repo": "nix-github-actions", - "rev": "7b5f051df789b6b20d259924d349a9ba3319b226", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "nix-github-actions", - "type": "github" - } - }, "nix-unit": { "inputs": { "flake-parts": "flake-parts_2", - "nix-github-actions": "nix-github-actions", "nixpkgs": "nixpkgs_2", "treefmt-nix": "treefmt-nix" }, "locked": { - "lastModified": 1752551767, - "narHash": "sha256-mZbCa2eh6Iy9/PQHJytjM6U2rVHeIxQSlhDHJwTPWLk=", + "lastModified": 1754985033, + "narHash": "sha256-OLaoMtlNL5AvYuaQ5P4p7riy7M9J8XpFdWKpD5RzQfU=", "owner": "nix-community", "repo": "nix-unit", - "rev": "f0f20d931fa043905bc5fd50c5afa73f8eab67b3", + "rev": "388045c7bee5a2617cca8e94444bea1862df19ac", "type": "github" }, "original": { @@ -141,11 +119,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1751984180, - "narHash": "sha256-LwWRsENAZJKUdD3SpLluwDmdXY9F45ZEgCb0X+xgOL0=", + "lastModified": 1755027561, + "narHash": "sha256-IVft239Bc8p8Dtvf7UAACMG5P3ZV+3/aO28gXpGtMXI=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "9807714d6944a957c2e036f84b0ff8caf9930bc0", + "rev": "005433b926e16227259a1843015b5b2b7f7d1fc3", "type": "github" }, "original": { @@ -157,11 +135,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1733376361, - "narHash": "sha256-aLJxoTDDSqB+/3orsulE6/qdlX6MzDLIITLZqdgMpqo=", + "lastModified": 1754800730, + "narHash": "sha256-HfVZCXic9XLBgybP0318ym3cDnGwBs/+H5MgxFVYF4I=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "929116e316068c7318c54eb4d827f7d9756d5e9c", + "rev": "641d909c4a7538f1539da9240dedb1755c907e40", "type": "github" }, "original": { @@ -178,11 +156,11 @@ "nixpkgs": [] }, "locked": { - "lastModified": 1750779888, - "narHash": "sha256-wibppH3g/E2lxU43ZQHC5yA/7kIKLGxVEnsnVK1BtRg=", + "lastModified": 1755960406, + "narHash": "sha256-RF7j6C1TmSTK9tYWO6CdEMtg6XZaUKcvZwOCD2SICZs=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "16ec914f6fb6f599ce988427d9d94efddf25fe6d", + "rev": "e891a93b193fcaf2fc8012d890dc7f0befe86ec2", "type": "github" }, "original": { @@ -206,11 +184,11 @@ ] }, "locked": { - "lastModified": 1733662930, - "narHash": "sha256-9qOp6jNdezzLMxwwXaXZWPXosHbNqno+f7Ii/xftqZ8=", + "lastModified": 1754847726, + "narHash": "sha256-2vX8QjO5lRsDbNYvN9hVHXLU6oMl+V/PsmIiJREG4rE=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "357cda84af1d74626afb7fb3bc12d6957167cda9", + "rev": "7d81f6fb2e19bf84f1c65135d1060d829fae2408", "type": "github" }, "original": { From c924fed81bb8f2e0e6b87adf70c65e13a07639de Mon Sep 17 00:00:00 2001 From: Tristan Ross Date: Thu, 18 Sep 2025 12:54:21 -0700 Subject: [PATCH 177/306] fix: rust types (cherry picked from commit 925a0abd694f6409b6d54b6cad3a61fc916215d3) --- rust/nix-expr/src/eval_state.rs | 2 +- rust/nix-flake/src/lib.rs | 4 ++-- rust/nix-util/src/context.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index af24045..e6cf8bd 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -284,7 +284,7 @@ impl EvalState { let n = unsafe { check_call!(raw::get_attrs_size(&mut self.context, v.raw_ptr())) }?; let mut attrs = Vec::with_capacity(n as usize); for i in 0..n { - let cstr_ptr: *const i8 = unsafe { + let cstr_ptr: *const u8 = unsafe { check_call!(raw::get_attr_name_byidx( &mut self.context, v.raw_ptr(), diff --git a/rust/nix-flake/src/lib.rs b/rust/nix-flake/src/lib.rs index e4c45e5..dfaa533 100644 --- a/rust/nix-flake/src/lib.rs +++ b/rust/nix-flake/src/lib.rs @@ -87,7 +87,7 @@ impl FlakeReferenceParseFlags { context::check_call!(raw::flake_reference_parse_flags_set_base_directory( &mut ctx, self.ptr.as_ptr(), - base_directory.as_ptr() as *const i8, + base_directory.as_ptr() as *const u8, base_directory.len() )) }?; @@ -125,7 +125,7 @@ impl FlakeReference { fetch_settings.raw_ptr(), flake_settings.ptr, flags.ptr.as_ptr(), - reference.as_ptr() as *const i8, + reference.as_ptr() as *const u8, reference.len(), // pointer to ptr &mut ptr, diff --git a/rust/nix-util/src/context.rs b/rust/nix-util/src/context.rs index 8e12a8b..0a885b5 100644 --- a/rust/nix-util/src/context.rs +++ b/rust/nix-util/src/context.rs @@ -50,7 +50,7 @@ impl Context { raw::set_err_msg( self.inner.as_ptr(), raw::err_NIX_OK, - b"\0".as_ptr() as *const i8, + b"\0".as_ptr() as *const u8, ); } } From f7d4499ab214651e570e65abb9ef9247bdf657fb Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 19 Sep 2025 18:10:50 +0200 Subject: [PATCH 178/306] fix: Use c_char where appropriate This builds on the previous commit to make it compatible with the currently locked build configuration. (cherry picked from commit bd1edec2fa5251263c768c3e2b312eb4f21d2bb4) --- rust/nix-expr/src/eval_state.rs | 2 +- rust/nix-flake/src/lib.rs | 6 +++--- rust/nix-util/src/context.rs | 3 ++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index e6cf8bd..81dc35a 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -284,7 +284,7 @@ impl EvalState { let n = unsafe { check_call!(raw::get_attrs_size(&mut self.context, v.raw_ptr())) }?; let mut attrs = Vec::with_capacity(n as usize); for i in 0..n { - let cstr_ptr: *const u8 = unsafe { + let cstr_ptr: *const c_char = unsafe { check_call!(raw::get_attr_name_byidx( &mut self.context, v.raw_ptr(), diff --git a/rust/nix-flake/src/lib.rs b/rust/nix-flake/src/lib.rs index dfaa533..acd5fa4 100644 --- a/rust/nix-flake/src/lib.rs +++ b/rust/nix-flake/src/lib.rs @@ -1,4 +1,4 @@ -use std::{ffi::CString, ptr::NonNull}; +use std::{ffi::CString, os::raw::c_char, ptr::NonNull}; use anyhow::{Context as _, Result}; use nix_c_raw as raw; @@ -87,7 +87,7 @@ impl FlakeReferenceParseFlags { context::check_call!(raw::flake_reference_parse_flags_set_base_directory( &mut ctx, self.ptr.as_ptr(), - base_directory.as_ptr() as *const u8, + base_directory.as_ptr() as *const c_char, base_directory.len() )) }?; @@ -125,7 +125,7 @@ impl FlakeReference { fetch_settings.raw_ptr(), flake_settings.ptr, flags.ptr.as_ptr(), - reference.as_ptr() as *const u8, + reference.as_ptr() as *const c_char, reference.len(), // pointer to ptr &mut ptr, diff --git a/rust/nix-util/src/context.rs b/rust/nix-util/src/context.rs index 0a885b5..55d8332 100644 --- a/rust/nix-util/src/context.rs +++ b/rust/nix-util/src/context.rs @@ -1,5 +1,6 @@ use anyhow::{bail, Result}; use nix_c_raw as raw; +use std::os::raw::c_char; use std::ptr::null_mut; use std::ptr::NonNull; @@ -50,7 +51,7 @@ impl Context { raw::set_err_msg( self.inner.as_ptr(), raw::err_NIX_OK, - b"\0".as_ptr() as *const u8, + b"\0".as_ptr() as *const c_char, ); } } From 4e80e1ad4aec574391152a36dac7ebd76b48a2ff Mon Sep 17 00:00:00 2001 From: Tristan Ross Date: Fri, 19 Sep 2025 10:16:17 -0700 Subject: [PATCH 179/306] feat: nix_expr::eval_state::require_list functions (cherry picked from commit 8498ebe3dddb34b274d83895b9bfecfb36ef6ab7) --- rust/nix-expr/src/eval_state.rs | 40 +++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 81dc35a..27f0271 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -362,6 +362,46 @@ impl EvalState { Ok(v2.map(|x| unsafe { Value::new(x) })) } + /// Evaluates, require that the value is a list, and select an element by index. + pub fn require_list_select_idx( + &mut self, + v: &Value, + idx: u32, + ) -> Result> { + let t = self.value_type(v)?; + if t != ValueType::List { + bail!("expected a list, but got a {:?}", t); + } + let v2 = unsafe { + check_call_opt_key!(raw::get_list_byidx( + &mut self.context, + v.raw_ptr(), + self.eval_state.as_ptr(), + idx + )) + }?; + Ok(v2.map(|x| unsafe { Value::new(x) })) + } + + /// Evaluates, require that the value is a list. + /// Returns the number of elements in the list. + pub fn require_list_size( + &mut self, + v: &Value, + ) -> Result { + let t = self.value_type(v)?; + if t != ValueType::List { + bail!("expected a list, but got a {:?}", t); + } + let ret = unsafe { + check_call!(raw::get_list_size( + &mut self.context, + v.raw_ptr() + )) + }?; + Ok(ret) + } + /// Create a new value containing the passed string. /// Returns a string value without any string context. pub fn new_value_str(&mut self, s: &str) -> Result { From 759550b47fb93372b897b9f4aa4413cf2c5b749a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 20 Sep 2025 01:12:32 +0200 Subject: [PATCH 180/306] refact: Rename require_list_select_idx -> require_list_select_idx_strict + tests The rename makes it so that we'll have the original name for the more sensible lazy behavior. (cherry picked from commit 6fdae53df54da0c6349ac32789ec67e3a25a5774) --- rust/nix-expr/src/eval_state.rs | 239 ++++++++++++++++++++++++++++---- 1 file changed, 214 insertions(+), 25 deletions(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 27f0271..188cab2 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -362,16 +362,44 @@ impl EvalState { Ok(v2.map(|x| unsafe { Value::new(x) })) } - /// Evaluates, require that the value is a list, and select an element by index. - pub fn require_list_select_idx( - &mut self, - v: &Value, - idx: u32, - ) -> Result> { + /// Evaluates, require that the value is a list. + /// Returns the number of elements in the list. + /// + /// This function only forces evaluation of the list structure itself, + /// not the individual elements. Elements remain as lazy thunks. + pub fn require_list_size(&mut self, v: &Value) -> Result { let t = self.value_type(v)?; if t != ValueType::List { bail!("expected a list, but got a {:?}", t); } + let ret = unsafe { check_call!(raw::get_list_size(&mut self.context, v.raw_ptr())) }?; + Ok(ret) + } + + /// Evaluates, require that the value is a list, and select an element by index. + /// + /// Returns `None` if the index is out of bounds. + /// + /// # Strictness + /// + /// This function forces evaluation of the selected element, similar to + /// `require_attrs_select`. If the element contains an error (e.g., `throw`), + /// this function will return that error rather than a lazy thunk. + pub fn require_list_select_idx_strict(&mut self, v: &Value, idx: u32) -> Result> { + let t = self.value_type(v)?; + if t != ValueType::List { + bail!("expected a list, but got a {:?}", t); + } + + // TODO: Remove this bounds checking once https://github.com/NixOS/nix/pull/14030 + // is merged, which will add proper bounds checking to the underlying C API. + // Currently we perform bounds checking in Rust to avoid undefined behavior. + let size = unsafe { check_call!(raw::get_list_size(&mut self.context, v.raw_ptr())) }?; + + if idx >= size { + return Ok(None); + } + let v2 = unsafe { check_call_opt_key!(raw::get_list_byidx( &mut self.context, @@ -383,25 +411,6 @@ impl EvalState { Ok(v2.map(|x| unsafe { Value::new(x) })) } - /// Evaluates, require that the value is a list. - /// Returns the number of elements in the list. - pub fn require_list_size( - &mut self, - v: &Value, - ) -> Result { - let t = self.value_type(v)?; - if t != ValueType::List { - bail!("expected a list, but got a {:?}", t); - } - let ret = unsafe { - check_call!(raw::get_list_size( - &mut self.context, - v.raw_ptr() - )) - }?; - Ok(ret) - } - /// Create a new value containing the passed string. /// Returns a string value without any string context. pub fn new_value_str(&mut self, s: &str) -> Result { @@ -1895,4 +1904,184 @@ mod tests { }) .unwrap(); } + + #[test] + fn eval_state_require_list_select_idx_strict_basic() { + gc_registering_current_thread(|| { + let store = Store::open(None, HashMap::new()).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); + let v = es.eval_from_string("[ 10 20 30 ]", "").unwrap(); + + let elem0 = es.require_list_select_idx_strict(&v, 0).unwrap().unwrap(); + let elem1 = es.require_list_select_idx_strict(&v, 1).unwrap().unwrap(); + let elem2 = es.require_list_select_idx_strict(&v, 2).unwrap().unwrap(); + + assert_eq!(es.require_int(&elem0).unwrap(), 10); + assert_eq!(es.require_int(&elem1).unwrap(), 20); + assert_eq!(es.require_int(&elem2).unwrap(), 30); + }) + .unwrap(); + } + + #[test] + fn eval_state_require_list_select_idx_strict_out_of_bounds() { + gc_registering_current_thread(|| { + let store = Store::open(None, HashMap::new()).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); + let v = es.eval_from_string("[ 1 2 3 ]", "").unwrap(); + + let out_of_bounds = es.require_list_select_idx_strict(&v, 3).unwrap(); + assert!(out_of_bounds.is_none()); + + // Test boundary case - the last valid index + let last_elem = es.require_list_select_idx_strict(&v, 2).unwrap().unwrap(); + assert_eq!(es.require_int(&last_elem).unwrap(), 3); + }) + .unwrap(); + } + + #[test] + fn eval_state_require_list_select_idx_strict_empty_list() { + gc_registering_current_thread(|| { + let store = Store::open(None, HashMap::new()).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); + let v = es.eval_from_string("[ ]", "").unwrap(); + + // Test that the safe version properly handles empty list access + let elem = es.require_list_select_idx_strict(&v, 0).unwrap(); + assert!(elem.is_none()); + + // Verify we can get the size of an empty list + let size = es.require_list_size(&v).unwrap(); + assert_eq!(size, 0); + }) + .unwrap(); + } + + #[test] + fn eval_state_require_list_select_idx_strict_forces_thunk() { + gc_registering_current_thread(|| { + let store = Store::open(None, HashMap::new()).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); + let v = make_thunk(&mut es, "[ 42 ]"); + assert!(es.value_type_unforced(&v).is_none()); + + let elem = es.require_list_select_idx_strict(&v, 0).unwrap().unwrap(); + assert_eq!(es.require_int(&elem).unwrap(), 42); + }) + .unwrap(); + } + + #[test] + fn eval_state_require_list_select_idx_strict_error_element() { + gc_registering_current_thread(|| { + let store = Store::open(None, HashMap::new()).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); + + let v = es + .eval_from_string("[ (1 + 1) (throw \"error\") (3 + 3) ]", "") + .unwrap(); + + let elem0 = es.require_list_select_idx_strict(&v, 0).unwrap().unwrap(); + assert_eq!(es.require_int(&elem0).unwrap(), 2); + + let elem2 = es.require_list_select_idx_strict(&v, 2).unwrap().unwrap(); + assert_eq!(es.require_int(&elem2).unwrap(), 6); + + let elem1_result = es.require_list_select_idx_strict(&v, 1); + match elem1_result { + Ok(_) => panic!("expected an error from throw during selection"), + Err(e) => { + assert!(e.to_string().contains("error")); + } + } + }) + .unwrap(); + } + + #[test] + fn eval_state_require_list_select_idx_strict_wrong_type() { + gc_registering_current_thread(|| { + let store = Store::open(None, HashMap::new()).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); + let v = es.eval_from_string("42", "").unwrap(); + + let r = es.require_list_select_idx_strict(&v, 0); + match r { + Ok(_) => panic!("expected an error"), + Err(e) => { + let err_msg = e.to_string(); + assert!(err_msg.contains("expected a list, but got a")); + } + } + }) + .unwrap(); + } + + #[test] + fn eval_state_require_list_size_basic() { + gc_registering_current_thread(|| { + let store = Store::open(None, HashMap::new()).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); + + let empty = es.eval_from_string("[ ]", "").unwrap(); + assert_eq!(es.require_list_size(&empty).unwrap(), 0); + + let three_elem = es.eval_from_string("[ 1 2 3 ]", "").unwrap(); + assert_eq!(es.require_list_size(&three_elem).unwrap(), 3); + }) + .unwrap(); + } + + #[test] + fn eval_state_require_list_size_forces_thunk() { + gc_registering_current_thread(|| { + let store = Store::open(None, HashMap::new()).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); + let v = make_thunk(&mut es, "[ 1 2 3 4 5 ]"); + assert!(es.value_type_unforced(&v).is_none()); + + let size = es.require_list_size(&v).unwrap(); + assert_eq!(size, 5); + }) + .unwrap(); + } + + #[test] + fn eval_state_require_list_size_lazy_elements() { + gc_registering_current_thread(|| { + let store = Store::open(None, HashMap::new()).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); + + let v = es + .eval_from_string( + "[ (throw \"error1\") (throw \"error2\") (throw \"error3\") ]", + "", + ) + .unwrap(); + + let size = es.require_list_size(&v).unwrap(); + assert_eq!(size, 3); + }) + .unwrap(); + } + + #[test] + fn eval_state_require_list_size_wrong_type() { + gc_registering_current_thread(|| { + let store = Store::open(None, HashMap::new()).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); + let v = es.eval_from_string("\"not a list\"", "").unwrap(); + + let r = es.require_list_size(&v); + match r { + Ok(_) => panic!("expected an error"), + Err(e) => { + let err_msg = e.to_string(); + assert!(err_msg.contains("expected a list, but got a")); + } + } + }) + .unwrap(); + } } From 41c99e02cfe37173365cbb90c07f8de525a7ef0c Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 20 Sep 2025 01:28:48 +0200 Subject: [PATCH 181/306] feat: nix_expr::eval_state::require_list_strict (cherry picked from commit 647854fd767b156c3a5db2da0e5659e040a69534) --- rust/nix-expr/src/eval_state.rs | 126 ++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 188cab2..c9249ad 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -11,6 +11,7 @@ use nix_util::context::Context; use nix_util::string_return::{callback_get_result_string, callback_get_result_string_data}; use nix_util::{check_call, check_call_opt_key, result_string_init}; use std::ffi::{c_char, CString}; +use std::iter::FromIterator; use std::os::raw::c_uint; use std::ptr::{null, null_mut, NonNull}; use std::sync::{Arc, Weak}; @@ -263,6 +264,46 @@ impl EvalState { unsafe { check_call!(raw::get_bool(&mut self.context, v.raw_ptr())) } } + /// Evaluate and require that the passed value is a list. + /// Returns the contained values in the specified container type. + /// This is strict - all list elements will be evaluated. + /// + /// # Examples + /// + /// ```rust,no_run + /// # use nix_expr::value::Value; + /// # use std::collections::VecDeque; + /// # fn example(es: &mut nix_expr::eval_state::EvalState, list_value: &Value) -> anyhow::Result<()> { + /// let vec: Vec = es.require_list_strict(&list_value)?; + /// let deque: VecDeque = es.require_list_strict(&list_value)?; + /// # Ok(()) + /// # } + /// ``` + pub fn require_list_strict(&mut self, value: &Value) -> Result + where + C: FromIterator, + { + let t = self.value_type(value)?; + if t != ValueType::List { + bail!("expected a list, but got a {:?}", t); + } + let size = unsafe { check_call!(raw::get_list_size(&mut self.context, value.raw_ptr())) }?; + + (0..size) + .map(|i| { + let element_ptr = unsafe { + check_call!(raw::get_list_byidx( + &mut self.context, + value.raw_ptr(), + self.eval_state.as_ptr(), + i + )) + }?; + Ok(unsafe { Value::new(element_ptr) }) + }) + .collect() + } + /// Evaluate, and require that the value is an attrset. /// Returns a list of the keys in the attrset. /// @@ -1320,6 +1361,91 @@ mod tests { .unwrap(); } + #[test] + fn eval_state_value_list_strict_empty() { + gc_registering_current_thread(|| { + let store = Store::open(None, HashMap::new()).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); + let v = es.eval_from_string("[]", "").unwrap(); + es.force(&v).unwrap(); + let list: Vec = es.require_list_strict(&v).unwrap(); + assert_eq!(list.len(), 0); + }) + .unwrap(); + } + + #[test] + fn eval_state_value_list_strict_int() { + gc_registering_current_thread(|| { + let store = Store::open(None, HashMap::new()).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); + let v = es.eval_from_string("[42]", "").unwrap(); + es.force(&v).unwrap(); + let list: Vec = es.require_list_strict(&v).unwrap(); + assert_eq!(list.len(), 1); + assert_eq!(es.require_int(&list[0]).unwrap(), 42); + }) + .unwrap(); + } + + #[test] + fn eval_state_value_list_strict_int_bool() { + gc_registering_current_thread(|| { + let store = Store::open(None, HashMap::new()).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); + let v = es.eval_from_string("[42 true]", "").unwrap(); + es.force(&v).unwrap(); + let list: Vec = es.require_list_strict(&v).unwrap(); + assert_eq!(list.len(), 2); + assert_eq!(es.require_int(&list[0]).unwrap(), 42); + assert_eq!(es.require_bool(&list[1]).unwrap(), true); + }) + .unwrap(); + } + + #[test] + fn eval_state_value_list_strict_error() { + gc_registering_current_thread(|| { + let store = Store::open(None, HashMap::new()).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); + let v = es.eval_from_string(r#"[(throw "_evaluated_item_")]"#, "").unwrap(); + es.force(&v).unwrap(); + // This should fail because require_list_strict evaluates all elements + let result: Result, _> = es.require_list_strict(&v); + assert!(result.is_err()); + match result { + Err(error_msg) => { + let error_str = error_msg.to_string(); + assert!(error_str.contains("_evaluated_item_")); + } + Ok(_) => panic!("unexpected success. The item should have been evaluated and its error propagated.") + } + }) + .unwrap(); + } + + #[test] + fn eval_state_value_list_strict_generic_container() { + gc_registering_current_thread(|| { + let store = Store::open(None, HashMap::new()).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); + let v = es.eval_from_string("[1 2 3]", "").unwrap(); + + // Test with Vec + let vec: Vec = es.require_list_strict(&v).unwrap(); + assert_eq!(vec.len(), 3); + + // Test with VecDeque + let deque: std::collections::VecDeque = es.require_list_strict(&v).unwrap(); + assert_eq!(deque.len(), 3); + + // Verify contents are the same + assert_eq!(es.require_int(&vec[0]).unwrap(), 1); + assert_eq!(es.require_int(&deque[0]).unwrap(), 1); + }) + .unwrap(); + } + #[test] fn eval_state_realise_string() { gc_registering_current_thread(|| { From e68ec02d05158b0c08c86cf415adc84111ce0fee Mon Sep 17 00:00:00 2001 From: Tristan Ross Date: Mon, 22 Sep 2025 12:25:23 -0700 Subject: [PATCH 182/306] feat: nix_store::store add real_path function (cherry picked from commit 3e421e39a6adf9eca412368a92c0e459e9b9f628) --- rust/nix-store/src/store.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/rust/nix-store/src/store.rs b/rust/nix-store/src/store.rs index 9ac67ed..0a6b2ae 100644 --- a/rust/nix-store/src/store.rs +++ b/rust/nix-store/src/store.rs @@ -217,6 +217,21 @@ impl Store { } } + #[doc(alias = "nix_store_real_path")] + pub fn real_path(&mut self, path: &StorePath) -> Result { + let mut r = result_string_init!(); + unsafe { + check_call!(raw::store_real_path( + &mut self.context, + self.inner.ptr(), + path.as_ptr(), + Some(callback_get_result_string), + callback_get_result_string_data(&mut r) + )) + }?; + r + } + pub fn weak_ref(&self) -> StoreWeak { StoreWeak { inner: Arc::downgrade(&self.inner), @@ -286,7 +301,9 @@ mod tests { let store_path_string = format!("{store_dir}/rdd4pnr4x9rqc9wgbibhngv217w2xvxl-bash-interactive-5.2p26"); let store_path = store.parse_store_path(store_path_string.as_str()).unwrap(); + let real_store_path = store.real_path(&store_path).unwrap(); assert_eq!(store_path.name().unwrap(), "bash-interactive-5.2p26"); + assert_eq!(real_store_path, store_path_string); } #[test] From 65c9d937cd4ae75540c04b3138d88cd1e55b7358 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 30 Sep 2025 15:50:42 +0200 Subject: [PATCH 183/306] nix-expr: Improve docs ... and fix an error message (cherry picked from commit ec3b5cfb911545ecf756336e792b59c5dd1be9b5) --- rust/nix-c-raw/src/lib.rs | 17 ++ rust/nix-expr/src/eval_state.rs | 517 +++++++++++++++++++++++++++++--- rust/nix-expr/src/value.rs | 31 +- 3 files changed, 514 insertions(+), 51 deletions(-) diff --git a/rust/nix-c-raw/src/lib.rs b/rust/nix-c-raw/src/lib.rs index a38a13a..05c7e41 100644 --- a/rust/nix-c-raw/src/lib.rs +++ b/rust/nix-c-raw/src/lib.rs @@ -1,5 +1,22 @@ +//! Raw bindings to Nix C API +//! +//! This crate contains automatically generated bindings from the Nix C headers. +//! The bindings are generated by bindgen and include C-style naming conventions +//! and documentation comments that don't always conform to Rust standards. +//! +//! Normally you don't have to use this crate directly. +//! Instead use `nix-store` and `nix-expr`. + +// Standard bindgen suppressions for C naming conventions #![allow(non_upper_case_globals)] #![allow(non_camel_case_types)] #![allow(non_snake_case)] +// Rustdoc suppressions for generated C documentation +// The C headers contain Doxygen-style documentation that doesn't translate +// well to Rust's rustdoc format, causing various warnings: +#![allow(rustdoc::broken_intra_doc_links)] // @param[in]/[out] references don't resolve +#![allow(rustdoc::bare_urls)] // C docs may contain unescaped URLs +#![allow(rustdoc::invalid_html_tags)] // Doxygen HTML tags like +#![allow(rustdoc::invalid_codeblock_attributes)] // C code examples may use unsupported attributes include!(concat!(env!("OUT_DIR"), "/bindings.rs")); diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index c9249ad..5c9154a 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -1,3 +1,133 @@ +//! # Nix Expression Evaluation +//! +//! This module provides the core [`EvalState`] type for evaluating Nix expressions +//! and extracting typed values from the results. +//! +//! ## Overview +//! +//! The [`EvalState`] manages the evaluation context for Nix expressions, including: +//! - Expression parsing and evaluation with [`eval_from_string`](EvalState::eval_from_string) +//! - Type-safe value extraction with [`require_*`](EvalState#implementations) methods +//! - Memory management and garbage collection integration +//! - Store integration for derivations and store paths +//! - Custom function creation with [`new_value_primop`](EvalState::new_value_primop) and [`new_value_thunk`](EvalState::new_value_thunk) +//! +//! ### Construction +//! +//! Create an [`EvalState`] using [`EvalState::new`] or [`EvalStateBuilder`] for advanced configuration: +//! +//! ```rust +//! # use nix_expr::eval_state::{EvalState, EvalStateBuilder, test_init, gc_register_my_thread}; +//! # use nix_store::store::Store; +//! # use std::collections::HashMap; +//! # fn example() -> anyhow::Result<()> { +//! # test_init(); let guard = gc_register_my_thread()?; +//! let store = Store::open(None, HashMap::new())?; +//! +//! // Simple creation +//! let mut es = EvalState::new(store.clone(), [])?; +//! +//! // With custom lookup paths +//! let mut es = EvalStateBuilder::new(store)? +//! .lookup_path(["nixpkgs=/path/to/nixpkgs"])? +//! .build()?; +//! # drop(guard); +//! # Ok(()) +//! # } +//! ``` +//! +//! ## Value Extraction +//! +//! All `require_*` methods perform these steps: +//! 1. **Evaluation**: Force evaluation of thunks as needed +//! 2. **Type checking**: Verify the value matches the expected type +//! 3. **Extraction**: Return the typed Rust value or an error +//! +//! Methods with `_strict` in their name also evaluate their return values before returning them. +//! +//! ### Evaluation Strictness +//! +//! - **Lazy methods** (e.g., [`require_list_size`](EvalState::require_list_size)): +//! Evaluate only the structure needed +//! - **Strict methods** (e.g., [`require_list_strict`](EvalState::require_list_strict)): +//! Force full evaluation of all contained values +//! - **Selective methods** (e.g., [`require_list_select_idx_strict`](EvalState::require_list_select_idx_strict)): +//! Evaluate only the accessed elements +//! +//! ## Laziness and Strictness +//! +//! The terms "lazy" and "strict" in this API refer to Nix's [Weak Head Normal Form (WHNF)](https://nix.dev/manual/nix/latest/language/evaluation.html#values) +//! evaluation model, not the kind of deep strictness that is exercised by functions such as `builtins.toJSON` or `builtins.deepSeq`. +//! +//! - **WHNF evaluation**: Values are evaluated just enough to determine their type and basic structure +//! - **Deep evaluation**: All nested values are recursively forced (like `builtins.deepSeq`) +//! +//! For example, a list in WHNF has its length determined but individual elements may remain unevaluated thunks. +//! Methods marked as "strict" in this API force WHNF evaluation of their results, but do not perform deep evaluation +//! of arbitrarily nested structures unless explicitly documented otherwise. +//! +//! ### Thread Safety and Memory Management +//! +//! Before using [`EvalState`] in a thread, register it with the (process memory) garbage collector: +//! +//! ```rust,no_run +//! # use nix_expr::eval_state::{init, gc_register_my_thread, test_init}; +//! # fn example() -> anyhow::Result<()> { +//! # test_init(); // Use test_init() in tests +//! init()?; // Initialize Nix library +//! let guard = gc_register_my_thread()?; // Register thread with GC +//! // Now safe to use EvalState in this thread +//! drop(guard); +//! # Ok(()) +//! # } +//! ``` +//! +//! ## Error Handling +//! +//! Evaluation methods return [`Result`] types. Common error scenarios include: +//! - **Type mismatches**: Expected type doesn't match actual value type +//! - **Evaluation errors**: Nix expressions that throw or have undefined behavior +//! - **Bounds errors**: Out-of-range access for indexed operations +//! +//! ## Examples +//! +//! ```rust +//! use nix_expr::eval_state::{EvalState, test_init, gc_register_my_thread}; +//! use nix_store::store::Store; +//! use std::collections::HashMap; +//! +//! # fn main() -> anyhow::Result<()> { +//! test_init(); // init() in non-test code +//! let guard = gc_register_my_thread()?; +//! +//! let store = Store::open(None, HashMap::new())?; +//! let mut es = EvalState::new(store, [])?; +//! +//! // Evaluate a list expression +//! let list_value = es.eval_from_string("[1 2 3]", "")?; +//! +//! // Check the size (lazy - doesn't evaluate elements) +//! let size = es.require_list_size(&list_value)?; +//! println!("List has {} elements", size); +//! +//! // Access specific elements (evaluates only accessed elements) +//! if let Some(first) = es.require_list_select_idx_strict(&list_value, 0)? { +//! let value = es.require_int(&first)?; +//! println!("First element: {}", value); +//! } +//! +//! // Process all elements (evaluates all elements) +//! let all_elements: Vec<_> = es.require_list_strict(&list_value)?; +//! for element in all_elements { +//! let value = es.require_int(&element)?; +//! println!("Element: {}", value); +//! } +//! +//! drop(guard); +//! # Ok(()) +//! # } +//! ``` + use crate::primop; use crate::value::{Int, Value, ValueType}; use anyhow::Context as _; @@ -25,23 +155,29 @@ lazy_static! { } }; } + pub fn init() -> Result<()> { let x = INIT.as_ref(); match x { Ok(_) => Ok(()), Err(e) => { // Couldn't just clone the error, so we have to print it here. - Err(anyhow::format_err!("nix_libstore_init error: {}", e)) + Err(anyhow::format_err!("nix_expr::init error: {}", e)) } } } +/// A string value with its associated [store paths](https://nix.dev/manual/nix/stable/store/store-path.html). +/// +/// Represents a Nix string with references to store paths. pub struct RealisedString { + /// The string content. pub s: String, + /// Store paths referenced by the string. pub paths: Vec, } -/// A [Weak] reference to an [EvalState] +/// A [Weak] reference to an [EvalState]. pub struct EvalStateWeak { inner: Weak, store: StoreWeak, @@ -65,9 +201,11 @@ struct EvalStateRef { eval_state: NonNull, } impl EvalStateRef { + /// Returns a raw pointer to the underlying EvalState. + /// /// # Safety /// - /// This function is unsafe because it returns a raw pointer. The caller must ensure that the pointer is not used beyond the lifetime of the underlying [raw::EvalState]. + /// The caller must ensure that the pointer is not used beyond the lifetime of the underlying [raw::EvalState]. unsafe fn as_ptr(&self) -> *mut raw::EvalState { self.eval_state.as_ptr() } @@ -79,6 +217,31 @@ impl Drop for EvalStateRef { } } } +/// Builder for configuring and creating an [`EvalState`]. +/// +/// Provides advanced configuration options for evaluation context setup. +/// Use [`EvalState::new`] for simple cases or this builder for custom configuration. +/// +/// # Examples +/// +/// ```rust +/// # use nix_expr::eval_state::{EvalState, EvalStateBuilder, test_init, gc_register_my_thread}; +/// # use nix_store::store::Store; +/// # use std::collections::HashMap; +/// # fn example() -> anyhow::Result<()> { +/// # test_init(); +/// # let guard = gc_register_my_thread()?; +/// let store = Store::open(None, HashMap::new())?; +/// +/// let mut es: EvalState = EvalStateBuilder::new(store)? +/// .lookup_path(["nixpkgs=/path/to/nixpkgs", "home-manager=/path/to/hm"])? +/// .build()?; +/// +/// let value = es.eval_from_string("", /* path display: */ "in-memory")?; +/// # drop(guard); +/// # Ok(()) +/// # } +/// ``` pub struct EvalStateBuilder { eval_state_builder: *mut raw::eval_state_builder, lookup_path: Vec, @@ -92,6 +255,7 @@ impl Drop for EvalStateBuilder { } } impl EvalStateBuilder { + /// Creates a new [`EvalStateBuilder`]. pub fn new(store: Store) -> Result { let mut context = Context::new(); let eval_state_builder = @@ -102,6 +266,7 @@ impl EvalStateBuilder { lookup_path: Vec::new(), }) } + /// Sets the [lookup path](https://nix.dev/manual/nix/latest/language/constructs/lookup-path.html) for Nix expression evaluation. pub fn lookup_path<'a>(mut self, path: impl IntoIterator) -> Result { let lookup_path: Vec = path .into_iter() @@ -114,6 +279,7 @@ impl EvalStateBuilder { self.lookup_path = lookup_path; Ok(self) } + /// Builds the configured [`EvalState`]. pub fn build(&self) -> Result { // Make sure the library is initialized init()?; @@ -148,6 +314,12 @@ impl EvalStateBuilder { context, }) } + /// Returns a raw pointer to the underlying eval state builder. + /// + /// # Safety + /// + /// The caller must ensure that the pointer is not used beyond the lifetime of this builder. + // TODO: This function should be marked `unsafe`. pub fn raw_ptr(&self) -> *mut raw::eval_state_builder { self.eval_state_builder } @@ -159,6 +331,8 @@ pub struct EvalState { pub(crate) context: Context, } impl EvalState { + /// Creates a new EvalState with basic configuration. + /// /// For more options, use [EvalStateBuilder]. pub fn new<'a>(store: Store, lookup_path: impl IntoIterator) -> Result { EvalStateBuilder::new(store)? @@ -166,15 +340,21 @@ impl EvalState { .build() } + /// Returns a raw pointer to the raw Nix C API EvalState. + /// /// # Safety /// - /// This function is unsafe because it returns a raw pointer. The caller must ensure that the pointer is not used beyond the lifetime of this `EvalState`. + /// The caller must ensure that the pointer is not used beyond the lifetime of this `EvalState`. pub unsafe fn raw_ptr(&self) -> *mut raw::EvalState { self.eval_state.as_ptr() } + + /// Returns a reference to the Store that's used for instantiation, import from derivation, etc. pub fn store(&self) -> &Store { &self.store } + + /// Creates a weak reference to this EvalState. pub fn weak_ref(&self) -> EvalStateWeak { EvalStateWeak { inner: Arc::downgrade(&self.eval_state), @@ -189,18 +369,25 @@ impl EvalState { /// # Examples /// /// ``` - /// # use nix_expr::eval_state::EvalState; + /// # use nix_expr::eval_state::{EvalState, test_init, gc_register_my_thread}; /// use nix_store::store::Store; /// use nix_expr::value::Value; + /// use std::collections::HashMap; /// /// # fn main() -> anyhow::Result<()> { - /// # let mut es = EvalState::new(Store::open(None, [])?, [])?; + /// # test_init(); + /// # let guard = gc_register_my_thread()?; + /// # let mut es = EvalState::new(Store::open(None, HashMap::new())?, [])?; /// let v: Value = es.eval_from_string("42", ".")?; /// assert_eq!(es.require_int(&v)?, 42); + /// # drop(guard); /// # Ok(()) /// # } /// ``` #[doc(alias = "nix_expr_eval_from_string")] + #[doc(alias = "parse")] + #[doc(alias = "eval")] + #[doc(alias = "evaluate")] pub fn eval_from_string(&mut self, expr: &str, path: &str) -> Result { let expr_ptr = CString::new(expr).with_context(|| "eval_from_string: expr contains null byte")?; @@ -218,7 +405,14 @@ impl EvalState { Ok(value) } } - /// Try turn any Value into a Value that isn't a Thunk. + + /// Forces [evaluation](https://nix.dev/manual/nix/latest/language/evaluation.html) of a value to [weak head normal form](https://nix.dev/manual/nix/latest/language/evaluation.html?highlight=WHNF#values). + /// + /// Converts [thunks](https://nix.dev/manual/nix/latest/language/evaluation.html#laziness) to their evaluated form. Does not modify already-evaluated values. + /// + /// Does not perform deep evaluation of nested structures. + #[doc(alias = "evaluate")] + #[doc(alias = "strict")] pub fn force(&mut self, v: &Value) -> Result<()> { unsafe { check_call!(raw::value_force( @@ -229,11 +423,34 @@ impl EvalState { }?; Ok(()) } + + /// Returns the type of a value without forcing [evaluation](https://nix.dev/manual/nix/latest/language/evaluation.html). + /// + /// Returns [`None`] if the value is an unevaluated [thunk](https://nix.dev/manual/nix/latest/language/evaluation.html#laziness). + /// + /// Returns [`Some`] if the value is already evaluated. + #[doc(alias = "type_of")] + #[doc(alias = "value_type_lazy")] + #[doc(alias = "nix_get_type")] + #[doc(alias = "get_type")] + #[doc(alias = "nix_value_type")] pub fn value_type_unforced(&mut self, value: &Value) -> Option { let r = unsafe { check_call!(raw::get_type(&mut self.context, value.raw_ptr())) }; // .unwrap(): no reason for this to fail, as it does not evaluate ValueType::from_raw(r.unwrap()) } + /// Returns the [type][`ValueType`] of a value, [forcing][`EvalState::force`] [evaluation](https://nix.dev/manual/nix/latest/language/evaluation.html) if necessary. + /// + /// Forces evaluation if the value is an unevaluated [thunk](https://nix.dev/manual/nix/latest/language/evaluation.html#laziness). + /// + /// Evaluation may fail, producing an [`Err`]. + /// + /// Guarantees a definitive result if [`Ok`], thanks to the language being [pure](https://nix.dev/manual/nix/latest/language/index.html?highlight=pure#nix-language) and [lazy](https://nix.dev/manual/nix/latest/language/index.html?highlight=lazy#nix-language). + #[doc(alias = "type_of")] + #[doc(alias = "value_type_strict")] + #[doc(alias = "nix_get_type")] + #[doc(alias = "get_type")] + #[doc(alias = "nix_value_type_strict")] pub fn value_type(&mut self, value: &Value) -> Result { match self.value_type_unforced(value) { Some(a) => Ok(a), @@ -248,6 +465,35 @@ impl EvalState { } } } + /// Extracts the value from an [integer][`ValueType::Int`] Nix value. + /// + /// Forces [evaluation](https://nix.dev/manual/nix/latest/language/evaluation.html) and verifies the value is an integer. + /// + /// Returns the integer value if successful, or an [`Err`] if evaluation failed or the value is not an integer. + /// + /// # Examples + /// + /// ```rust + /// # use nix_expr::eval_state::{EvalState, test_init, gc_register_my_thread}; + /// # use nix_store::store::Store; + /// # use std::collections::HashMap; + /// # fn example() -> anyhow::Result<()> { + /// # test_init(); + /// # let guard = gc_register_my_thread()?; + /// let store = Store::open(None, HashMap::new())?; + /// let mut es = EvalState::new(store, [])?; + /// + /// let value = es.eval_from_string("42", "")?; + /// let int_val = es.require_int(&value)?; + /// assert_eq!(int_val, 42); + /// # drop(guard); + /// # Ok(()) + /// # } + /// ``` + #[doc(alias = "integer")] + #[doc(alias = "number")] + #[doc(alias = "nix_get_int")] + #[doc(alias = "get_int")] pub fn require_int(&mut self, v: &Value) -> Result { let t = self.value_type(v)?; if t != ValueType::Int { @@ -256,6 +502,14 @@ impl EvalState { unsafe { check_call!(raw::get_int(&mut self.context, v.raw_ptr())) } } + /// Extracts the value from a [boolean][`ValueType::Bool`] Nix value. + /// + /// Forces [evaluation](https://nix.dev/manual/nix/latest/language/evaluation.html) and verifies the value is a boolean. + /// + /// Returns the boolean value if successful, or an [`Err`] if evaluation failed or the value is not a boolean. + #[doc(alias = "boolean")] + #[doc(alias = "nix_get_bool")] + #[doc(alias = "get_bool")] pub fn require_bool(&mut self, v: &Value) -> Result { let t = self.value_type(v)?; if t != ValueType::Bool { @@ -264,21 +518,31 @@ impl EvalState { unsafe { check_call!(raw::get_bool(&mut self.context, v.raw_ptr())) } } - /// Evaluate and require that the passed value is a list. - /// Returns the contained values in the specified container type. - /// This is strict - all list elements will be evaluated. + /// Extracts all elements from a [list][`ValueType::List`] Nix value. + /// + /// Forces [evaluation](https://nix.dev/manual/nix/latest/language/evaluation.html) and verifies the value is a list. + /// + /// Returns the contained values in the specified container type (e.g., [`Vec`], [`VecDeque`][`std::collections::VecDeque`], etc.). + /// + /// This is [strict](https://nix.dev/manual/nix/latest/language/evaluation.html#strictness) - all list elements will be evaluated. /// /// # Examples /// /// ```rust,no_run /// # use nix_expr::value::Value; - /// # use std::collections::VecDeque; + /// # use std::collections::{VecDeque, LinkedList}; /// # fn example(es: &mut nix_expr::eval_state::EvalState, list_value: &Value) -> anyhow::Result<()> { /// let vec: Vec = es.require_list_strict(&list_value)?; /// let deque: VecDeque = es.require_list_strict(&list_value)?; + /// let linked_list = es.require_list_strict::>(&list_value)?; /// # Ok(()) /// # } /// ``` + #[doc(alias = "collect")] + #[doc(alias = "to_vec")] + #[doc(alias = "all")] + #[doc(alias = "nix_get_list_size")] + #[doc(alias = "nix_get_list_byidx")] pub fn require_list_strict(&mut self, value: &Value) -> Result where C: FromIterator, @@ -304,10 +568,14 @@ impl EvalState { .collect() } - /// Evaluate, and require that the value is an attrset. + /// Evaluate, and require that the [`Value`] is a Nix [`ValueType::AttrSet`]. + /// /// Returns a list of the keys in the attrset. /// /// NOTE: this currently implements its own sorting, which probably matches Nix's implementation, but is not guaranteed. + #[doc(alias = "keys")] + #[doc(alias = "attributes")] + #[doc(alias = "fields")] pub fn require_attrs_names(&mut self, v: &Value) -> Result> { self.require_attrs_names_unsorted(v).map(|mut v| { v.sort(); @@ -315,8 +583,11 @@ impl EvalState { }) } - /// For when [require_attrs_names] isn't fast enough. + /// For when [`EvalState::require_attrs_names`] isn't fast enough. + /// /// Only use when it's ok that the keys are returned in an arbitrary order. + #[doc(alias = "keys_unsorted")] + #[doc(alias = "attributes_unsorted")] pub fn require_attrs_names_unsorted(&mut self, v: &Value) -> Result> { let t = self.value_type(v)?; if t != ValueType::AttrSet { @@ -342,7 +613,14 @@ impl EvalState { Ok(attrs) } - /// Evaluate, require that the value is an attrset, and select an attribute by name. + /// Extracts an attribute value from an [attribute set][`ValueType::AttrSet`] Nix value. + /// + /// Forces [evaluation](https://nix.dev/manual/nix/latest/language/evaluation.html) and verifies the value is an attribute set. + /// + /// Returns the attribute value if found, or an [`Err`] if evaluation failed, the attribute doesn't exist, or the value is not an attribute set. + #[doc(alias = "get_attr")] + #[doc(alias = "attribute")] + #[doc(alias = "field")] pub fn require_attrs_select(&mut self, v: &Value, attr_name: &str) -> Result { let t = self.value_type(v)?; if t != ValueType::AttrSet { @@ -374,13 +652,20 @@ impl EvalState { } } - /// Evaluate, require that the value is an attrset, and select an attribute by name. + /// Extracts an optional attribute value from an [attribute set][`ValueType::AttrSet`] Nix value. /// - /// Return `Err(...)` if `v` is not an attrset, or if some other error occurred. + /// Forces [evaluation](https://nix.dev/manual/nix/latest/language/evaluation.html) and verifies the value is an attribute set. /// - /// Return `Ok(None)` if the attribute is not present. + /// Returns [`Err`] if evaluation failed or the value is not an attribute set. /// - /// Return `Ok(Some(value))` if the attribute is present. + /// Returns [`Ok(None)`] if the attribute is not present. + /// + /// Returns [`Ok(Some(value))`] if the attribute is present. + #[doc(alias = "nix_get_attr_byname")] + #[doc(alias = "get_attr_byname")] + #[doc(alias = "get_attr_opt")] + #[doc(alias = "try_get")] + #[doc(alias = "maybe_get")] pub fn require_attrs_select_opt( &mut self, v: &Value, @@ -403,11 +688,16 @@ impl EvalState { Ok(v2.map(|x| unsafe { Value::new(x) })) } - /// Evaluates, require that the value is a list. - /// Returns the number of elements in the list. + /// Returns the number of elements in a [list][`ValueType::List`] Nix value. /// - /// This function only forces evaluation of the list structure itself, - /// not the individual elements. Elements remain as lazy thunks. + /// Forces [evaluation](https://nix.dev/manual/nix/latest/language/evaluation.html) of the list structure and verifies the value is a list. + /// + /// Individual elements remain as lazy [thunks](https://nix.dev/manual/nix/latest/language/evaluation.html#laziness) and are not evaluated. + #[doc(alias = "length")] + #[doc(alias = "count")] + #[doc(alias = "len")] + #[doc(alias = "nix_get_list_size")] + #[doc(alias = "get_list_size")] pub fn require_list_size(&mut self, v: &Value) -> Result { let t = self.value_type(v)?; if t != ValueType::List { @@ -417,15 +707,21 @@ impl EvalState { Ok(ret) } - /// Evaluates, require that the value is a list, and select an element by index. + /// Extracts an element from a [list][`ValueType::List`] Nix value by index. /// - /// Returns `None` if the index is out of bounds. + /// Forces [evaluation](https://nix.dev/manual/nix/latest/language/evaluation.html) and verifies the value is a list. + /// Forces evaluation of the selected element, similar to [`require_attrs_select`]. /// - /// # Strictness + /// Returns [`Ok(Some(value))`] if the element is found. /// - /// This function forces evaluation of the selected element, similar to - /// `require_attrs_select`. If the element contains an error (e.g., `throw`), - /// this function will return that error rather than a lazy thunk. + /// Returns [`Ok(None)`] if the index is out of bounds. + /// + /// Returns [`Err`] if evaluation failed, the element contains an error (e.g., `throw`), or the value is not a list. + #[doc(alias = "get")] + #[doc(alias = "index")] + #[doc(alias = "at")] + #[doc(alias = "nix_get_list_byidx")] + #[doc(alias = "get_list_byidx")] pub fn require_list_select_idx_strict(&mut self, v: &Value, idx: u32) -> Result> { let t = self.value_type(v)?; if t != ValueType::List { @@ -452,8 +748,12 @@ impl EvalState { Ok(v2.map(|x| unsafe { Value::new(x) })) } - /// Create a new value containing the passed string. - /// Returns a string value without any string context. + /// Creates a new [string][`ValueType::String`] Nix value. + /// + /// Returns a string value without any [string context](https://nix.dev/manual/nix/latest/language/string-context.html). + #[doc(alias = "make_string")] + #[doc(alias = "create_string")] + #[doc(alias = "string_value")] pub fn new_value_str(&mut self, s: &str) -> Result { let s = CString::new(s).with_context(|| "new_value_str: contains null byte")?; let v = unsafe { @@ -468,6 +768,11 @@ impl EvalState { Ok(v) } + /// Creates a new [integer][`ValueType::Int`] Nix value. + #[doc(alias = "make_int")] + #[doc(alias = "create_int")] + #[doc(alias = "int_value")] + #[doc(alias = "integer_value")] pub fn new_value_int(&mut self, i: Int) -> Result { let v = unsafe { let value = self.new_value_uninitialized()?; @@ -477,11 +782,15 @@ impl EvalState { Ok(v) } - /// Create a new thunk that will evaluate to the result of the given function. - /// The function will be called with the current EvalState. - /// The function must not return a thunk. + /// Creates a new [thunk](https://nix.dev/manual/nix/latest/language/evaluation.html#laziness) Nix value. + /// + /// The [thunk](https://nix.dev/manual/nix/latest/language/evaluation.html#laziness) will lazily evaluate to the result of the given Rust function when forced. + /// The Rust function will be called with the current [`EvalState`] and must not return a thunk. /// /// The name is shown in stack traces. + #[doc(alias = "make_thunk")] + #[doc(alias = "create_thunk")] + #[doc(alias = "lazy_value")] pub fn new_value_thunk( &mut self, name: &str, @@ -520,7 +829,16 @@ impl EvalState { }; r } - /// NOTE: this will be replaced by two methods, one that also returns the context, and one that checks that the context is empty + /// Extracts a string value from a [string][`ValueType::String`] Nix value. + /// + /// Forces [evaluation](https://nix.dev/manual/nix/latest/language/evaluation.html) and verifies the value is a string. + /// Returns the string value if successful, or an [`Err`] if evaluation failed or the value is not a string. + /// + /// NOTE: this will be replaced by two methods, one that also returns the context, and one that checks that the context is empty. + #[doc(alias = "str")] + #[doc(alias = "text")] + #[doc(alias = "nix_get_string")] + #[doc(alias = "get_string")] pub fn require_string(&mut self, value: &Value) -> Result { let t = self.value_type(value)?; if t != ValueType::String { @@ -528,6 +846,13 @@ impl EvalState { } self.get_string(value) } + /// Realises a [string][`ValueType::String`] Nix value with context information. + /// + /// Forces [evaluation](https://nix.dev/manual/nix/latest/language/evaluation.html), verifies the value is a string, and builds any derivations + /// referenced in the [string context](https://nix.dev/manual/nix/latest/language/string-context.html) if required. + #[doc(alias = "realize_string")] + #[doc(alias = "string_with_context")] + #[doc(alias = "build_string")] pub fn realise_string( &mut self, value: &Value, @@ -578,9 +903,15 @@ impl EvalState { Ok(RealisedString { s, paths }) } - /// Eagerly apply a function to an argument. + /// Applies a function to an argument and returns the result. /// - /// For a lazy version, see [`new_value_apply`][`EvalState::new_value_apply`]. + /// Forces [evaluation](https://nix.dev/manual/nix/latest/language/evaluation.html) of the function application. + /// For a lazy version, see [`new_value_apply`]. + #[doc(alias = "nix_value_call")] + #[doc(alias = "value_call")] + #[doc(alias = "apply")] + #[doc(alias = "invoke")] + #[doc(alias = "execute")] pub fn call(&mut self, f: Value, a: Value) -> Result { let value = self.new_value_uninitialized()?; unsafe { @@ -595,8 +926,49 @@ impl EvalState { Ok(value) } - /// Eagerly apply a function with multiple curried arguments. + /// Apply a sequence of [function applications](https://nix.dev/manual/nix/latest/language/operators.html#function-application). + /// + /// When argument `f` is a curried function, this applies each argument in sequence. + /// Equivalent to the Nix expression `f arg1 arg2 arg3`. + /// + /// Returns a [`Value`] in at least weak head normal form if successful. + /// + /// Returns an [`Err`] + /// - if `f` did not evaluate to a function + /// - if `f arg1` had any problems + /// - if `f arg1` did not evaluate to a function (for `(f arg1) arg2`) + /// - etc + /// + /// # Examples + /// + /// ```rust + /// # use nix_expr::eval_state::{EvalState, test_init, gc_register_my_thread}; + /// # use nix_store::store::Store; + /// # use std::collections::HashMap; + /// # fn example() -> anyhow::Result<()> { + /// # test_init(); + /// # let guard = gc_register_my_thread()?; + /// let store = Store::open(None, HashMap::new())?; + /// let mut es = EvalState::new(store, [])?; + /// + /// // Create a curried function: x: y: x + y + /// let f = es.eval_from_string("x: y: x + y", "")?; + /// let arg1 = es.eval_from_string("5", "")?; + /// let arg2 = es.eval_from_string("3", "")?; + /// + /// // Equivalent to: (x: y: x + y) 5 3 + /// let result = es.call_multi(&f, &[arg1, arg2])?; + /// let value = es.require_int(&result)?; + /// assert_eq!(value, 8); + /// # drop(guard); + /// # Ok(()) + /// # } + /// ``` #[doc(alias = "nix_value_call_multi")] + #[doc(alias = "value_call_multi")] + #[doc(alias = "apply_multi")] + #[doc(alias = "curry")] + #[doc(alias = "call_with_args")] pub fn call_multi(&mut self, f: &Value, args: &[Value]) -> Result { let value = self.new_value_uninitialized()?; unsafe { @@ -613,9 +985,13 @@ impl EvalState { Ok(value) } - /// Apply a function to an argument, but don't evaluate the result just yet. + /// Applies a function to an argument lazily, creating a [thunk](https://nix.dev/manual/nix/latest/language/evaluation.html#laziness). /// - /// For an eager version, see [`call`][`EvalState::call`]. + /// Does not force [evaluation](https://nix.dev/manual/nix/latest/language/evaluation.html) of the function application. + /// For an eager version, see [`call`]. + #[doc(alias = "lazy_apply")] + #[doc(alias = "thunk_apply")] + #[doc(alias = "defer_call")] pub fn new_value_apply(&mut self, f: &Value, a: &Value) -> Result { let value = self.new_value_uninitialized()?; unsafe { @@ -639,10 +1015,14 @@ impl EvalState { } } - /// Create a new Nix function that is implemented by a Rust function. + /// Creates a new [function][`ValueType::Function`] Nix value implemented by a Rust function. + /// /// This is also known as a "primop" in Nix, short for primitive operation. - /// Most of the `builtins.*` values are examples of primops, but - /// `new_value_primop` does not affect `builtins`. + /// Most of the `builtins.*` values are examples of primops, but this function + /// does not affect `builtins`. + #[doc(alias = "make_primop")] + #[doc(alias = "create_function")] + #[doc(alias = "builtin")] pub fn new_value_primop(&mut self, primop: primop::PrimOp) -> Result { let value = self.new_value_uninitialized()?; unsafe { @@ -655,6 +1035,46 @@ impl EvalState { Ok(value) } + /// Creates a new [attribute set][`ValueType::Attrs`] Nix value from an iterator of name-value pairs. + /// + /// Accepts any iterator that yields `(String, Value)` pairs and has an exact size. + /// Common usage includes [`Vec`], [`HashMap`], and array literals. + /// + /// # Examples + /// + /// ```rust + /// # use nix_expr::eval_state::{EvalState, test_init, gc_register_my_thread}; + /// # use nix_store::store::Store; + /// # use std::collections::HashMap; + /// # fn example() -> anyhow::Result<()> { + /// # test_init(); + /// # let guard = gc_register_my_thread()?; + /// let store = Store::open(None, HashMap::new())?; + /// let mut es = EvalState::new(store, [])?; + /// let a = es.new_value_int(1)?; + /// let b = es.new_value_int(2)?; + /// let c = es.new_value_int(3)?; + /// let d = es.new_value_int(4)?; + /// + /// // From array + /// let attrs1 = es.new_value_attrs([ + /// ("x".to_string(), a), + /// ("y".to_string(), b) + /// ])?; + /// + /// // From HashMap + /// let mut map = HashMap::new(); + /// map.insert("foo".to_string(), c); + /// map.insert("bar".to_string(), d); + /// let attrs2 = es.new_value_attrs(map)?; + /// # drop(guard); + /// # Ok(()) + /// # } + /// ``` + #[doc(alias = "make_attrs")] + #[doc(alias = "create_attrset")] + #[doc(alias = "object")] + #[doc(alias = "record")] pub fn new_value_attrs(&mut self, attrs: I) -> Result where I: IntoIterator, @@ -687,6 +1107,7 @@ impl EvalState { } } +// Internal RAII helper; could be refactored and made pub struct BindingsBuilder { ptr: *mut raw::BindingsBuilder, } @@ -710,12 +1131,19 @@ impl BindingsBuilder { } } +/// Triggers garbage collection immediately. +#[doc(alias = "garbage_collect")] +#[doc(alias = "collect")] +#[doc(alias = "gc")] pub fn gc_now() { unsafe { raw::gc_now(); } } +/// RAII guard for thread registration with the garbage collector. +/// +/// Automatically unregisters the thread when dropped. pub struct ThreadRegistrationGuard { must_unregister: bool, } @@ -743,6 +1171,9 @@ fn gc_register_my_thread_do_it() -> Result<()> { } } +#[doc(alias = "register_thread")] +#[doc(alias = "thread_setup")] +#[doc(alias = "gc_register")] pub fn gc_register_my_thread() -> Result { init()?; unsafe { @@ -771,6 +1202,8 @@ impl Clone for EvalState { /// Initialize the Nix library for testing. This includes some modifications to the Nix settings, that must not be used in production. /// Use at your own peril, in rust test suites. +#[doc(alias = "test_initialize")] +#[doc(alias = "test_setup")] pub fn test_init() { init().unwrap(); diff --git a/rust/nix-expr/src/value.rs b/rust/nix-expr/src/value.rs index bfa7011..14cda88 100644 --- a/rust/nix-expr/src/value.rs +++ b/rust/nix-expr/src/value.rs @@ -8,28 +8,41 @@ use std::ptr::{null_mut, NonNull}; pub type Int = i64; -/// The type of a value (or thunk) +/// The type discriminator of a [`Value`] that has successfully evaluated to at least [weak head normal form](https://nix.dev/manual/nix/latest/language/evaluation.html?highlight=WHNF#values). +/// +/// Typically acquired with [`EvalState::value_type`][`crate::eval_state::EvalState::value_type`] #[derive(Eq, PartialEq, Debug)] pub enum ValueType { + /// A Nix [attribute set](https://nix.dev/manual/nix/stable/language/types.html#type-attrs) AttrSet, + /// A Nix [boolean](https://nix.dev/manual/nix/stable/language/types.html#type-bool) Bool, + /// A Nix external value (mostly-opaque value for plugins, linked applications) External, + /// A Nix [float](https://nix.dev/manual/nix/stable/language/types.html#type-float) Float, + /// A Nix [function](https://nix.dev/manual/nix/stable/language/types.html#type-function) Function, + /// A Nix [integer](https://nix.dev/manual/nix/stable/language/types.html#type-int) Int, + /// A Nix [list](https://nix.dev/manual/nix/stable/language/types.html#type-list) List, + /// A Nix [`null`](https://nix.dev/manual/nix/stable/language/types.html#type-null) Null, + /// A Nix [path value](https://nix.dev/manual/nix/stable/language/types.html#type-path) Path, + /// A Nix [string](https://nix.dev/manual/nix/stable/language/types.html#type-string) String, + /// An unknown value, presumably from a new, partially unsupported version of Nix Unknown, } impl ValueType { - /// Convert a raw value type to a `ValueType`. + /// Convert a raw value type to a [`ValueType`]. /// - /// Return `None` if the Value is still a thunk (i.e. not yet evaluated). + /// Return [`None`] if the Value is still a thunk (i.e. not yet evaluated). /// - /// Return `Some(ValueType::Unknown)` if the value type is not recognized. + /// Return `Some(`[`ValueType::Unknown`]`)` if the value type is not recognized. pub(crate) fn from_raw(raw: raw::ValueType) -> Option { match raw { raw::ValueType_NIX_TYPE_ATTRS => Some(ValueType::AttrSet), @@ -51,14 +64,14 @@ impl ValueType { } } -/* A pointer to a value or thunk, to be used with EvalState methods. */ +/// A pointer to a [value](https://nix.dev/manual/nix/latest/language/types.html) or [thunk](https://nix.dev/manual/nix/2.31/language/evaluation.html?highlight=thunk#laziness), to be used with [`EvalState`][`crate::eval_state::EvalState`] methods. pub struct Value { inner: NonNull, } impl Value { - /// Take ownership of a new Value. + /// Take ownership of a new [`Value`]. /// - /// This does not call `nix_gc_incref`, but does call `nix_gc_decref` when dropped. + /// This does not call [`nix_c_raw::gc_incref`], but does call [`nix_c_raw::nix_gc_decref`] when [dropped][`Drop`]. /// /// # Safety /// @@ -69,9 +82,9 @@ impl Value { } } - /// Borrow a reference to a Value. + /// Borrow a reference to a [`Value`]. /// - /// This calls `nix_gc_incref`, and the returned Value will call `nix_gc_decref` when dropped. + /// This calls [`nix_c_raw::value_incref`], and the returned Value will call [`nix_c_raw::value_decref`] when dropped. /// /// # Safety /// From 6c0fb20a9ab222678b68f8efeb33eb902492ad7a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 3 Sep 2024 15:03:18 +0200 Subject: [PATCH 184/306] dev: Recommend against clangd vscode extension It is of no use for our single header file, and it hasn't been set up. Stop loading it, and stop it from complaining. (cherry picked from commit 70b4adad231f3f6ba4d182aa64b9b00d0d4be280) --- .vscode/extensions.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 38d06b1..cc8f965 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -2,5 +2,8 @@ "recommendations": [ "mkhl.direnv", "rust-lang.rust-analyzer" + ], + "unwantedRecommendations": [ + "llvm-vs-code-extensions.vscode-clangd" ] } From 206a4ca7c5fcef331bfd5eb4131baebbe1d58dca Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 23 Oct 2024 16:46:56 +0200 Subject: [PATCH 185/306] maint: Auto-update flake (cherry picked from commit 975d1a7193f7ed1ee8456b909637fb078046db41) --- dev/flake-module.nix | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/dev/flake-module.nix b/dev/flake-module.nix index 7f9da9c..a79871a 100644 --- a/dev/flake-module.nix +++ b/dev/flake-module.nix @@ -90,5 +90,15 @@ { ciSystems = [ "x86_64-linux" ]; }; + hercules-ci.flake-update = { + enable = true; + baseMerge.enable = true; + autoMergeMethod = "merge"; + when = { dayOfMonth = 2; }; + flakes = { + "." = { }; + "dev" = { }; + }; + }; flake = { }; } From 4b13929db3323b7bfac3b5ce4bcff82d8524d2cc Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 4 Oct 2025 02:03:25 +0200 Subject: [PATCH 186/306] maint: Update first of the month --- dev/flake-module.nix | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dev/flake-module.nix b/dev/flake-module.nix index a79871a..38583c1 100644 --- a/dev/flake-module.nix +++ b/dev/flake-module.nix @@ -94,7 +94,9 @@ enable = true; baseMerge.enable = true; autoMergeMethod = "merge"; - when = { dayOfMonth = 2; }; + when = { + dayOfMonth = 1; + }; flakes = { "." = { }; "dev" = { }; From b3171585d168d635a038d803cc6451f1eb3bf23e Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 4 Oct 2025 02:44:22 +0200 Subject: [PATCH 187/306] Rename crates nix- -> nix-bindings- This way, the crates can be published without interfering with potential future non-bindings `nix-` crates, if Nix proper wants to have native rust code, for instance. --- rust/Cargo.lock | 1804 +---------------- rust/Cargo.toml | 12 +- .../Cargo.toml | 2 +- .../README.md | 4 +- .../build.rs | 0 .../include/nix-c-raw.h | 0 .../src/lib.rs | 0 rust/nix-bindings-expr/Cargo.toml | 18 + .../src/eval_state.rs | 56 +- .../src/lib.rs | 0 .../src/primop.rs | 4 +- .../src/value.rs | 4 +- .../src/value/__private.rs | 2 +- rust/nix-bindings-fetchers/Cargo.toml | 17 + .../src/lib.rs | 4 +- rust/nix-bindings-flake/Cargo.toml | 20 + .../src/lib.rs | 26 +- .../Cargo.toml | 6 +- .../build.rs | 0 .../src/lib.rs | 0 .../src/path.rs | 6 +- .../src/store.rs | 8 +- .../Cargo.toml | 4 +- .../src/context.rs | 4 +- .../src/lib.rs | 0 .../src/settings.rs | 2 +- .../src/string_return.rs | 4 +- rust/nix-expr/Cargo.toml | 18 - rust/nix-fetchers/Cargo.toml | 17 - rust/nix-flake/Cargo.toml | 20 - 30 files changed, 209 insertions(+), 1853 deletions(-) rename rust/{nix-c-raw => nix-bindings-bindgen-raw}/Cargo.toml (83%) rename rust/{nix-c-raw => nix-bindings-bindgen-raw}/README.md (79%) rename rust/{nix-c-raw => nix-bindings-bindgen-raw}/build.rs (100%) rename rust/{nix-c-raw => nix-bindings-bindgen-raw}/include/nix-c-raw.h (100%) rename rust/{nix-c-raw => nix-bindings-bindgen-raw}/src/lib.rs (100%) create mode 100644 rust/nix-bindings-expr/Cargo.toml rename rust/{nix-expr => nix-bindings-expr}/src/eval_state.rs (98%) rename rust/{nix-expr => nix-bindings-expr}/src/lib.rs (100%) rename rust/{nix-expr => nix-bindings-expr}/src/primop.rs (98%) rename rust/{nix-expr => nix-bindings-expr}/src/value.rs (98%) rename rust/{nix-expr => nix-bindings-expr}/src/value/__private.rs (90%) create mode 100644 rust/nix-bindings-fetchers/Cargo.toml rename rust/{nix-fetchers => nix-bindings-fetchers}/src/lib.rs (91%) create mode 100644 rust/nix-bindings-flake/Cargo.toml rename rust/{nix-flake => nix-bindings-flake}/src/lib.rs (95%) rename rust/{nix-store => nix-bindings-store}/Cargo.toml (57%) rename rust/{nix-store => nix-bindings-store}/build.rs (100%) rename rust/{nix-store => nix-bindings-store}/src/lib.rs (100%) rename rust/{nix-store => nix-bindings-store}/src/path.rs (96%) rename rust/{nix-store => nix-bindings-store}/src/store.rs (97%) rename rust/{nix-util => nix-bindings-util}/Cargo.toml (59%) rename rust/{nix-util => nix-bindings-util}/src/context.rs (97%) rename rust/{nix-util => nix-bindings-util}/src/lib.rs (100%) rename rust/{nix-util => nix-bindings-util}/src/settings.rs (98%) rename rust/{nix-util => nix-bindings-util}/src/string_return.rs (97%) delete mode 100644 rust/nix-expr/Cargo.toml delete mode 100644 rust/nix-fetchers/Cargo.toml delete mode 100644 rust/nix-flake/Cargo.toml diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 62df232..54f6191 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -2,21 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "addr2line" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - [[package]] name = "aho-corasick" version = "1.1.3" @@ -26,142 +11,11 @@ dependencies = [ "memchr", ] -[[package]] -name = "allocator-api2" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" - -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "ansi-parser" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c43e7fd8284f025d0bd143c2855618ecdf697db55bde39211e5c9faec7669173" -dependencies = [ - "heapless", - "nom", -] - -[[package]] -name = "anstream" -version = "0.6.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" - -[[package]] -name = "anstyle-parse" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" -dependencies = [ - "windows-sys 0.60.2", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" -dependencies = [ - "anstyle", - "once_cell_polyfill", - "windows-sys 0.60.2", -] - [[package]] name = "anyhow" -version = "1.0.99" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" - -[[package]] -name = "async-channel" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" -dependencies = [ - "concurrent-queue", - "event-listener-strategy", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-trait" -version = "0.1.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "autocfg" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" - -[[package]] -name = "backtrace" -version = "0.3.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-targets 0.52.6", -] - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" [[package]] name = "bindgen" @@ -172,7 +26,7 @@ dependencies = [ "bitflags", "cexpr", "clang-sys", - "itertools 0.12.1", + "itertools", "lazy_static", "lazycell", "log", @@ -188,51 +42,9 @@ dependencies = [ [[package]] name = "bitflags" -version = "2.9.3" +version = "2.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34efbcccd345379ca2868b2b2c9d3782e9cc58ba87bc7d79d5b53d9c9ae6f25d" - -[[package]] -name = "bumpalo" -version = "3.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "bytes" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" - -[[package]] -name = "cassowary" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" - -[[package]] -name = "castaway" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" -dependencies = [ - "rustversion", -] - -[[package]] -name = "cc" -version = "1.2.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42bc4aea80032b7bf409b0bc7ccad88853858911b7713a8062fdc0623867bedc" -dependencies = [ - "shlex", -] +checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" [[package]] name = "cexpr" @@ -249,27 +61,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - -[[package]] -name = "chrono" -version = "0.4.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "js-sys", - "num-traits", - "serde", - "wasm-bindgen", - "windows-link", -] - [[package]] name = "clang-sys" version = "1.8.1" @@ -281,140 +72,6 @@ dependencies = [ "libloading", ] -[[package]] -name = "clap" -version = "4.5.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc0e74a703892159f5ae7d3aac52c8e6c392f5ae5f359c70b5881d60aaac318" -dependencies = [ - "clap_builder", - "clap_derive", -] - -[[package]] -name = "clap-markdown" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2a2617956a06d4885b490697b5307ebb09fec10b088afc18c81762d848c2339" -dependencies = [ - "clap", -] - -[[package]] -name = "clap_builder" -version = "4.5.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3e7f4214277f3c7aa526a59dd3fbe306a370daee1f8b7b8c987069cd8e888a8" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_complete" -version = "4.5.57" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d9501bd3f5f09f7bbee01da9a511073ed30a80cd7a509f1214bb74eadea71ad" -dependencies = [ - "clap", -] - -[[package]] -name = "clap_derive" -version = "4.5.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14cb31bb0a7d536caef2639baa7fad459e15c3144efefa6dbd1c84562c4739f6" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "clap_lex" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" - -[[package]] -name = "clap_mangen" -version = "0.2.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b4c3c54b30f0d9adcb47f25f61fcce35c4dd8916638c6b82fbd5f4fb4179e2" -dependencies = [ - "clap", - "roff", -] - -[[package]] -name = "colorchoice" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" - -[[package]] -name = "compact_str" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b79c4069c6cad78e2e0cdfcbd26275770669fb39fd308a752dc110e83b9af32" -dependencies = [ - "castaway", - "cfg-if", - "itoa", - "rustversion", - "ryu", - "static_assertions", -] - -[[package]] -name = "concurrent-queue" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "crossbeam-utils" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" - -[[package]] -name = "crossterm" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" -dependencies = [ - "bitflags", - "crossterm_winapi", - "mio", - "parking_lot", - "rustix 0.38.44", - "signal-hook", - "signal-hook-mio", - "winapi", -] - -[[package]] -name = "crossterm_winapi" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" -dependencies = [ - "winapi", -] - [[package]] name = "cstr" version = "0.2.12" @@ -435,112 +92,20 @@ dependencies = [ "syn", ] -[[package]] -name = "ctrlc" -version = "3.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46f93780a459b7d656ef7f071fe699c4d3d2cb201c4b24d085b6ddc505276e73" -dependencies = [ - "nix 0.30.1", - "windows-sys 0.59.0", -] - -[[package]] -name = "darling" -version = "0.20.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.20.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn", -] - -[[package]] -name = "darling_macro" -version = "0.20.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" -dependencies = [ - "darling_core", - "quote", - "syn", -] - -[[package]] -name = "dashmap" -version = "6.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" -dependencies = [ - "cfg-if", - "crossbeam-utils", - "hashbrown 0.14.5", - "lock_api", - "once_cell", - "parking_lot_core", -] - -[[package]] -name = "dyn-clone" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" - [[package]] name = "either" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - [[package]] name = "errno" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.60.2", -] - -[[package]] -name = "event-listener" -version = "5.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener-strategy" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" -dependencies = [ - "event-listener", - "pin-project-lite", + "windows-sys 0.61.1", ] [[package]] @@ -549,41 +114,6 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" -[[package]] -name = "fd-lock" -version = "4.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce92ff622d6dadf7349484f42c93271a0d49b7cc4d466a936405bacbe10aa78" -dependencies = [ - "cfg-if", - "rustix 1.0.8", - "windows-sys 0.59.0", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - -[[package]] -name = "fuchsia-cprng" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" - -[[package]] -name = "futures-core" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" - [[package]] name = "getrandom" version = "0.3.3" @@ -593,63 +123,15 @@ dependencies = [ "cfg-if", "libc", "r-efi", - "wasi 0.14.2+wasi-0.2.4", + "wasi", ] -[[package]] -name = "gimli" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" - [[package]] name = "glob" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" -[[package]] -name = "hash32" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" -dependencies = [ - "byteorder", -] - -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" - -[[package]] -name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" -dependencies = [ - "allocator-api2", - "equivalent", - "foldhash", -] - -[[package]] -name = "heapless" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" -dependencies = [ - "hash32", - "stable_deref_trait", -] - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - [[package]] name = "home" version = "0.5.11" @@ -659,72 +141,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "iana-time-zone" -version = "0.1.63" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "indoc" -version = "2.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" - -[[package]] -name = "instability" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435d80800b936787d62688c927b6490e887c7ef5ff9ce922c6c6050fca75eb9a" -dependencies = [ - "darling", - "indoc", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "io-uring" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" -dependencies = [ - "bitflags", - "cfg-if", - "libc", -] - -[[package]] -name = "is_terminal_polyfill" -version = "1.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" - [[package]] name = "itertools" version = "0.12.1" @@ -734,53 +150,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" - -[[package]] -name = "js-sys" -version = "0.3.77" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" -dependencies = [ - "once_cell", - "wasm-bindgen", -] - -[[package]] -name = "json-patch" -version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "159294d661a039f7644cea7e4d844e6b25aaf71c1ffe9d73a96d768c24b0faf4" -dependencies = [ - "jsonptr", - "serde", - "serde_json", - "thiserror 1.0.69", -] - -[[package]] -name = "jsonptr" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5a3cc660ba5d72bce0b3bb295bf20847ccbb40fd423f3f05b61273672e561fe" -dependencies = [ - "serde", - "serde_json", -] - [[package]] name = "lazy_static" version = "1.5.0" @@ -795,18 +164,18 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.175" +version = "0.2.176" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" +checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" [[package]] name = "libloading" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" dependencies = [ "cfg-if", - "windows-targets 0.53.3", + "windows-link", ] [[package]] @@ -817,40 +186,21 @@ checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "linux-raw-sys" -version = "0.9.4" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" - -[[package]] -name = "lock_api" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" -dependencies = [ - "autocfg", - "scopeguard", -] +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "log" -version = "0.4.27" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" - -[[package]] -name = "lru" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" -dependencies = [ - "hashbrown 0.15.5", -] +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" [[package]] name = "memchr" -version = "2.7.5" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "minimal-lexical" @@ -859,52 +209,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", -] - -[[package]] -name = "mio" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" -dependencies = [ - "libc", - "log", - "wasi 0.11.1+wasi-snapshot-preview1", - "windows-sys 0.59.0", -] - -[[package]] -name = "nix" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" -dependencies = [ - "bitflags", - "cfg-if", - "cfg_aliases", - "libc", -] - -[[package]] -name = "nix" -version = "0.30.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" -dependencies = [ - "bitflags", - "cfg-if", - "cfg_aliases", - "libc", -] - -[[package]] -name = "nix-c-raw" +name = "nix-bindings-bindgen-raw" version = "0.1.0" dependencies = [ "bindgen", @@ -912,175 +217,66 @@ dependencies = [ ] [[package]] -name = "nix-expr" +name = "nix-bindings-expr" version = "0.1.0" dependencies = [ "anyhow", "cstr", "ctor", "lazy_static", - "nix-c-raw", - "nix-store", - "nix-util", + "nix-bindings-bindgen-raw", + "nix-bindings-store", + "nix-bindings-util", "tempfile", ] [[package]] -name = "nix-fetchers" +name = "nix-bindings-fetchers" version = "0.1.0" dependencies = [ "anyhow", "cstr", "ctor", - "nix-c-raw", - "nix-store", - "nix-util", + "nix-bindings-bindgen-raw", + "nix-bindings-store", + "nix-bindings-util", "tempfile", ] [[package]] -name = "nix-flake" +name = "nix-bindings-flake" version = "0.1.0" dependencies = [ "anyhow", "cstr", "ctor", "lazy_static", - "nix-c-raw", - "nix-expr", - "nix-fetchers", - "nix-store", - "nix-util", + "nix-bindings-bindgen-raw", + "nix-bindings-expr", + "nix-bindings-fetchers", + "nix-bindings-store", + "nix-bindings-util", "tempfile", ] [[package]] -name = "nix-store" +name = "nix-bindings-store" version = "0.1.0" dependencies = [ "anyhow", "lazy_static", - "nix-c-raw", - "nix-util", + "nix-bindings-bindgen-raw", + "nix-bindings-util", "pkg-config", ] [[package]] -name = "nix-util" +name = "nix-bindings-util" version = "0.1.0" dependencies = [ "anyhow", "ctor", - "nix-c-raw", -] - -[[package]] -name = "nixops4" -version = "0.1.0" -dependencies = [ - "ansi-parser", - "anyhow", - "async-trait", - "clap", - "clap-markdown", - "clap_complete", - "clap_mangen", - "crossterm", - "ctrlc", - "json-patch", - "nix 0.29.0", - "nixops4-core", - "nixops4-resource", - "nixops4-resource-runner", - "pubsub-rs", - "ratatui", - "serde", - "serde_json", - "tokio", - "tracing", - "tracing-subscriber", - "tracing-tunnel", -] - -[[package]] -name = "nixops4-core" -version = "0.1.0" -dependencies = [ - "anyhow", - "serde", - "serde_json", -] - -[[package]] -name = "nixops4-eval" -version = "0.1.0" -dependencies = [ - "anyhow", - "async-trait", - "base64", - "cstr", - "ctor", - "ctrlc", - "nix-expr", - "nix-fetchers", - "nix-flake", - "nix-store", - "nix-util", - "nixops4-core", - "serde", - "serde_json", - "tempdir", - "tokio", - "tracing", - "tracing-tunnel", -] - -[[package]] -name = "nixops4-resource" -version = "0.1.0" -dependencies = [ - "anyhow", - "chrono", - "json-patch", - "nix 0.29.0", - "prettyplease", - "schemars", - "serde", - "serde_json", - "syn", - "typify", -] - -[[package]] -name = "nixops4-resource-runner" -version = "0.1.0" -dependencies = [ - "anyhow", - "clap", - "clap-markdown", - "clap_complete", - "clap_derive", - "clap_mangen", - "json-patch", - "nixops4-resource", - "serde", - "serde_json", - "tokio", - "tracing", -] - -[[package]] -name = "nixops4-resources-local" -version = "0.1.0" -dependencies = [ - "anyhow", - "chrono", - "fd-lock", - "json-patch", - "nixops4-resource", - "serde", - "serde_json", - "tempfile", + "nix-bindings-bindgen-raw", ] [[package]] @@ -1093,93 +289,12 @@ dependencies = [ "minimal-lexical", ] -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - -[[package]] -name = "object" -version = "0.36.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" -dependencies = [ - "memchr", -] - [[package]] name = "once_cell" version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" -[[package]] -name = "once_cell_polyfill" -version = "1.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" - -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - -[[package]] -name = "parking" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" - -[[package]] -name = "parking_lot" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets 0.52.6", -] - -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - -[[package]] -name = "pin-project-lite" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" - [[package]] name = "pkg-config" version = "0.3.32" @@ -1205,22 +320,11 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "pubsub-rs" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cba7b19d46ae57a9a6307681961fd24ab5e2794e4cc7e2e711cacfa733f610b" -dependencies = [ - "async-channel", - "dashmap", - "tokio", -] - [[package]] name = "quote" -version = "1.0.40" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" dependencies = [ "proc-macro2", ] @@ -1231,78 +335,11 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" -[[package]] -name = "rand" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" -dependencies = [ - "fuchsia-cprng", - "libc", - "rand_core 0.3.1", - "rdrand", - "winapi", -] - -[[package]] -name = "rand_core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -dependencies = [ - "rand_core 0.4.2", -] - -[[package]] -name = "rand_core" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" - -[[package]] -name = "ratatui" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b" -dependencies = [ - "bitflags", - "cassowary", - "compact_str", - "crossterm", - "indoc", - "instability", - "itertools 0.13.0", - "lru", - "paste", - "strum", - "unicode-segmentation", - "unicode-truncate", - "unicode-width 0.2.0", -] - -[[package]] -name = "rdrand" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "redox_syscall" -version = "0.5.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" -dependencies = [ - "bitflags", -] - [[package]] name = "regex" -version = "1.11.2" +version = "1.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" +checksum = "8b5288124840bee7b386bc413c487869b360b2b4ec421ea56425128692f2a82c" dependencies = [ "aho-corasick", "memchr", @@ -1312,9 +349,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" +checksum = "833eb9ce86d40ef33cb1306d8accf7bc8ec2bfea4355cbdebb3df68b40925cad" dependencies = [ "aho-corasick", "memchr", @@ -1327,37 +364,6 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" -[[package]] -name = "regress" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145bb27393fe455dd64d6cbc8d059adfa392590a45eadf079c01b11857e7b010" -dependencies = [ - "hashbrown 0.15.5", - "memchr", -] - -[[package]] -name = "remove_dir_all" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] - -[[package]] -name = "roff" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88f8660c1ff60292143c98d08fc6e2f654d722db50410e3f3797d40baaf9d8f3" - -[[package]] -name = "rustc-demangle" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" - [[package]] name = "rustc-hash" version = "1.1.0" @@ -1379,130 +385,15 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.8" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ "bitflags", "errno", "libc", - "linux-raw-sys 0.9.4", - "windows-sys 0.60.2", -] - -[[package]] -name = "rustversion" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" - -[[package]] -name = "ryu" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" - -[[package]] -name = "schemars" -version = "0.8.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" -dependencies = [ - "dyn-clone", - "schemars_derive", - "serde", - "serde_json", -] - -[[package]] -name = "schemars_derive" -version = "0.8.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d" -dependencies = [ - "proc-macro2", - "quote", - "serde_derive_internals", - "syn", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "semver" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" -dependencies = [ - "serde", -] - -[[package]] -name = "serde" -version = "1.0.219" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.219" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_derive_internals" -version = "0.29.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.143" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" -dependencies = [ - "itoa", - "memchr", - "ryu", - "serde", -] - -[[package]] -name = "serde_tokenstream" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64060d864397305347a78851c51588fd283767e7e7589829e8121d65512340f1" -dependencies = [ - "proc-macro2", - "quote", - "serde", - "syn", -] - -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", + "linux-raw-sys 0.11.0", + "windows-sys 0.61.1", ] [[package]] @@ -1511,98 +402,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" -[[package]] -name = "signal-hook" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" -dependencies = [ - "libc", - "signal-hook-registry", -] - -[[package]] -name = "signal-hook-mio" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" -dependencies = [ - "libc", - "mio", - "signal-hook", -] - -[[package]] -name = "signal-hook-registry" -version = "1.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" -dependencies = [ - "libc", -] - -[[package]] -name = "slab" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" - -[[package]] -name = "smallvec" -version = "1.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" - -[[package]] -name = "socket2" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" -dependencies = [ - "libc", - "windows-sys 0.59.0", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "strum" -version = "0.26.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" -dependencies = [ - "strum_macros", -] - -[[package]] -name = "strum_macros" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "rustversion", - "syn", -] - [[package]] name = "syn" version = "2.0.106" @@ -1614,341 +413,41 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "tempdir" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" -dependencies = [ - "rand", - "remove_dir_all", -] - [[package]] name = "tempfile" -version = "3.21.0" +version = "3.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15b61f8f20e3a6f7e0649d825294eaf317edce30f82cf6026e7e4cb9222a7d1e" +checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" dependencies = [ "fastrand", "getrandom", "once_cell", - "rustix 1.0.8", - "windows-sys 0.60.2", -] - -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl 1.0.69", -] - -[[package]] -name = "thiserror" -version = "2.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" -dependencies = [ - "thiserror-impl 2.0.16", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "thread_local" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "tokio" -version = "1.47.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" -dependencies = [ - "backtrace", - "bytes", - "io-uring", - "libc", - "mio", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "slab", - "socket2", - "tokio-macros", - "windows-sys 0.59.0", -] - -[[package]] -name = "tokio-macros" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tracing" -version = "0.1.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" -dependencies = [ - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tracing-core" -version = "0.1.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" -dependencies = [ - "nu-ansi-term", - "sharded-slab", - "smallvec", - "thread_local", - "tracing-core", - "tracing-log", -] - -[[package]] -name = "tracing-tunnel" -version = "0.1.99-fork-0" -source = "git+https://github.com/roberth/tracing-toolbox?tag=0.1.99-fork-0#2b342882dd2ed36beb3ea4eed5bb28c70d13a28f" -dependencies = [ - "once_cell", - "serde", - "tracing-core", -] - -[[package]] -name = "typify" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7144144e97e987c94758a3017c920a027feac0799df325d6df4fc8f08d02068e" -dependencies = [ - "typify-impl", - "typify-macro", -] - -[[package]] -name = "typify-impl" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "062879d46aa4c9dfe0d33b035bbaf512da192131645d05deacb7033ec8581a09" -dependencies = [ - "heck", - "log", - "proc-macro2", - "quote", - "regress", - "schemars", - "semver", - "serde", - "serde_json", - "syn", - "thiserror 2.0.16", - "unicode-ident", -] - -[[package]] -name = "typify-macro" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9708a3ceb6660ba3f8d2b8f0567e7d4b8b198e2b94d093b8a6077a751425de9e" -dependencies = [ - "proc-macro2", - "quote", - "schemars", - "semver", - "serde", - "serde_json", - "serde_tokenstream", - "syn", - "typify-impl", + "rustix 1.1.2", + "windows-sys 0.61.1", ] [[package]] name = "unicode-ident" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" - -[[package]] -name = "unicode-segmentation" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" - -[[package]] -name = "unicode-truncate" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" -dependencies = [ - "itertools 0.13.0", - "unicode-segmentation", - "unicode-width 0.1.14", -] - -[[package]] -name = "unicode-width" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" - -[[package]] -name = "unicode-width" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" - -[[package]] -name = "utf8parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" - -[[package]] -name = "valuable" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" +checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" [[package]] name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" +version = "0.14.7+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - -[[package]] -name = "wasi" -version = "0.14.2+wasi-0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" dependencies = [ - "wit-bindgen-rt", + "wasip2", ] [[package]] -name = "wasm-bindgen" -version = "0.2.100" +name = "wasip2" +version = "1.0.1+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" -dependencies = [ - "unicode-ident", + "wit-bindgen", ] [[package]] @@ -1963,86 +462,11 @@ dependencies = [ "rustix 0.38.44", ] -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-core" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-implement" -version = "0.60.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-interface" -version = "0.59.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "windows-link" -version = "0.1.3" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" - -[[package]] -name = "windows-result" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" -dependencies = [ - "windows-link", -] +checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" [[package]] name = "windows-sys" @@ -2050,16 +474,16 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] name = "windows-sys" -version = "0.60.2" +version = "0.61.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +checksum = "6f109e41dd4a3c848907eb83d5a42ea98b3769495597450cf6d153507b166f0f" dependencies = [ - "windows-targets 0.53.3", + "windows-link", ] [[package]] @@ -2068,31 +492,14 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.53.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" -dependencies = [ - "windows-link", - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] @@ -2101,84 +508,42 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" - [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" -[[package]] -name = "windows_i686_gnu" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" - [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" - [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_i686_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -2186,16 +551,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] -name = "windows_x86_64_msvc" -version = "0.53.0" +name = "wit-bindgen" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" - -[[package]] -name = "wit-bindgen-rt" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" -dependencies = [ - "bitflags", -] +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index f71d8a8..02f532b 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,10 +1,10 @@ [workspace] members = [ - "nix-c-raw", - "nix-expr", - "nix-fetchers", - "nix-flake", - "nix-store", - "nix-util", + "nix-bindings-bindgen-raw", + "nix-bindings-expr", + "nix-bindings-fetchers", + "nix-bindings-flake", + "nix-bindings-store", + "nix-bindings-util", ] resolver = "2" diff --git a/rust/nix-c-raw/Cargo.toml b/rust/nix-bindings-bindgen-raw/Cargo.toml similarity index 83% rename from rust/nix-c-raw/Cargo.toml rename to rust/nix-bindings-bindgen-raw/Cargo.toml index d19b4fa..7668b60 100644 --- a/rust/nix-c-raw/Cargo.toml +++ b/rust/nix-bindings-bindgen-raw/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "nix-c-raw" +name = "nix-bindings-bindgen-raw" version = "0.1.0" edition = "2021" build = "build.rs" diff --git a/rust/nix-c-raw/README.md b/rust/nix-bindings-bindgen-raw/README.md similarity index 79% rename from rust/nix-c-raw/README.md rename to rust/nix-bindings-bindgen-raw/README.md index dc9d8b4..1c7e5f0 100644 --- a/rust/nix-c-raw/README.md +++ b/rust/nix-bindings-bindgen-raw/README.md @@ -1,8 +1,8 @@ -# nix-c-raw +# nix-bindings-bindgen-raw This crate contains generated bindings for the Nix C API. **You should not have to use this crate directly,** and so you should probably not add it to your dependencies. -Instead, use the `nix-util`, `nix-store` and `nix-expr` crates, which _should_ be sufficient. +Instead, use the `nix-bindings-util`, `nix-bindings-store` and `nix-bindings-expr` crates, which _should_ be sufficient. ## Design diff --git a/rust/nix-c-raw/build.rs b/rust/nix-bindings-bindgen-raw/build.rs similarity index 100% rename from rust/nix-c-raw/build.rs rename to rust/nix-bindings-bindgen-raw/build.rs diff --git a/rust/nix-c-raw/include/nix-c-raw.h b/rust/nix-bindings-bindgen-raw/include/nix-c-raw.h similarity index 100% rename from rust/nix-c-raw/include/nix-c-raw.h rename to rust/nix-bindings-bindgen-raw/include/nix-c-raw.h diff --git a/rust/nix-c-raw/src/lib.rs b/rust/nix-bindings-bindgen-raw/src/lib.rs similarity index 100% rename from rust/nix-c-raw/src/lib.rs rename to rust/nix-bindings-bindgen-raw/src/lib.rs diff --git a/rust/nix-bindings-expr/Cargo.toml b/rust/nix-bindings-expr/Cargo.toml new file mode 100644 index 0000000..3c97117 --- /dev/null +++ b/rust/nix-bindings-expr/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "nix-bindings-expr" +version = "0.1.0" +edition = "2021" +license = "LGPL-2.1" + +[lib] +path = "src/lib.rs" + +[dependencies] +anyhow = "1.0" +nix-bindings-store = { path = "../nix-bindings-store" } +nix-bindings-util = { path = "../nix-bindings-util" } +nix-bindings-bindgen-raw = { path = "../nix-bindings-bindgen-raw" } +lazy_static = "1.4" +ctor = "0.2" +tempfile = "3.10" +cstr = "0.2" diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-bindings-expr/src/eval_state.rs similarity index 98% rename from rust/nix-expr/src/eval_state.rs rename to rust/nix-bindings-expr/src/eval_state.rs index 5c9154a..0cdde8c 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-bindings-expr/src/eval_state.rs @@ -17,8 +17,8 @@ //! Create an [`EvalState`] using [`EvalState::new`] or [`EvalStateBuilder`] for advanced configuration: //! //! ```rust -//! # use nix_expr::eval_state::{EvalState, EvalStateBuilder, test_init, gc_register_my_thread}; -//! # use nix_store::store::Store; +//! # use nix_bindings_expr::eval_state::{EvalState, EvalStateBuilder, test_init, gc_register_my_thread}; +//! # use nix_bindings_store::store::Store; //! # use std::collections::HashMap; //! # fn example() -> anyhow::Result<()> { //! # test_init(); let guard = gc_register_my_thread()?; @@ -71,7 +71,7 @@ //! Before using [`EvalState`] in a thread, register it with the (process memory) garbage collector: //! //! ```rust,no_run -//! # use nix_expr::eval_state::{init, gc_register_my_thread, test_init}; +//! # use nix_bindings_expr::eval_state::{init, gc_register_my_thread, test_init}; //! # fn example() -> anyhow::Result<()> { //! # test_init(); // Use test_init() in tests //! init()?; // Initialize Nix library @@ -92,8 +92,8 @@ //! ## Examples //! //! ```rust -//! use nix_expr::eval_state::{EvalState, test_init, gc_register_my_thread}; -//! use nix_store::store::Store; +//! use nix_bindings_expr::eval_state::{EvalState, test_init, gc_register_my_thread}; +//! use nix_bindings_store::store::Store; //! use std::collections::HashMap; //! //! # fn main() -> anyhow::Result<()> { @@ -134,12 +134,12 @@ use anyhow::Context as _; use anyhow::{bail, Result}; use cstr::cstr; use lazy_static::lazy_static; -use nix_c_raw as raw; -use nix_store::path::StorePath; -use nix_store::store::{Store, StoreWeak}; -use nix_util::context::Context; -use nix_util::string_return::{callback_get_result_string, callback_get_result_string_data}; -use nix_util::{check_call, check_call_opt_key, result_string_init}; +use nix_bindings_bindgen_raw as raw; +use nix_bindings_store::path::StorePath; +use nix_bindings_store::store::{Store, StoreWeak}; +use nix_bindings_util::context::Context; +use nix_bindings_util::string_return::{callback_get_result_string, callback_get_result_string_data}; +use nix_bindings_util::{check_call, check_call_opt_key, result_string_init}; use std::ffi::{c_char, CString}; use std::iter::FromIterator; use std::os::raw::c_uint; @@ -162,7 +162,7 @@ pub fn init() -> Result<()> { Ok(_) => Ok(()), Err(e) => { // Couldn't just clone the error, so we have to print it here. - Err(anyhow::format_err!("nix_expr::init error: {}", e)) + Err(anyhow::format_err!("nix_bindings_expr::init error: {}", e)) } } } @@ -225,8 +225,8 @@ impl Drop for EvalStateRef { /// # Examples /// /// ```rust -/// # use nix_expr::eval_state::{EvalState, EvalStateBuilder, test_init, gc_register_my_thread}; -/// # use nix_store::store::Store; +/// # use nix_bindings_expr::eval_state::{EvalState, EvalStateBuilder, test_init, gc_register_my_thread}; +/// # use nix_bindings_store::store::Store; /// # use std::collections::HashMap; /// # fn example() -> anyhow::Result<()> { /// # test_init(); @@ -369,9 +369,9 @@ impl EvalState { /// # Examples /// /// ``` - /// # use nix_expr::eval_state::{EvalState, test_init, gc_register_my_thread}; - /// use nix_store::store::Store; - /// use nix_expr::value::Value; + /// # use nix_bindings_expr::eval_state::{EvalState, test_init, gc_register_my_thread}; + /// use nix_bindings_store::store::Store; + /// use nix_bindings_expr::value::Value; /// use std::collections::HashMap; /// /// # fn main() -> anyhow::Result<()> { @@ -474,8 +474,8 @@ impl EvalState { /// # Examples /// /// ```rust - /// # use nix_expr::eval_state::{EvalState, test_init, gc_register_my_thread}; - /// # use nix_store::store::Store; + /// # use nix_bindings_expr::eval_state::{EvalState, test_init, gc_register_my_thread}; + /// # use nix_bindings_store::store::Store; /// # use std::collections::HashMap; /// # fn example() -> anyhow::Result<()> { /// # test_init(); @@ -529,9 +529,9 @@ impl EvalState { /// # Examples /// /// ```rust,no_run - /// # use nix_expr::value::Value; + /// # use nix_bindings_expr::value::Value; /// # use std::collections::{VecDeque, LinkedList}; - /// # fn example(es: &mut nix_expr::eval_state::EvalState, list_value: &Value) -> anyhow::Result<()> { + /// # fn example(es: &mut nix_bindings_expr::eval_state::EvalState, list_value: &Value) -> anyhow::Result<()> { /// let vec: Vec = es.require_list_strict(&list_value)?; /// let deque: VecDeque = es.require_list_strict(&list_value)?; /// let linked_list = es.require_list_strict::>(&list_value)?; @@ -942,8 +942,8 @@ impl EvalState { /// # Examples /// /// ```rust - /// # use nix_expr::eval_state::{EvalState, test_init, gc_register_my_thread}; - /// # use nix_store::store::Store; + /// # use nix_bindings_expr::eval_state::{EvalState, test_init, gc_register_my_thread}; + /// # use nix_bindings_store::store::Store; /// # use std::collections::HashMap; /// # fn example() -> anyhow::Result<()> { /// # test_init(); @@ -1043,8 +1043,8 @@ impl EvalState { /// # Examples /// /// ```rust - /// # use nix_expr::eval_state::{EvalState, test_init, gc_register_my_thread}; - /// # use nix_store::store::Store; + /// # use nix_bindings_expr::eval_state::{EvalState, test_init, gc_register_my_thread}; + /// # use nix_bindings_store::store::Store; /// # use std::collections::HashMap; /// # fn example() -> anyhow::Result<()> { /// # test_init(); @@ -1211,18 +1211,18 @@ pub fn test_init() { // would cause the test suite to reinvokes itself, causing an infinite loop. // While _NIX_TEST_NO_SANDBOX=1 should prevent this, we may also set the // build hook to "" to prevent this. - nix_util::settings::set("build-hook", "").unwrap(); + nix_bindings_util::settings::set("build-hook", "").unwrap(); // When testing in the sandbox, the default build dir would be a parent of the storeDir, // which causes an error. So we set a custom build dir here. // Only available on linux if cfg!(target_os = "linux") { - nix_util::settings::set("sandbox-build-dir", "/custom-build-dir-for-test").unwrap(); + nix_bindings_util::settings::set("sandbox-build-dir", "/custom-build-dir-for-test").unwrap(); } std::env::set_var("_NIX_TEST_NO_SANDBOX", "1"); // The tests run offline - nix_util::settings::set("substituters", "").unwrap(); + nix_bindings_util::settings::set("substituters", "").unwrap(); } #[cfg(test)] diff --git a/rust/nix-expr/src/lib.rs b/rust/nix-bindings-expr/src/lib.rs similarity index 100% rename from rust/nix-expr/src/lib.rs rename to rust/nix-bindings-expr/src/lib.rs diff --git a/rust/nix-expr/src/primop.rs b/rust/nix-bindings-expr/src/primop.rs similarity index 98% rename from rust/nix-expr/src/primop.rs rename to rust/nix-bindings-expr/src/primop.rs index 2d2e2d2..f430d4d 100644 --- a/rust/nix-expr/src/primop.rs +++ b/rust/nix-bindings-expr/src/primop.rs @@ -1,8 +1,8 @@ use crate::eval_state::{EvalState, EvalStateWeak}; use crate::value::Value; use anyhow::Result; -use nix_c_raw as raw; -use nix_util::check_call; +use nix_bindings_bindgen_raw as raw; +use nix_bindings_util::check_call; use std::ffi::{c_int, c_void, CStr, CString}; use std::mem::ManuallyDrop; use std::ptr::{null, null_mut}; diff --git a/rust/nix-expr/src/value.rs b/rust/nix-bindings-expr/src/value.rs similarity index 98% rename from rust/nix-expr/src/value.rs rename to rust/nix-bindings-expr/src/value.rs index 14cda88..086a343 100644 --- a/rust/nix-expr/src/value.rs +++ b/rust/nix-bindings-expr/src/value.rs @@ -1,7 +1,7 @@ pub mod __private; -use nix_c_raw as raw; -use nix_util::{check_call, context::Context}; +use nix_bindings_bindgen_raw as raw; +use nix_bindings_util::{check_call, context::Context}; use std::ptr::{null_mut, NonNull}; // TODO: test: cloning a thunk does not duplicate the evaluation. diff --git a/rust/nix-expr/src/value/__private.rs b/rust/nix-bindings-expr/src/value/__private.rs similarity index 90% rename from rust/nix-expr/src/value/__private.rs rename to rust/nix-bindings-expr/src/value/__private.rs index 6b5b2e8..571dcd0 100644 --- a/rust/nix-expr/src/value/__private.rs +++ b/rust/nix-bindings-expr/src/value/__private.rs @@ -1,6 +1,6 @@ //! Functions that are relevant for other bindings modules, but normally not end users. use super::Value; -use nix_c_raw as raw; +use nix_bindings_bindgen_raw as raw; /// See [Value::new]. pub unsafe fn raw_value_new(ptr: *mut raw::Value) -> Value { diff --git a/rust/nix-bindings-fetchers/Cargo.toml b/rust/nix-bindings-fetchers/Cargo.toml new file mode 100644 index 0000000..fa75657 --- /dev/null +++ b/rust/nix-bindings-fetchers/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "nix-bindings-fetchers" +version = "0.1.0" +edition = "2021" +license = "LGPL-2.1" + +[lib] +path = "src/lib.rs" + +[dependencies] +anyhow = "1.0" +nix-bindings-store = { path = "../nix-bindings-store" } +nix-bindings-util = { path = "../nix-bindings-util" } +nix-bindings-bindgen-raw = { path = "../nix-bindings-bindgen-raw" } +ctor = "0.2" +tempfile = "3.10" +cstr = "0.2" diff --git a/rust/nix-fetchers/src/lib.rs b/rust/nix-bindings-fetchers/src/lib.rs similarity index 91% rename from rust/nix-fetchers/src/lib.rs rename to rust/nix-bindings-fetchers/src/lib.rs index 091830c..06bf45f 100644 --- a/rust/nix-fetchers/src/lib.rs +++ b/rust/nix-bindings-fetchers/src/lib.rs @@ -1,6 +1,6 @@ use anyhow::{Context as _, Result}; -use nix_c_raw as raw; -use nix_util::context::{self, Context}; +use nix_bindings_bindgen_raw as raw; +use nix_bindings_util::context::{self, Context}; use std::ptr::NonNull; pub struct FetchersSettings { diff --git a/rust/nix-bindings-flake/Cargo.toml b/rust/nix-bindings-flake/Cargo.toml new file mode 100644 index 0000000..2b70980 --- /dev/null +++ b/rust/nix-bindings-flake/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "nix-bindings-flake" +version = "0.1.0" +edition = "2021" +license = "LGPL-2.1" + +[lib] +path = "src/lib.rs" + +[dependencies] +anyhow = "1.0" +nix-bindings-expr = { path = "../nix-bindings-expr" } +nix-bindings-fetchers = { path = "../nix-bindings-fetchers" } +nix-bindings-store = { path = "../nix-bindings-store" } +nix-bindings-util = { path = "../nix-bindings-util" } +nix-bindings-bindgen-raw = { path = "../nix-bindings-bindgen-raw" } +lazy_static = "1.4" +ctor = "0.2" +tempfile = "3.10" +cstr = "0.2" diff --git a/rust/nix-flake/src/lib.rs b/rust/nix-bindings-flake/src/lib.rs similarity index 95% rename from rust/nix-flake/src/lib.rs rename to rust/nix-bindings-flake/src/lib.rs index acd5fa4..ef8a963 100644 --- a/rust/nix-flake/src/lib.rs +++ b/rust/nix-bindings-flake/src/lib.rs @@ -1,10 +1,10 @@ use std::{ffi::CString, os::raw::c_char, ptr::NonNull}; use anyhow::{Context as _, Result}; -use nix_c_raw as raw; -use nix_expr::eval_state::EvalState; -use nix_fetchers::FetchersSettings; -use nix_util::{ +use nix_bindings_bindgen_raw as raw; +use nix_bindings_expr::eval_state::EvalState; +use nix_bindings_fetchers::FetchersSettings; +use nix_bindings_util::{ context::{self, Context}, result_string_init, string_return::{callback_get_result_string, callback_get_result_string_data}, @@ -29,7 +29,7 @@ impl FlakeSettings { } fn add_to_eval_state_builder( &self, - builder: &mut nix_expr::eval_state::EvalStateBuilder, + builder: &mut nix_bindings_expr::eval_state::EvalStateBuilder, ) -> Result<()> { let mut ctx = Context::new(); unsafe { @@ -45,14 +45,14 @@ impl FlakeSettings { pub trait EvalStateBuilderExt { /// Configures the eval state to provide flakes features such as `builtins.getFlake`. - fn flakes(self, settings: &FlakeSettings) -> Result; + fn flakes(self, settings: &FlakeSettings) -> Result; } -impl EvalStateBuilderExt for nix_expr::eval_state::EvalStateBuilder { +impl EvalStateBuilderExt for nix_bindings_expr::eval_state::EvalStateBuilder { /// Configures the eval state to provide flakes features such as `builtins.getFlake`. fn flakes( mut self, settings: &FlakeSettings, - ) -> Result { + ) -> Result { settings.add_to_eval_state_builder(&mut self)?; Ok(self) } @@ -239,7 +239,7 @@ impl LockedFlake { &self, flake_settings: &FlakeSettings, eval_state: &mut EvalState, - ) -> Result { + ) -> Result { let mut ctx = Context::new(); unsafe { let r = context::check_call!(raw::locked_flake_get_output_attrs( @@ -248,15 +248,15 @@ impl LockedFlake { eval_state.raw_ptr(), self.ptr.as_ptr() ))?; - Ok(nix_expr::value::__private::raw_value_new(r)) + Ok(nix_bindings_expr::value::__private::raw_value_new(r)) } } } #[cfg(test)] mod tests { - use nix_expr::eval_state::{gc_register_my_thread, EvalStateBuilder}; - use nix_store::store::Store; + use nix_bindings_expr::eval_state::{gc_register_my_thread, EvalStateBuilder}; + use nix_bindings_store::store::Store; use super::*; use std::sync::Once; @@ -267,7 +267,7 @@ mod tests { // Only set experimental-features once to minimize the window where // concurrent Nix operations might read the setting while it's being modified INIT.call_once(|| { - nix_util::settings::set("experimental-features", "flakes").unwrap(); + nix_bindings_util::settings::set("experimental-features", "flakes").unwrap(); }); } diff --git a/rust/nix-store/Cargo.toml b/rust/nix-bindings-store/Cargo.toml similarity index 57% rename from rust/nix-store/Cargo.toml rename to rust/nix-bindings-store/Cargo.toml index a7ea036..cc92326 100644 --- a/rust/nix-store/Cargo.toml +++ b/rust/nix-bindings-store/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "nix-store" +name = "nix-bindings-store" version = "0.1.0" edition = "2021" build = "build.rs" @@ -10,8 +10,8 @@ path = "src/lib.rs" [dependencies] anyhow = "1.0" -nix-util = { path = "../nix-util" } -nix-c-raw = { path = "../nix-c-raw" } +nix-bindings-util = { path = "../nix-bindings-util" } +nix-bindings-bindgen-raw = { path = "../nix-bindings-bindgen-raw" } lazy_static = "1.4" [build-dependencies] diff --git a/rust/nix-store/build.rs b/rust/nix-bindings-store/build.rs similarity index 100% rename from rust/nix-store/build.rs rename to rust/nix-bindings-store/build.rs diff --git a/rust/nix-store/src/lib.rs b/rust/nix-bindings-store/src/lib.rs similarity index 100% rename from rust/nix-store/src/lib.rs rename to rust/nix-bindings-store/src/lib.rs diff --git a/rust/nix-store/src/path.rs b/rust/nix-bindings-store/src/path.rs similarity index 96% rename from rust/nix-store/src/path.rs rename to rust/nix-bindings-store/src/path.rs index 29b6c83..d8aea0c 100644 --- a/rust/nix-store/src/path.rs +++ b/rust/nix-bindings-store/src/path.rs @@ -1,8 +1,8 @@ use std::ptr::NonNull; use anyhow::Result; -use nix_c_raw as raw; -use nix_util::{ +use nix_bindings_bindgen_raw as raw; +use nix_bindings_util::{ result_string_init, string_return::{callback_get_result_string, callback_get_result_string_data}, }; @@ -60,7 +60,7 @@ impl StorePath { /// # Safety /// /// This function is unsafe because it returns a raw pointer. The caller must ensure that the pointer is not used beyond the lifetime of this `StorePath`. - pub unsafe fn as_ptr(&self) -> *mut nix_c_raw::StorePath { + pub unsafe fn as_ptr(&self) -> *mut raw::StorePath { self.raw.as_ptr() } } diff --git a/rust/nix-store/src/store.rs b/rust/nix-bindings-store/src/store.rs similarity index 97% rename from rust/nix-store/src/store.rs rename to rust/nix-bindings-store/src/store.rs index 0a6b2ae..4650535 100644 --- a/rust/nix-store/src/store.rs +++ b/rust/nix-bindings-store/src/store.rs @@ -1,9 +1,9 @@ use anyhow::{bail, Error, Result}; use lazy_static::lazy_static; -use nix_c_raw as raw; -use nix_util::context::Context; -use nix_util::string_return::{callback_get_result_string, callback_get_result_string_data}; -use nix_util::{check_call, result_string_init}; +use nix_bindings_bindgen_raw as raw; +use nix_bindings_util::context::Context; +use nix_bindings_util::string_return::{callback_get_result_string, callback_get_result_string_data}; +use nix_bindings_util::{check_call, result_string_init}; use std::collections::HashMap; use std::ffi::{c_char, CString}; use std::ptr::null_mut; diff --git a/rust/nix-util/Cargo.toml b/rust/nix-bindings-util/Cargo.toml similarity index 59% rename from rust/nix-util/Cargo.toml rename to rust/nix-bindings-util/Cargo.toml index cefbbb8..a0fda9d 100644 --- a/rust/nix-util/Cargo.toml +++ b/rust/nix-bindings-util/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "nix-util" +name = "nix-bindings-util" version = "0.1.0" edition = "2021" license = "LGPL-2.1" @@ -9,5 +9,5 @@ path = "src/lib.rs" [dependencies] anyhow = "1.0" -nix-c-raw = { path = "../nix-c-raw" } +nix-bindings-bindgen-raw = { path = "../nix-bindings-bindgen-raw" } ctor = "0.2" diff --git a/rust/nix-util/src/context.rs b/rust/nix-bindings-util/src/context.rs similarity index 97% rename from rust/nix-util/src/context.rs rename to rust/nix-bindings-util/src/context.rs index 55d8332..33b657a 100644 --- a/rust/nix-util/src/context.rs +++ b/rust/nix-bindings-util/src/context.rs @@ -1,10 +1,10 @@ use anyhow::{bail, Result}; -use nix_c_raw as raw; +use nix_bindings_bindgen_raw as raw; use std::os::raw::c_char; use std::ptr::null_mut; use std::ptr::NonNull; -/// A context for error handling, when interacting directly with the generated bindings for the C API in [nix_c_raw]. +/// A context for error handling, when interacting directly with the generated bindings for the C API in [nix_bindings_bindgen_raw]. /// /// The `nix-store` and `nix-expr` libraries that consume this type internally store a private context in their `EvalState` and `Store` structs to avoid allocating a new context for each operation. The state of a context is irrelevant when used correctly (e.g. with [check_call!]), so it's safe to reuse, and safe to allocate more contexts in methods such as [Clone::clone]. pub struct Context { diff --git a/rust/nix-util/src/lib.rs b/rust/nix-bindings-util/src/lib.rs similarity index 100% rename from rust/nix-util/src/lib.rs rename to rust/nix-bindings-util/src/lib.rs diff --git a/rust/nix-util/src/settings.rs b/rust/nix-bindings-util/src/settings.rs similarity index 98% rename from rust/nix-util/src/settings.rs rename to rust/nix-bindings-util/src/settings.rs index 93d305d..5b5b5ef 100644 --- a/rust/nix-util/src/settings.rs +++ b/rust/nix-bindings-util/src/settings.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use nix_c_raw as raw; +use nix_bindings_bindgen_raw as raw; use std::sync::Mutex; use crate::{ diff --git a/rust/nix-util/src/string_return.rs b/rust/nix-bindings-util/src/string_return.rs similarity index 97% rename from rust/nix-util/src/string_return.rs rename to rust/nix-bindings-util/src/string_return.rs index 2ccbc11..b7bf010 100644 --- a/rust/nix-util/src/string_return.rs +++ b/rust/nix-bindings-util/src/string_return.rs @@ -52,9 +52,9 @@ macro_rules! result_string_init { #[cfg(test)] mod tests { use super::*; - use nix_c_raw as raw; + use nix_bindings_bindgen_raw as raw; - /// Typecheck the function signature against the generated bindings in nix_c_raw. + /// Typecheck the function signature against the generated bindings in nix_bindings_bindgen_raw. static _CALLBACK_GET_RESULT_STRING: raw::get_string_callback = Some(callback_get_result_string); #[test] diff --git a/rust/nix-expr/Cargo.toml b/rust/nix-expr/Cargo.toml deleted file mode 100644 index 0cd087b..0000000 --- a/rust/nix-expr/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "nix-expr" -version = "0.1.0" -edition = "2021" -license = "LGPL-2.1" - -[lib] -path = "src/lib.rs" - -[dependencies] -anyhow = "1.0" -nix-store = { path = "../nix-store" } -nix-util = { path = "../nix-util" } -nix-c-raw = { path = "../nix-c-raw" } -lazy_static = "1.4" -ctor = "0.2" -tempfile = "3.10" -cstr = "0.2" diff --git a/rust/nix-fetchers/Cargo.toml b/rust/nix-fetchers/Cargo.toml deleted file mode 100644 index 721aeb9..0000000 --- a/rust/nix-fetchers/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "nix-fetchers" -version = "0.1.0" -edition = "2021" -license = "LGPL-2.1" - -[lib] -path = "src/lib.rs" - -[dependencies] -anyhow = "1.0" -nix-store = { path = "../nix-store" } -nix-util = { path = "../nix-util" } -nix-c-raw = { path = "../nix-c-raw" } -ctor = "0.2" -tempfile = "3.10" -cstr = "0.2" diff --git a/rust/nix-flake/Cargo.toml b/rust/nix-flake/Cargo.toml deleted file mode 100644 index 78fd715..0000000 --- a/rust/nix-flake/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "nix-flake" -version = "0.1.0" -edition = "2021" -license = "LGPL-2.1" - -[lib] -path = "src/lib.rs" - -[dependencies] -anyhow = "1.0" -nix-expr = { path = "../nix-expr" } -nix-fetchers = { path = "../nix-fetchers" } -nix-store = { path = "../nix-store" } -nix-util = { path = "../nix-util" } -nix-c-raw = { path = "../nix-c-raw" } -lazy_static = "1.4" -ctor = "0.2" -tempfile = "3.10" -cstr = "0.2" From 40303c27796a557e50fc7fe5fb4fbb1329851fe3 Mon Sep 17 00:00:00 2001 From: Tristan Ross Date: Wed, 15 Oct 2025 18:18:20 +0200 Subject: [PATCH 188/306] feat: nix_store::store add get_fs_closure function --- rust/nix-bindings-store/src/store.rs | 43 ++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/rust/nix-bindings-store/src/store.rs b/rust/nix-bindings-store/src/store.rs index 4650535..0da9ebc 100644 --- a/rust/nix-bindings-store/src/store.rs +++ b/rust/nix-bindings-store/src/store.rs @@ -65,6 +65,25 @@ lazy_static! { static ref STORE_CACHE: Arc> = Arc::new(Mutex::new(HashMap::new())); } +unsafe extern "C" fn callback_get_result_store_path_set( + user_data: *mut std::os::raw::c_void, + store_path: *const raw::StorePath, +) { + let ret = user_data as *mut Vec; + let ret: &mut Vec = &mut *ret; + + let store_path = raw::store_path_clone(store_path); + + let store_path = + NonNull::new(store_path).expect("nix_store_parse_path returned a null pointer"); + let store_path = StorePath::new_raw(store_path); + ret.push(store_path); +} + +fn callback_get_result_store_path_set_data(vec: &mut Vec) -> *mut std::os::raw::c_void { + vec as *mut Vec as *mut std::os::raw::c_void +} + pub struct Store { inner: Arc, /* An error context to reuse. This way we don't have to allocate them for each store operation. */ @@ -232,6 +251,30 @@ impl Store { r } + #[doc(alias = "nix_store_get_fs_closure")] + pub fn get_fs_closure( + &mut self, + store_path: &StorePath, + flip_direction: bool, + include_outputs: bool, + include_derivers: bool, + ) -> Result> { + let mut r = Vec::new(); + unsafe { + check_call!(raw::store_get_fs_closure( + &mut self.context, + self.inner.ptr(), + store_path.as_ptr(), + flip_direction, + include_outputs, + include_derivers, + callback_get_result_store_path_set_data(&mut r), + Some(callback_get_result_store_path_set) + )) + }?; + Ok(r) + } + pub fn weak_ref(&self) -> StoreWeak { StoreWeak { inner: Arc::downgrade(&self.inner), From 2d210260f92da9391d8b21a1b57d169b5fe6fe8c Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 15 Oct 2025 18:45:55 +0200 Subject: [PATCH 189/306] maint: Remove unintentional addition --- .claude/settings.local.json | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 .claude/settings.local.json diff --git a/.claude/settings.local.json b/.claude/settings.local.json deleted file mode 100644 index 1b694a3..0000000 --- a/.claude/settings.local.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/claude-code-settings.json", - "permissions": { - "allow": [ - "Bash(nix flake check:*)", - "Bash(git grep:*)", - "Bash(git cherry-pick:*)", - "Bash(sed:*)" - ], - "deny": [] - } -} \ No newline at end of file From da869e998cedb34b45cea1c986871bbae3779a54 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 16 Oct 2025 00:40:52 +0200 Subject: [PATCH 190/306] feat: Store::realise, Store::add_derivation, Store::derivation_from_json --- rust/Cargo.lock | 2 + rust/nix-bindings-store/Cargo.toml | 4 + rust/nix-bindings-store/src/derivation.rs | 21 + rust/nix-bindings-store/src/lib.rs | 1 + rust/nix-bindings-store/src/store.rs | 459 +++++++++++++++++++++- 5 files changed, 486 insertions(+), 1 deletion(-) create mode 100644 rust/nix-bindings-store/src/derivation.rs diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 54f6191..416f121 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -264,10 +264,12 @@ name = "nix-bindings-store" version = "0.1.0" dependencies = [ "anyhow", + "ctor", "lazy_static", "nix-bindings-bindgen-raw", "nix-bindings-util", "pkg-config", + "tempfile", ] [[package]] diff --git a/rust/nix-bindings-store/Cargo.toml b/rust/nix-bindings-store/Cargo.toml index cc92326..c1300b7 100644 --- a/rust/nix-bindings-store/Cargo.toml +++ b/rust/nix-bindings-store/Cargo.toml @@ -14,5 +14,9 @@ nix-bindings-util = { path = "../nix-bindings-util" } nix-bindings-bindgen-raw = { path = "../nix-bindings-bindgen-raw" } lazy_static = "1.4" +[dev-dependencies] +ctor = "0.2" +tempfile = "3.10" + [build-dependencies] pkg-config = "0.3" diff --git a/rust/nix-bindings-store/src/derivation.rs b/rust/nix-bindings-store/src/derivation.rs new file mode 100644 index 0000000..6d05549 --- /dev/null +++ b/rust/nix-bindings-store/src/derivation.rs @@ -0,0 +1,21 @@ +use nix_bindings_bindgen_raw as raw; +use std::ptr::NonNull; + +/// A Nix derivation +pub struct Derivation { + pub(crate) inner: NonNull, +} + +impl Derivation { + pub(crate) fn new_raw(inner: NonNull) -> Self { + Derivation { inner } + } +} + +impl Drop for Derivation { + fn drop(&mut self) { + unsafe { + raw::derivation_free(self.inner.as_ptr()); + } + } +} diff --git a/rust/nix-bindings-store/src/lib.rs b/rust/nix-bindings-store/src/lib.rs index 5c57e2c..6010f2e 100644 --- a/rust/nix-bindings-store/src/lib.rs +++ b/rust/nix-bindings-store/src/lib.rs @@ -1,2 +1,3 @@ +pub mod derivation; pub mod path; pub mod store; diff --git a/rust/nix-bindings-store/src/store.rs b/rust/nix-bindings-store/src/store.rs index 0da9ebc..b8cfb06 100644 --- a/rust/nix-bindings-store/src/store.rs +++ b/rust/nix-bindings-store/src/store.rs @@ -4,12 +4,13 @@ use nix_bindings_bindgen_raw as raw; use nix_bindings_util::context::Context; use nix_bindings_util::string_return::{callback_get_result_string, callback_get_result_string_data}; use nix_bindings_util::{check_call, result_string_init}; -use std::collections::HashMap; +use std::collections::{BTreeMap, HashMap}; use std::ffi::{c_char, CString}; use std::ptr::null_mut; use std::ptr::NonNull; use std::sync::{Arc, Mutex, Weak}; +use crate::derivation::Derivation; use crate::path::StorePath; /* TODO make Nix itself thread safe */ @@ -66,6 +67,7 @@ lazy_static! { } unsafe extern "C" fn callback_get_result_store_path_set( + _context: *mut raw::c_context, user_data: *mut std::os::raw::c_void, store_path: *const raw::StorePath, ) { @@ -251,6 +253,106 @@ impl Store { r } + /// Parse a derivation from JSON. + /// + /// The JSON format follows the [Nix derivation JSON schema](https://nix.dev/manual/nix/latest/protocols/json/derivation.html). + /// Note that this format is experimental as of writing. + /// The derivation is not added to the store; use [`Store::add_derivation`] for that. + /// + /// # Parameters + /// - `json`: A JSON string representing the derivation + /// + /// # Returns + /// A [`Derivation`] object if parsing succeeds, or an error if the JSON is invalid + /// or malformed. + #[doc(alias = "nix_derivation_from_json")] + pub fn derivation_from_json(&mut self, json: &str) -> Result { + let json_cstr = CString::new(json)?; + unsafe { + let drv = check_call!(raw::derivation_from_json( + &mut self.context, + self.inner.ptr(), + json_cstr.as_ptr() + ))?; + let inner = NonNull::new(drv) + .ok_or_else(|| Error::msg("derivation_from_json returned null"))?; + Ok(Derivation::new_raw(inner)) + } + } + + /// Add a derivation to the store. + /// + /// This computes the store path for the derivation and registers it in the store. + /// The derivation itself is written to the store as a `.drv` file. + /// + /// # Parameters + /// - `drv`: The derivation to add + /// + /// # Returns + /// The store path of the derivation (ending in `.drv`). + #[doc(alias = "nix_add_derivation")] + pub fn add_derivation(&mut self, drv: &Derivation) -> Result { + unsafe { + let path = check_call!(raw::add_derivation( + &mut self.context, + self.inner.ptr(), + drv.inner.as_ptr() + ))?; + let path = NonNull::new(path) + .ok_or_else(|| Error::msg("add_derivation returned null"))?; + Ok(StorePath::new_raw(path)) + } + } + + /// Build a derivation and return its outputs. + /// + /// This builds the derivation at the given store path and returns a map of output + /// names to their realized store paths. The derivation must already exist in the store + /// (see [`Store::add_derivation`]). + /// + /// # Parameters + /// - `path`: The store path of the derivation to build (typically ending in `.drv`) + /// + /// # Returns + /// A [`BTreeMap`] mapping output names (e.g., "out", "dev", "doc") to their store paths. + /// The map is ordered alphabetically by output name for deterministic iteration. + #[doc(alias = "nix_store_realise")] + pub fn realise(&mut self, path: &StorePath) -> Result> { + let mut outputs = BTreeMap::new(); + let userdata = &mut outputs as *mut BTreeMap as *mut std::os::raw::c_void; + + unsafe extern "C" fn callback( + userdata: *mut std::os::raw::c_void, + outname: *const c_char, + out_path: *const raw::StorePath, + ) { + let outputs = userdata as *mut BTreeMap; + let outputs = &mut *outputs; + + let name = std::ffi::CStr::from_ptr(outname) + .to_string_lossy() + .into_owned(); + + let path = raw::store_path_clone(out_path); + let path = NonNull::new(path).expect("store_path_clone returned null"); + let path = StorePath::new_raw(path); + + outputs.insert(name, path); + } + + unsafe { + check_call!(raw::store_realise( + &mut self.context, + self.inner.ptr(), + path.as_ptr(), + userdata, + Some(callback) + ))?; + } + + Ok(outputs) + } + #[doc(alias = "nix_store_get_fs_closure")] pub fn get_fs_closure( &mut self, @@ -294,9 +396,32 @@ impl Clone for Store { #[cfg(test)] mod tests { use std::collections::HashMap; + use ctor::ctor; use super::*; + #[ctor] + fn test_setup() { + // Initialize settings for tests + let _ = INIT.as_ref(); + + // Enable ca-derivations for all tests + nix_bindings_util::settings::set("experimental-features", "ca-derivations").ok(); + + // Disable build hooks to prevent test recursion + nix_bindings_util::settings::set("build-hook", "").ok(); + + // Set custom build dir for sandbox + if cfg!(target_os = "linux") { + nix_bindings_util::settings::set("sandbox-build-dir", "/custom-build-dir-for-test").ok(); + } + + std::env::set_var("_NIX_TEST_NO_SANDBOX", "1"); + + // Tests run offline + nix_bindings_util::settings::set("substituters", "").ok(); + } + #[test] fn none_works() { let res = Store::open(None, HashMap::new()); @@ -381,4 +506,336 @@ mod tests { assert!(weak.upgrade().is_none()); assert!(weak.inner.upgrade().is_none()); } + + fn create_temp_store() -> (Store, tempfile::TempDir) { + let temp_dir = tempfile::tempdir().unwrap(); + + let store_dir = temp_dir.path().join("store"); + let state_dir = temp_dir.path().join("state"); + let log_dir = temp_dir.path().join("log"); + + let store_dir_str = store_dir.to_str().unwrap(); + let state_dir_str = state_dir.to_str().unwrap(); + let log_dir_str = log_dir.to_str().unwrap(); + + let params = vec![ + ("store", store_dir_str), + ("state", state_dir_str), + ("log", log_dir_str), + ]; + + let store = Store::open(Some("local"), params).unwrap(); + (store, temp_dir) + } + + fn current_system() -> Result { + nix_bindings_util::settings::get("system") + } + + fn create_test_derivation_json() -> String { + let system = current_system().unwrap_or_else(|_| { + // Fallback to Rust's platform detection + format!("{}-{}", std::env::consts::ARCH, std::env::consts::OS) + }); + format!( + r#"{{ + "args": ["-c", "echo $name foo > $out"], + "builder": "/bin/sh", + "env": {{ + "builder": "/bin/sh", + "name": "myname", + "out": "/1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9", + "system": "{}" + }}, + "inputDrvs": {{}}, + "inputSrcs": [], + "name": "myname", + "outputs": {{ + "out": {{ + "hashAlgo": "sha256", + "method": "nar" + }} + }}, + "system": "{}", + "version": 3 + }}"#, + system, system + ) + } + + #[test] + fn derivation_from_json() { + let (mut store, temp_dir) = create_temp_store(); + let drv_json = create_test_derivation_json(); + let drv = store.derivation_from_json(&drv_json).unwrap(); + // If we got here, parsing succeeded + drop(drv); + drop(store); + drop(temp_dir); + } + + #[test] + fn derivation_from_invalid_json() { + let (mut store, temp_dir) = create_temp_store(); + let result = store.derivation_from_json("not valid json"); + assert!(result.is_err()); + drop(store); + drop(temp_dir); + } + + #[test] + fn add_derivation() { + let (mut store, temp_dir) = create_temp_store(); + let drv_json = create_test_derivation_json(); + let drv = store.derivation_from_json(&drv_json).unwrap(); + let drv_path = store.add_derivation(&drv).unwrap(); + + // Verify we got a .drv path + let name = drv_path.name().unwrap(); + assert!(name.ends_with(".drv")); + + drop(store); + drop(temp_dir); + } + + #[test] + fn realise() { + let (mut store, temp_dir) = create_temp_store(); + let drv_json = create_test_derivation_json(); + let drv = store.derivation_from_json(&drv_json).unwrap(); + let drv_path = store.add_derivation(&drv).unwrap(); + + // Build the derivation + let outputs = store.realise(&drv_path).unwrap(); + + // Verify we got the expected output + assert!(outputs.contains_key("out")); + let out_path = &outputs["out"]; + let out_name = out_path.name().unwrap(); + assert_eq!(out_name, "myname"); + + drop(store); + drop(temp_dir); + } + + fn create_multi_output_derivation_json() -> String { + let system = current_system().unwrap_or_else(|_| { + format!("{}-{}", std::env::consts::ARCH, std::env::consts::OS) + }); + + format!( + r#"{{ + "version": 3, + "name": "multi-output-test", + "system": "{}", + "builder": "/bin/sh", + "args": ["-c", "echo a > $outa; echo b > $outb; echo c > $outc; echo d > $outd; echo e > $oute; echo f > $outf; echo g > $outg; echo h > $outh; echo i > $outi; echo j > $outj"], + "env": {{ + "builder": "/bin/sh", + "name": "multi-output-test", + "system": "{}", + "outf": "/1vkfzqpwk313b51x0xjyh5s7w1lx141mr8da3dr9wqz5aqjyr2fh", + "outd": "/1ypxifgmbzp5sd0pzsp2f19aq68x5215260z3lcrmy5fch567lpm", + "outi": "/1wmasjnqi12j1mkjbxazdd0qd0ky6dh1qry12fk8qyp5kdamhbdx", + "oute": "/1f9r2k1s168js509qlw8a9di1qd14g5lqdj5fcz8z7wbqg11qp1f", + "outh": "/1rkx1hmszslk5nq9g04iyvh1h7bg8p92zw0hi4155hkjm8bpdn95", + "outc": "/1rj4nsf9pjjqq9jsq58a2qkwa7wgvgr09kgmk7mdyli6h1plas4w", + "outb": "/1p7i1dxifh86xq97m5kgb44d7566gj7rfjbw7fk9iij6ca4akx61", + "outg": "/14f8qi0r804vd6a6v40ckylkk1i6yl6fm243qp6asywy0km535lc", + "outj": "/0gkw1366qklqfqb2lw1pikgdqh3cmi3nw6f1z04an44ia863nxaz", + "outa": "/039akv9zfpihrkrv4pl54f3x231x362bll9afblsgfqgvx96h198" + }}, + "inputDrvs": {{}}, + "inputSrcs": [], + "outputs": {{ + "outd": {{ "hashAlgo": "sha256", "method": "nar" }}, + "outf": {{ "hashAlgo": "sha256", "method": "nar" }}, + "outg": {{ "hashAlgo": "sha256", "method": "nar" }}, + "outb": {{ "hashAlgo": "sha256", "method": "nar" }}, + "outc": {{ "hashAlgo": "sha256", "method": "nar" }}, + "outi": {{ "hashAlgo": "sha256", "method": "nar" }}, + "outj": {{ "hashAlgo": "sha256", "method": "nar" }}, + "outh": {{ "hashAlgo": "sha256", "method": "nar" }}, + "outa": {{ "hashAlgo": "sha256", "method": "nar" }}, + "oute": {{ "hashAlgo": "sha256", "method": "nar" }} + }} + }}"#, + system, system + ) + } + + #[test] + fn realise_multi_output_ordering() { + let (mut store, temp_dir) = create_temp_store(); + let drv_json = create_multi_output_derivation_json(); + let drv = store.derivation_from_json(&drv_json).unwrap(); + let drv_path = store.add_derivation(&drv).unwrap(); + + // Build the derivation + let outputs = store.realise(&drv_path).unwrap(); + + // Verify outputs are complete (BTreeMap guarantees ordering) + let output_names: Vec<&String> = outputs.keys().collect(); + let expected_order = vec!["outa", "outb", "outc", "outd", "oute", "outf", "outg", "outh", "outi", "outj"]; + assert_eq!(output_names, expected_order); + + drop(store); + drop(temp_dir); + } + + #[test] + fn realise_invalid_system() { + let (mut store, temp_dir) = create_temp_store(); + + // Create a derivation with an invalid system + let system = "bogus65-bogusos"; + let drv_json = format!( + r#"{{ + "args": ["-c", "echo $name foo > $out"], + "builder": "/bin/sh", + "env": {{ + "builder": "/bin/sh", + "name": "myname", + "out": "/1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9", + "system": "{}" + }}, + "inputDrvs": {{}}, + "inputSrcs": [], + "name": "myname", + "outputs": {{ + "out": {{ + "hashAlgo": "sha256", + "method": "nar" + }} + }}, + "system": "{}", + "version": 3 + }}"#, + system, system + ); + + let drv = store.derivation_from_json(&drv_json).unwrap(); + let drv_path = store.add_derivation(&drv).unwrap(); + + // Try to build - should fail + let result = store.realise(&drv_path); + let err = match result { + Ok(_) => panic!("Build should fail with invalid system"), + Err(e) => e.to_string(), + }; + assert!( + err.contains("required system or feature not available"), + "Error should mention system not available, got: {}", + err + ); + + drop(store); + drop(temp_dir); + } + + #[test] + fn realise_builder_fails() { + let (mut store, temp_dir) = create_temp_store(); + + let system = current_system().unwrap_or_else(|_| { + format!("{}-{}", std::env::consts::ARCH, std::env::consts::OS) + }); + + // Create a derivation where the builder exits with error + let drv_json = format!( + r#"{{ + "args": ["-c", "exit 1"], + "builder": "/bin/sh", + "env": {{ + "builder": "/bin/sh", + "name": "failing", + "out": "/1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9", + "system": "{}" + }}, + "inputDrvs": {{}}, + "inputSrcs": [], + "name": "failing", + "outputs": {{ + "out": {{ + "hashAlgo": "sha256", + "method": "nar" + }} + }}, + "system": "{}", + "version": 3 + }}"#, + system, system + ); + + let drv = store.derivation_from_json(&drv_json).unwrap(); + let drv_path = store.add_derivation(&drv).unwrap(); + + // Try to build - should fail + let result = store.realise(&drv_path); + let err = match result { + Ok(_) => panic!("Build should fail when builder exits with error"), + Err(e) => e.to_string(), + }; + assert!( + err.contains("builder failed with exit code 1"), + "Error should mention builder failed with exit code, got: {}", + err + ); + + drop(store); + drop(temp_dir); + } + + #[test] + fn realise_builder_no_output() { + let (mut store, temp_dir) = create_temp_store(); + + let system = current_system().unwrap_or_else(|_| { + format!("{}-{}", std::env::consts::ARCH, std::env::consts::OS) + }); + + // Create a derivation where the builder succeeds but produces no output + let drv_json = format!( + r#"{{ + "args": ["-c", "true"], + "builder": "/bin/sh", + "env": {{ + "builder": "/bin/sh", + "name": "no-output", + "out": "/1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9", + "system": "{}" + }}, + "inputDrvs": {{}}, + "inputSrcs": [], + "name": "no-output", + "outputs": {{ + "out": {{ + "hashAlgo": "sha256", + "method": "nar" + }} + }}, + "system": "{}", + "version": 3 + }}"#, + system, system + ); + + let drv = store.derivation_from_json(&drv_json).unwrap(); + let drv_path = store.add_derivation(&drv).unwrap(); + + // Try to build - should fail + let result = store.realise(&drv_path); + let err = match result { + Ok(_) => panic!("Build should fail when builder produces no output"), + Err(e) => e.to_string(), + }; + assert!( + err.contains("failed to produce output path"), + "Error should mention failed to produce output, got: {}", + err + ); + + drop(store); + drop(temp_dir); + } } From 03c0dac5b324853c86cb2d2cdf23342e23f1b32f Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 16 Oct 2025 01:30:14 +0200 Subject: [PATCH 191/306] test: Test and document Store::get_fs_closure --- rust/nix-bindings-store/src/store.rs | 111 +++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/rust/nix-bindings-store/src/store.rs b/rust/nix-bindings-store/src/store.rs index b8cfb06..64864e5 100644 --- a/rust/nix-bindings-store/src/store.rs +++ b/rust/nix-bindings-store/src/store.rs @@ -353,6 +353,22 @@ impl Store { Ok(outputs) } + /// Get the closure of a specific store path. + /// + /// Computes the filesystem closure (dependency graph) of a store path, with options + /// to control the direction and which related paths to include. + /// + /// # Parameters + /// - `store_path`: The path to compute the closure from + /// - `flip_direction`: If false, compute the forward closure (paths referenced by this path). + /// If true, compute the backward closure (paths that reference this path). + /// - `include_outputs`: When `flip_direction` is false: for any derivation in the closure, include its outputs. + /// When `flip_direction` is true: for any output in the closure, include derivations that produce it. + /// - `include_derivers`: When `flip_direction` is false: for any output in the closure, include the derivation that produced it. + /// When `flip_direction` is true: for any derivation in the closure, include its outputs. + /// + /// # Returns + /// A vector of store paths in the closure, in no particular order. #[doc(alias = "nix_store_get_fs_closure")] pub fn get_fs_closure( &mut self, @@ -838,4 +854,99 @@ mod tests { drop(store); drop(temp_dir); } + + #[test] + fn get_fs_closure_with_outputs() { + let (mut store, temp_dir) = create_temp_store(); + let drv_json = create_test_derivation_json(); + let drv = store.derivation_from_json(&drv_json).unwrap(); + let drv_path = store.add_derivation(&drv).unwrap(); + + // Build the derivation to get the output path + let outputs = store.realise(&drv_path).unwrap(); + let out_path = &outputs["out"]; + let out_path_name = out_path.name().unwrap(); + + // Get closure with include_outputs=true + let closure = store.get_fs_closure(&drv_path, false, true, false).unwrap(); + + // The closure should contain at least the derivation and its output + assert!(closure.len() >= 2, "Closure should contain at least drv and output"); + + // Verify the output path is in the closure + let out_in_closure = closure.iter().any(|p| p.name().unwrap() == out_path_name); + assert!(out_in_closure, "Output path should be in closure when include_outputs=true"); + + drop(store); + drop(temp_dir); + } + + #[test] + fn get_fs_closure_without_outputs() { + let (mut store, temp_dir) = create_temp_store(); + let drv_json = create_test_derivation_json(); + let drv = store.derivation_from_json(&drv_json).unwrap(); + let drv_path = store.add_derivation(&drv).unwrap(); + + // Build the derivation to get the output path + let outputs = store.realise(&drv_path).unwrap(); + let out_path = &outputs["out"]; + let out_path_name = out_path.name().unwrap(); + + // Get closure with include_outputs=false + let closure = store.get_fs_closure(&drv_path, false, false, false).unwrap(); + + // Verify the output path is NOT in the closure + let out_in_closure = closure.iter().any(|p| p.name().unwrap() == out_path_name); + assert!(!out_in_closure, "Output path should not be in closure when include_outputs=false"); + + drop(store); + drop(temp_dir); + } + + #[test] + fn get_fs_closure_flip_direction() { + let (mut store, temp_dir) = create_temp_store(); + let drv_json = create_test_derivation_json(); + let drv = store.derivation_from_json(&drv_json).unwrap(); + let drv_path = store.add_derivation(&drv).unwrap(); + + // Build the derivation to get the output path + let outputs = store.realise(&drv_path).unwrap(); + let out_path = &outputs["out"]; + let out_path_name = out_path.name().unwrap(); + + // Get closure with flip_direction=true (reverse dependencies) + let closure = store.get_fs_closure(&drv_path, true, true, false).unwrap(); + + // Verify the output path is NOT in the closure when direction is flipped + let out_in_closure = closure.iter().any(|p| p.name().unwrap() == out_path_name); + assert!(!out_in_closure, "Output path should not be in closure when flip_direction=true"); + + drop(store); + drop(temp_dir); + } + + #[test] + fn get_fs_closure_include_derivers() { + let (mut store, temp_dir) = create_temp_store(); + let drv_json = create_test_derivation_json(); + let drv = store.derivation_from_json(&drv_json).unwrap(); + let drv_path = store.add_derivation(&drv).unwrap(); + let drv_path_name = drv_path.name().unwrap(); + + // Build the derivation to get the output path + let outputs = store.realise(&drv_path).unwrap(); + let out_path = &outputs["out"]; + + // Get closure of the output path with include_derivers=true + let closure = store.get_fs_closure(out_path, false, false, true).unwrap(); + + // Verify the derivation path is in the closure + let drv_in_closure = closure.iter().any(|p| p.name().unwrap() == drv_path_name); + assert!(drv_in_closure, "Derivation should be in closure when include_derivers=true"); + + drop(store); + drop(temp_dir); + } } From 01443c7f69fa3afe9b1bca4bb068da93375cb732 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 21 Oct 2025 23:18:33 +0200 Subject: [PATCH 192/306] maint: Add version bound to new additions --- rust/nix-bindings-store/build.rs | 2 +- rust/nix-bindings-store/src/derivation.rs | 4 +++ rust/nix-bindings-store/src/store.rs | 33 ++++++++++++++++++++++- 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/rust/nix-bindings-store/build.rs b/rust/nix-bindings-store/build.rs index f573fe8..e97be70 100644 --- a/rust/nix-bindings-store/build.rs +++ b/rust/nix-bindings-store/build.rs @@ -6,7 +6,7 @@ fn main() { // Unfortunately, Rust doesn't give us a "greater than" operator in conditional // compilation, so we pre-evaluate the version comparisons here, making use // of the multi-valued nature of Rust cfgs. - let relevant_versions = vec!["2.26"]; + let relevant_versions = vec!["2.26", "2.33"]; let versions = relevant_versions .iter() .map(|v| format!("\"{}\"", v)) diff --git a/rust/nix-bindings-store/src/derivation.rs b/rust/nix-bindings-store/src/derivation.rs index 6d05549..ff13c08 100644 --- a/rust/nix-bindings-store/src/derivation.rs +++ b/rust/nix-bindings-store/src/derivation.rs @@ -1,7 +1,11 @@ +#![cfg(nix_at_least = "2.33")] + use nix_bindings_bindgen_raw as raw; use std::ptr::NonNull; /// A Nix derivation +/// +/// **Requires Nix 2.33 or later.** pub struct Derivation { pub(crate) inner: NonNull, } diff --git a/rust/nix-bindings-store/src/store.rs b/rust/nix-bindings-store/src/store.rs index 64864e5..5e0885f 100644 --- a/rust/nix-bindings-store/src/store.rs +++ b/rust/nix-bindings-store/src/store.rs @@ -4,12 +4,15 @@ use nix_bindings_bindgen_raw as raw; use nix_bindings_util::context::Context; use nix_bindings_util::string_return::{callback_get_result_string, callback_get_result_string_data}; use nix_bindings_util::{check_call, result_string_init}; -use std::collections::{BTreeMap, HashMap}; +use std::collections::HashMap; +#[cfg(nix_at_least = "2.33")] +use std::collections::BTreeMap; use std::ffi::{c_char, CString}; use std::ptr::null_mut; use std::ptr::NonNull; use std::sync::{Arc, Mutex, Weak}; +#[cfg(nix_at_least = "2.33")] use crate::derivation::Derivation; use crate::path::StorePath; @@ -66,6 +69,7 @@ lazy_static! { static ref STORE_CACHE: Arc> = Arc::new(Mutex::new(HashMap::new())); } +#[cfg(nix_at_least = "2.33")] unsafe extern "C" fn callback_get_result_store_path_set( _context: *mut raw::c_context, user_data: *mut std::os::raw::c_void, @@ -82,6 +86,7 @@ unsafe extern "C" fn callback_get_result_store_path_set( ret.push(store_path); } +#[cfg(nix_at_least = "2.33")] fn callback_get_result_store_path_set_data(vec: &mut Vec) -> *mut std::os::raw::c_void { vec as *mut Vec as *mut std::os::raw::c_void } @@ -255,6 +260,8 @@ impl Store { /// Parse a derivation from JSON. /// + /// **Requires Nix 2.33 or later.** + /// /// The JSON format follows the [Nix derivation JSON schema](https://nix.dev/manual/nix/latest/protocols/json/derivation.html). /// Note that this format is experimental as of writing. /// The derivation is not added to the store; use [`Store::add_derivation`] for that. @@ -265,6 +272,7 @@ impl Store { /// # Returns /// A [`Derivation`] object if parsing succeeds, or an error if the JSON is invalid /// or malformed. + #[cfg(nix_at_least = "2.33")] #[doc(alias = "nix_derivation_from_json")] pub fn derivation_from_json(&mut self, json: &str) -> Result { let json_cstr = CString::new(json)?; @@ -282,6 +290,8 @@ impl Store { /// Add a derivation to the store. /// + /// **Requires Nix 2.33 or later.** + /// /// This computes the store path for the derivation and registers it in the store. /// The derivation itself is written to the store as a `.drv` file. /// @@ -290,6 +300,7 @@ impl Store { /// /// # Returns /// The store path of the derivation (ending in `.drv`). + #[cfg(nix_at_least = "2.33")] #[doc(alias = "nix_add_derivation")] pub fn add_derivation(&mut self, drv: &Derivation) -> Result { unsafe { @@ -306,6 +317,8 @@ impl Store { /// Build a derivation and return its outputs. /// + /// **Requires Nix 2.33 or later.** + /// /// This builds the derivation at the given store path and returns a map of output /// names to their realized store paths. The derivation must already exist in the store /// (see [`Store::add_derivation`]). @@ -316,6 +329,7 @@ impl Store { /// # Returns /// A [`BTreeMap`] mapping output names (e.g., "out", "dev", "doc") to their store paths. /// The map is ordered alphabetically by output name for deterministic iteration. + #[cfg(nix_at_least = "2.33")] #[doc(alias = "nix_store_realise")] pub fn realise(&mut self, path: &StorePath) -> Result> { let mut outputs = BTreeMap::new(); @@ -355,6 +369,8 @@ impl Store { /// Get the closure of a specific store path. /// + /// **Requires Nix 2.33 or later.** + /// /// Computes the filesystem closure (dependency graph) of a store path, with options /// to control the direction and which related paths to include. /// @@ -369,6 +385,7 @@ impl Store { /// /// # Returns /// A vector of store paths in the closure, in no particular order. + #[cfg(nix_at_least = "2.33")] #[doc(alias = "nix_store_get_fs_closure")] pub fn get_fs_closure( &mut self, @@ -548,6 +565,7 @@ mod tests { nix_bindings_util::settings::get("system") } + #[cfg(nix_at_least = "2.33")] fn create_test_derivation_json() -> String { let system = current_system().unwrap_or_else(|_| { // Fallback to Rust's platform detection @@ -580,6 +598,7 @@ mod tests { } #[test] + #[cfg(nix_at_least = "2.33")] fn derivation_from_json() { let (mut store, temp_dir) = create_temp_store(); let drv_json = create_test_derivation_json(); @@ -591,6 +610,7 @@ mod tests { } #[test] + #[cfg(nix_at_least = "2.33")] fn derivation_from_invalid_json() { let (mut store, temp_dir) = create_temp_store(); let result = store.derivation_from_json("not valid json"); @@ -600,6 +620,7 @@ mod tests { } #[test] + #[cfg(nix_at_least = "2.33")] fn add_derivation() { let (mut store, temp_dir) = create_temp_store(); let drv_json = create_test_derivation_json(); @@ -615,6 +636,7 @@ mod tests { } #[test] + #[cfg(nix_at_least = "2.33")] fn realise() { let (mut store, temp_dir) = create_temp_store(); let drv_json = create_test_derivation_json(); @@ -634,6 +656,7 @@ mod tests { drop(temp_dir); } + #[cfg(nix_at_least = "2.33")] fn create_multi_output_derivation_json() -> String { let system = current_system().unwrap_or_else(|_| { format!("{}-{}", std::env::consts::ARCH, std::env::consts::OS) @@ -681,6 +704,7 @@ mod tests { } #[test] + #[cfg(nix_at_least = "2.33")] fn realise_multi_output_ordering() { let (mut store, temp_dir) = create_temp_store(); let drv_json = create_multi_output_derivation_json(); @@ -700,6 +724,7 @@ mod tests { } #[test] + #[cfg(nix_at_least = "2.33")] fn realise_invalid_system() { let (mut store, temp_dir) = create_temp_store(); @@ -750,6 +775,7 @@ mod tests { } #[test] + #[cfg(nix_at_least = "2.33")] fn realise_builder_fails() { let (mut store, temp_dir) = create_temp_store(); @@ -803,6 +829,7 @@ mod tests { } #[test] + #[cfg(nix_at_least = "2.33")] fn realise_builder_no_output() { let (mut store, temp_dir) = create_temp_store(); @@ -856,6 +883,7 @@ mod tests { } #[test] + #[cfg(nix_at_least = "2.33")] fn get_fs_closure_with_outputs() { let (mut store, temp_dir) = create_temp_store(); let drv_json = create_test_derivation_json(); @@ -882,6 +910,7 @@ mod tests { } #[test] + #[cfg(nix_at_least = "2.33")] fn get_fs_closure_without_outputs() { let (mut store, temp_dir) = create_temp_store(); let drv_json = create_test_derivation_json(); @@ -905,6 +934,7 @@ mod tests { } #[test] + #[cfg(nix_at_least = "2.33")] fn get_fs_closure_flip_direction() { let (mut store, temp_dir) = create_temp_store(); let drv_json = create_test_derivation_json(); @@ -928,6 +958,7 @@ mod tests { } #[test] + #[cfg(nix_at_least = "2.33")] fn get_fs_closure_include_derivers() { let (mut store, temp_dir) = create_temp_store(); let drv_json = create_test_derivation_json(); From 220ff29bccb15964673a3efd61011abd1bfe9917 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 21 Oct 2025 23:21:25 +0200 Subject: [PATCH 193/306] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nix': 'github:NixOS/nix/ab095c029c7deef98b99c5249c09fe9a8a095800?narHash=sha256-JpSWFjOgPWeWb5bb%2BHMMGR%2BQ0dOH7nl2t4WgyBVaWx8%3D' (2025-09-01) → 'github:NixOS/nix/7e8db2eb59d8798047e8cc025a3eb18613a8918c?narHash=sha256-R6uBB3fef75wVM1OjiM0uYLLf2P5eTCWHPCQAbCaGzA%3D' (2025-10-21) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index dce5dbc..602976c 100644 --- a/flake.lock +++ b/flake.lock @@ -170,11 +170,11 @@ "nixpkgs-regression": "nixpkgs-regression" }, "locked": { - "lastModified": 1756762578, - "narHash": "sha256-JpSWFjOgPWeWb5bb+HMMGR+Q0dOH7nl2t4WgyBVaWx8=", + "lastModified": 1761069056, + "narHash": "sha256-R6uBB3fef75wVM1OjiM0uYLLf2P5eTCWHPCQAbCaGzA=", "owner": "NixOS", "repo": "nix", - "rev": "ab095c029c7deef98b99c5249c09fe9a8a095800", + "rev": "7e8db2eb59d8798047e8cc025a3eb18613a8918c", "type": "github" }, "original": { From 18da552952b1ecb832bc914e7500a4ed18874995 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 23 Oct 2025 19:42:05 +0200 Subject: [PATCH 194/306] fix: Pre-enable ca-derivations in tests This way we don't run into any race conditions when individual tests enable the feature. Specifically this affects the initialization of our default store for testing. The test suite *should* not be all that sensitive to the environment, but that's some future work to make sure. --- rust/nci.nix | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rust/nci.nix b/rust/nci.nix index fabe122..1509336 100644 --- a/rust/nci.nix +++ b/rust/nci.nix @@ -56,6 +56,10 @@ echo "Configuring relocated store at $NIX_REMOTE..." + # Create nix.conf with experimental features enabled + mkdir -p "$NIX_CONF_DIR" + echo "experimental-features = ca-derivations flakes" > "$NIX_CONF_DIR/nix.conf" + # Init ahead of time, because concurrent initialization is flaky ${ # Not using nativeBuildInputs because this should (hopefully) be From 510ba4abe231c8371c1717f588998d08fe7ebbbf Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 26 Oct 2025 22:49:14 +0100 Subject: [PATCH 195/306] fix: Uncrash the tests by keeping fetchers_settings around This is arguably a partial fix. This should either be modeled with lifetimes, or be addressed in Nix itself. --- rust/nix-bindings-flake/src/lib.rs | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/rust/nix-bindings-flake/src/lib.rs b/rust/nix-bindings-flake/src/lib.rs index ef8a963..96d5852 100644 --- a/rust/nix-bindings-flake/src/lib.rs +++ b/rust/nix-bindings-flake/src/lib.rs @@ -267,6 +267,7 @@ mod tests { // Only set experimental-features once to minimize the window where // concurrent Nix operations might read the setting while it's being modified INIT.call_once(|| { + nix_bindings_expr::eval_state::init().unwrap(); nix_bindings_util::settings::set("experimental-features", "flakes").unwrap(); }); } @@ -299,6 +300,7 @@ mod tests { init(); let gc_registration = gc_register_my_thread(); let store = Store::open(None, []).unwrap(); + let fetchers_settings = FetchersSettings::new().unwrap(); let flake_settings = FlakeSettings::new().unwrap(); let mut eval_state = EvalStateBuilder::new(store) .unwrap() @@ -326,7 +328,7 @@ mod tests { let flake_lock_flags = FlakeLockFlags::new(&flake_settings).unwrap(); let (flake_ref, fragment) = FlakeReference::parse_with_fragment( - &FetchersSettings::new().unwrap(), + &fetchers_settings, &flake_settings, &FlakeReferenceParseFlags::new(&flake_settings).unwrap(), &format!("path:{}#subthing", tmp_dir.path().display()), @@ -336,7 +338,7 @@ mod tests { assert_eq!(fragment, "subthing"); let locked_flake = LockedFlake::lock( - &FetchersSettings::new().unwrap(), + &fetchers_settings, &flake_settings, &eval_state, &flake_lock_flags, @@ -353,6 +355,7 @@ mod tests { assert_eq!(hello, "potato"); + drop(fetchers_settings); drop(tmp_dir); drop(gc_registration); } @@ -362,6 +365,7 @@ mod tests { init(); let gc_registration = gc_register_my_thread(); let store = Store::open(None, []).unwrap(); + let fetchers_settings = FetchersSettings::new().unwrap(); let flake_settings = FlakeSettings::new().unwrap(); let mut eval_state = EvalStateBuilder::new(store) .unwrap() @@ -382,6 +386,8 @@ mod tests { let flake_dir_a_str = flake_dir_a.to_str().unwrap(); let flake_dir_c_str = flake_dir_c.to_str().unwrap(); + assert!(!flake_dir_a_str.is_empty()); + assert!(!flake_dir_c_str.is_empty()); // a std::fs::write( @@ -434,7 +440,7 @@ mod tests { .unwrap(); let (flake_ref_a, fragment) = FlakeReference::parse_with_fragment( - &FetchersSettings::new().unwrap(), + &fetchers_settings, &flake_settings, &flake_reference_parse_flags, &format!("path:{}", &flake_dir_a_str), @@ -448,7 +454,7 @@ mod tests { flake_lock_flags.set_mode_check().unwrap(); let locked_flake = LockedFlake::lock( - &FetchersSettings::new().unwrap(), + &fetchers_settings, &flake_settings, &eval_state, &flake_lock_flags, @@ -465,7 +471,7 @@ mod tests { flake_lock_flags.set_mode_virtual().unwrap(); let locked_flake = LockedFlake::lock( - &FetchersSettings::new().unwrap(), + &fetchers_settings, &flake_settings, &eval_state, &flake_lock_flags, @@ -487,7 +493,7 @@ mod tests { flake_lock_flags.set_mode_check().unwrap(); let locked_flake = LockedFlake::lock( - &FetchersSettings::new().unwrap(), + &fetchers_settings, &flake_settings, &eval_state, &flake_lock_flags, @@ -507,7 +513,7 @@ mod tests { flake_lock_flags.set_mode_write_as_needed().unwrap(); let locked_flake = LockedFlake::lock( - &FetchersSettings::new().unwrap(), + &fetchers_settings, &flake_settings, &eval_state, &flake_lock_flags, @@ -527,7 +533,7 @@ mod tests { flake_lock_flags.set_mode_check().unwrap(); let locked_flake = LockedFlake::lock( - &FetchersSettings::new().unwrap(), + &fetchers_settings, &flake_settings, &eval_state, &flake_lock_flags, @@ -548,7 +554,7 @@ mod tests { flake_lock_flags.set_mode_write_as_needed().unwrap(); let (flake_ref_c, fragment) = FlakeReference::parse_with_fragment( - &FetchersSettings::new().unwrap(), + &fetchers_settings, &flake_settings, &flake_reference_parse_flags, &format!("path:{}", &flake_dir_c_str), @@ -561,7 +567,7 @@ mod tests { .unwrap(); let locked_flake = LockedFlake::lock( - &FetchersSettings::new().unwrap(), + &fetchers_settings, &flake_settings, &eval_state, &flake_lock_flags, @@ -584,7 +590,7 @@ mod tests { flake_lock_flags.set_mode_check().unwrap(); let locked_flake = LockedFlake::lock( - &FetchersSettings::new().unwrap(), + &fetchers_settings, &flake_settings, &eval_state, &flake_lock_flags, @@ -599,6 +605,7 @@ mod tests { let hello = eval_state.require_string(&hello).unwrap(); assert_eq!(hello, "BOB"); + drop(fetchers_settings); drop(tmp_dir); drop(gc_registration); } From 55eacf43c3638c353472c47ebdd70d33d16fbae5 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 26 Oct 2025 23:29:13 +0100 Subject: [PATCH 196/306] maint: Move to /rust/* to / This makes it easier for tooling to find the Rust stuff. Rust/non-rust is not a useful distinction in this repo anymore anyway. --- rust/Cargo.lock => Cargo.lock | 0 rust/Cargo.toml => Cargo.toml | 0 rust/bindgen-gcc.sh => bindgen-gcc.sh | 0 dev/flake-module.nix | 4 ++-- doc/hacking/test-ffi.md | 4 ++-- flake.nix | 2 +- rust/nci.nix => nci.nix | 0 .../Cargo.toml | 0 .../README.md | 0 .../build.rs | 0 .../include/nix-c-raw.h | 0 .../src/lib.rs | 0 {rust/nix-bindings-expr => nix-bindings-expr}/Cargo.toml | 0 .../nix-bindings-expr => nix-bindings-expr}/src/eval_state.rs | 0 {rust/nix-bindings-expr => nix-bindings-expr}/src/lib.rs | 0 {rust/nix-bindings-expr => nix-bindings-expr}/src/primop.rs | 0 {rust/nix-bindings-expr => nix-bindings-expr}/src/value.rs | 0 .../src/value/__private.rs | 0 .../Cargo.toml | 0 .../src/lib.rs | 0 {rust/nix-bindings-flake => nix-bindings-flake}/Cargo.toml | 0 {rust/nix-bindings-flake => nix-bindings-flake}/src/lib.rs | 0 {rust/nix-bindings-store => nix-bindings-store}/Cargo.toml | 0 {rust/nix-bindings-store => nix-bindings-store}/build.rs | 0 .../src/derivation.rs | 0 {rust/nix-bindings-store => nix-bindings-store}/src/lib.rs | 0 {rust/nix-bindings-store => nix-bindings-store}/src/path.rs | 0 {rust/nix-bindings-store => nix-bindings-store}/src/store.rs | 0 {rust/nix-bindings-util => nix-bindings-util}/Cargo.toml | 0 {rust/nix-bindings-util => nix-bindings-util}/src/context.rs | 0 {rust/nix-bindings-util => nix-bindings-util}/src/lib.rs | 0 {rust/nix-bindings-util => nix-bindings-util}/src/settings.rs | 0 .../src/string_return.rs | 0 33 files changed, 5 insertions(+), 5 deletions(-) rename rust/Cargo.lock => Cargo.lock (100%) rename rust/Cargo.toml => Cargo.toml (100%) rename rust/bindgen-gcc.sh => bindgen-gcc.sh (100%) rename rust/nci.nix => nci.nix (100%) rename {rust/nix-bindings-bindgen-raw => nix-bindings-bindgen-raw}/Cargo.toml (100%) rename {rust/nix-bindings-bindgen-raw => nix-bindings-bindgen-raw}/README.md (100%) rename {rust/nix-bindings-bindgen-raw => nix-bindings-bindgen-raw}/build.rs (100%) rename {rust/nix-bindings-bindgen-raw => nix-bindings-bindgen-raw}/include/nix-c-raw.h (100%) rename {rust/nix-bindings-bindgen-raw => nix-bindings-bindgen-raw}/src/lib.rs (100%) rename {rust/nix-bindings-expr => nix-bindings-expr}/Cargo.toml (100%) rename {rust/nix-bindings-expr => nix-bindings-expr}/src/eval_state.rs (100%) rename {rust/nix-bindings-expr => nix-bindings-expr}/src/lib.rs (100%) rename {rust/nix-bindings-expr => nix-bindings-expr}/src/primop.rs (100%) rename {rust/nix-bindings-expr => nix-bindings-expr}/src/value.rs (100%) rename {rust/nix-bindings-expr => nix-bindings-expr}/src/value/__private.rs (100%) rename {rust/nix-bindings-fetchers => nix-bindings-fetchers}/Cargo.toml (100%) rename {rust/nix-bindings-fetchers => nix-bindings-fetchers}/src/lib.rs (100%) rename {rust/nix-bindings-flake => nix-bindings-flake}/Cargo.toml (100%) rename {rust/nix-bindings-flake => nix-bindings-flake}/src/lib.rs (100%) rename {rust/nix-bindings-store => nix-bindings-store}/Cargo.toml (100%) rename {rust/nix-bindings-store => nix-bindings-store}/build.rs (100%) rename {rust/nix-bindings-store => nix-bindings-store}/src/derivation.rs (100%) rename {rust/nix-bindings-store => nix-bindings-store}/src/lib.rs (100%) rename {rust/nix-bindings-store => nix-bindings-store}/src/path.rs (100%) rename {rust/nix-bindings-store => nix-bindings-store}/src/store.rs (100%) rename {rust/nix-bindings-util => nix-bindings-util}/Cargo.toml (100%) rename {rust/nix-bindings-util => nix-bindings-util}/src/context.rs (100%) rename {rust/nix-bindings-util => nix-bindings-util}/src/lib.rs (100%) rename {rust/nix-bindings-util => nix-bindings-util}/src/settings.rs (100%) rename {rust/nix-bindings-util => nix-bindings-util}/src/string_return.rs (100%) diff --git a/rust/Cargo.lock b/Cargo.lock similarity index 100% rename from rust/Cargo.lock rename to Cargo.lock diff --git a/rust/Cargo.toml b/Cargo.toml similarity index 100% rename from rust/Cargo.toml rename to Cargo.toml diff --git a/rust/bindgen-gcc.sh b/bindgen-gcc.sh similarity index 100% rename from rust/bindgen-gcc.sh rename to bindgen-gcc.sh diff --git a/dev/flake-module.nix b/dev/flake-module.nix index 38583c1..811ae0e 100644 --- a/dev/flake-module.nix +++ b/dev/flake-module.nix @@ -16,7 +16,7 @@ pre-commit.settings.hooks.nixfmt-rfc-style.enable = true; # Temporarily disable rustfmt due to configuration issues # pre-commit.settings.hooks.rustfmt.enable = true; - pre-commit.settings.settings.rust.cargoManifestPath = "./rust/Cargo.toml"; + pre-commit.settings.settings.rust.cargoManifestPath = "./Cargo.toml"; # Check that we're using ///-style doc comments in Rust code. # @@ -78,7 +78,7 @@ ]; shellHook = '' ${config.pre-commit.installationScript} - source ${../rust/bindgen-gcc.sh} + source ${../bindgen-gcc.sh} echo 1>&2 "Welcome to the development shell!" ''; # rust-analyzer needs a NIX_PATH for some reason diff --git a/doc/hacking/test-ffi.md b/doc/hacking/test-ffi.md index 2ae260e..c19c485 100644 --- a/doc/hacking/test-ffi.md +++ b/doc/hacking/test-ffi.md @@ -3,9 +3,9 @@ If `cargo-valgrind` is broken, you may run `valgrind` manually. -1. `cd rust; cargo test -v` +1. `cargo test -v` 2. find the relevant test suite executable in the log - - example: `/home/user/src/nix-bindings-rust/rust/target/debug/deps/nix_util-036ec381a9e3fd6d` + - example: `/home/user/src/nix-bindings-rust/target/debug/deps/nix_util-036ec381a9e3fd6d` 3. `valgrind --leak-check=full ` 4. check that - `definitely lost: 0 bytes in 0 blocks` diff --git a/flake.nix b/flake.nix index af537e3..b14286b 100644 --- a/flake.nix +++ b/flake.nix @@ -21,7 +21,7 @@ imports = [ inputs.nix-cargo-integration.flakeModule inputs.flake-parts.flakeModules.partitions - ./rust/nci.nix + ./nci.nix ]; systems = [ "x86_64-linux" diff --git a/rust/nci.nix b/nci.nix similarity index 100% rename from rust/nci.nix rename to nci.nix diff --git a/rust/nix-bindings-bindgen-raw/Cargo.toml b/nix-bindings-bindgen-raw/Cargo.toml similarity index 100% rename from rust/nix-bindings-bindgen-raw/Cargo.toml rename to nix-bindings-bindgen-raw/Cargo.toml diff --git a/rust/nix-bindings-bindgen-raw/README.md b/nix-bindings-bindgen-raw/README.md similarity index 100% rename from rust/nix-bindings-bindgen-raw/README.md rename to nix-bindings-bindgen-raw/README.md diff --git a/rust/nix-bindings-bindgen-raw/build.rs b/nix-bindings-bindgen-raw/build.rs similarity index 100% rename from rust/nix-bindings-bindgen-raw/build.rs rename to nix-bindings-bindgen-raw/build.rs diff --git a/rust/nix-bindings-bindgen-raw/include/nix-c-raw.h b/nix-bindings-bindgen-raw/include/nix-c-raw.h similarity index 100% rename from rust/nix-bindings-bindgen-raw/include/nix-c-raw.h rename to nix-bindings-bindgen-raw/include/nix-c-raw.h diff --git a/rust/nix-bindings-bindgen-raw/src/lib.rs b/nix-bindings-bindgen-raw/src/lib.rs similarity index 100% rename from rust/nix-bindings-bindgen-raw/src/lib.rs rename to nix-bindings-bindgen-raw/src/lib.rs diff --git a/rust/nix-bindings-expr/Cargo.toml b/nix-bindings-expr/Cargo.toml similarity index 100% rename from rust/nix-bindings-expr/Cargo.toml rename to nix-bindings-expr/Cargo.toml diff --git a/rust/nix-bindings-expr/src/eval_state.rs b/nix-bindings-expr/src/eval_state.rs similarity index 100% rename from rust/nix-bindings-expr/src/eval_state.rs rename to nix-bindings-expr/src/eval_state.rs diff --git a/rust/nix-bindings-expr/src/lib.rs b/nix-bindings-expr/src/lib.rs similarity index 100% rename from rust/nix-bindings-expr/src/lib.rs rename to nix-bindings-expr/src/lib.rs diff --git a/rust/nix-bindings-expr/src/primop.rs b/nix-bindings-expr/src/primop.rs similarity index 100% rename from rust/nix-bindings-expr/src/primop.rs rename to nix-bindings-expr/src/primop.rs diff --git a/rust/nix-bindings-expr/src/value.rs b/nix-bindings-expr/src/value.rs similarity index 100% rename from rust/nix-bindings-expr/src/value.rs rename to nix-bindings-expr/src/value.rs diff --git a/rust/nix-bindings-expr/src/value/__private.rs b/nix-bindings-expr/src/value/__private.rs similarity index 100% rename from rust/nix-bindings-expr/src/value/__private.rs rename to nix-bindings-expr/src/value/__private.rs diff --git a/rust/nix-bindings-fetchers/Cargo.toml b/nix-bindings-fetchers/Cargo.toml similarity index 100% rename from rust/nix-bindings-fetchers/Cargo.toml rename to nix-bindings-fetchers/Cargo.toml diff --git a/rust/nix-bindings-fetchers/src/lib.rs b/nix-bindings-fetchers/src/lib.rs similarity index 100% rename from rust/nix-bindings-fetchers/src/lib.rs rename to nix-bindings-fetchers/src/lib.rs diff --git a/rust/nix-bindings-flake/Cargo.toml b/nix-bindings-flake/Cargo.toml similarity index 100% rename from rust/nix-bindings-flake/Cargo.toml rename to nix-bindings-flake/Cargo.toml diff --git a/rust/nix-bindings-flake/src/lib.rs b/nix-bindings-flake/src/lib.rs similarity index 100% rename from rust/nix-bindings-flake/src/lib.rs rename to nix-bindings-flake/src/lib.rs diff --git a/rust/nix-bindings-store/Cargo.toml b/nix-bindings-store/Cargo.toml similarity index 100% rename from rust/nix-bindings-store/Cargo.toml rename to nix-bindings-store/Cargo.toml diff --git a/rust/nix-bindings-store/build.rs b/nix-bindings-store/build.rs similarity index 100% rename from rust/nix-bindings-store/build.rs rename to nix-bindings-store/build.rs diff --git a/rust/nix-bindings-store/src/derivation.rs b/nix-bindings-store/src/derivation.rs similarity index 100% rename from rust/nix-bindings-store/src/derivation.rs rename to nix-bindings-store/src/derivation.rs diff --git a/rust/nix-bindings-store/src/lib.rs b/nix-bindings-store/src/lib.rs similarity index 100% rename from rust/nix-bindings-store/src/lib.rs rename to nix-bindings-store/src/lib.rs diff --git a/rust/nix-bindings-store/src/path.rs b/nix-bindings-store/src/path.rs similarity index 100% rename from rust/nix-bindings-store/src/path.rs rename to nix-bindings-store/src/path.rs diff --git a/rust/nix-bindings-store/src/store.rs b/nix-bindings-store/src/store.rs similarity index 100% rename from rust/nix-bindings-store/src/store.rs rename to nix-bindings-store/src/store.rs diff --git a/rust/nix-bindings-util/Cargo.toml b/nix-bindings-util/Cargo.toml similarity index 100% rename from rust/nix-bindings-util/Cargo.toml rename to nix-bindings-util/Cargo.toml diff --git a/rust/nix-bindings-util/src/context.rs b/nix-bindings-util/src/context.rs similarity index 100% rename from rust/nix-bindings-util/src/context.rs rename to nix-bindings-util/src/context.rs diff --git a/rust/nix-bindings-util/src/lib.rs b/nix-bindings-util/src/lib.rs similarity index 100% rename from rust/nix-bindings-util/src/lib.rs rename to nix-bindings-util/src/lib.rs diff --git a/rust/nix-bindings-util/src/settings.rs b/nix-bindings-util/src/settings.rs similarity index 100% rename from rust/nix-bindings-util/src/settings.rs rename to nix-bindings-util/src/settings.rs diff --git a/rust/nix-bindings-util/src/string_return.rs b/nix-bindings-util/src/string_return.rs similarity index 100% rename from rust/nix-bindings-util/src/string_return.rs rename to nix-bindings-util/src/string_return.rs From 26b7cb211675e0f4d152afc2749035585b3d9cf6 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 27 Oct 2025 01:37:18 +0100 Subject: [PATCH 197/306] feat: Expose module for setting up the build and test environment. --- README.md | 33 ++++++++ dev/flake-module.nix | 8 +- flake.nix | 176 ++++++++++++++++++++++++++++++++++++++++++- nci.nix | 48 ++---------- 4 files changed, 221 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index aa5eb40..5e85853 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,39 @@ Use the Nix [C API] from Rust. +## Build with `nix-cargo-integration` + +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)). + +The dependency on Nix is taken care of with the [`nix-bindings-rust` flake-parts module](). + +Example usage: + +```nix +{ + outputs = + inputs@{ self, flake-parts, ... }: + flake-parts.lib.mkFlake { inherit inputs; } + { + imports = [ + inputs.nix-cargo-integration.flakeModule + inputs.nix-bindings-rust.modules.flake.default + ]; + + perSystem = { config, pkgs, ... }: { + # optional: + nix-bindings-rust.nixPackage = pkgs.nix; + + nci.projects."myproject" = { + depsDrvConfig = { + imports = [ config.nix-bindings-rust.nciBuildConfig ]; + }; + }; + }; + }; +} +``` + ## Hacking The following will open a shell with dependencies, and install pre-commit for automatic formatting. diff --git a/dev/flake-module.nix b/dev/flake-module.nix index 811ae0e..6fb0317 100644 --- a/dev/flake-module.nix +++ b/dev/flake-module.nix @@ -10,8 +10,14 @@ inputs.hercules-ci-effects.flakeModule ]; perSystem = - { config, pkgs, ... }: { + config, + pkgs, + inputs', + ... + }: + { + nix-bindings-rust.nixPackage = inputs'.nix.packages.default; pre-commit.settings.hooks.nixfmt-rfc-style.enable = true; # Temporarily disable rustfmt due to configuration issues diff --git a/flake.nix b/flake.nix index b14286b..f752825 100644 --- a/flake.nix +++ b/flake.nix @@ -13,14 +13,158 @@ outputs = inputs@{ self, flake-parts, ... }: flake-parts.lib.mkFlake { inherit inputs; } ( - { + toplevel@{ lib, + withSystem, ... }: + let + /** + Makes perSystem.nix-bindings-rust available. + */ + flake-parts-modules.basic = + { + config, + flake-parts-lib, + withSystem, + ... + }: + { + options.perSystem = flake-parts-lib.mkPerSystemOption ( + { config, pkgs, ... }: + let + cfg = config.nix-bindings-rust; + in + { + options.nix-bindings-rust = { + nixPackage = lib.mkOption { + type = lib.types.package; + default = pkgs.nix; + defaultText = lib.literalMD "pkgs.nix"; + description = '' + The Nix package to use when building the `nix-bindings-...` crates. + ''; + }; + nciBuildConfig = lib.mkOption { + type = lib.types.deferredModule; + description = '' + A module to load into your nix-cargo-integration + [`perSystem.nci.projects..depsDrvConfig`](https://flake.parts/options/nix-cargo-integration.html#opt-perSystem.nci.projects._name_.depsDrvConfig) or similar such options. + + Example: + ```nix + perSystem = perSystem@{ config, ... }: { + nci.projects."my_project".depsDrvConfig = perSystem.config.nix-bindings-rust.nciBuildConfig; + } + ``` + ''; + }; + }; + config.nix-bindings-rust = { + nciBuildConfig = { + mkDerivation = { + buildInputs = [ + # stdbool.h + pkgs.stdenv.cc + ] + ++ ( + if cfg.nixPackage ? libs then + let + l = cfg.nixPackage.libs; + in + [ + l.nix-expr-c + l.nix-store-c + l.nix-util-c + l.nix-fetchers-c or null # Nix >= 2.29 + l.nix-flake-c + ] + else + [ cfg.nixPackage ] + ); + nativeBuildInputs = [ + pkgs.pkg-config + ]; + # bindgen uses clang to generate bindings, but it doesn't know where to + # find our stdenv cc's headers, so when it's gcc, we need to tell it. + postConfigure = lib.optionalString pkgs.stdenv.cc.isGNU '' + source ${./bindgen-gcc.sh} + ''; + }; + # NOTE: duplicated in flake.nix devShell + env = { + LIBCLANG_PATH = lib.makeLibraryPath [ pkgs.buildPackages.llvmPackages.clang-unwrapped ]; + BINDGEN_EXTRA_CLANG_ARGS = + # Work around missing [[deprecated]] in clang + "-x c++ -std=c++2a"; + } + // lib.optionalAttrs pkgs.stdenv.cc.isGNU { + # Avoid cc wrapper, because we only need to add the compiler/"system" dirs + NIX_CC_UNWRAPPED = "${pkgs.stdenv.cc.cc}/bin/gcc"; + }; + }; + }; + } + ); + }; + + /** + Adds flake checks that test the bindings with the provided nix package. + */ + flake-parts-modules.tested = + # Consumer toplevel + { options, config, ... }: + { + imports = [ flake-parts-modules.basic ]; + config.perSystem = + # Consumer perSystem + consumerPerSystem@{ + lib, + config, + system, + pkgs, + ... + }: + let + # nix-bindings-rust's perSystem, but with the consumer's `pkgs` + nix-bindings-rust-perSystemConfig = + # Extending our own perSystem, not the consumer's perSystem! + toplevel.config.partitions.testing-support.module.nix-bindings-rust.internalWithSystem system + ({ extendModules, ... }: extendModules) + { + modules = [ + { + config = { + # Overriding our `perSystem` to use the consumer's `pkgs` + _module.args.pkgs = lib.mkForce consumerPerSystem.pkgs; + # ... and `nixPackage` + nix-bindings-rust.nixPackage = lib.mkForce consumerPerSystem.config.nix-bindings-rust.nixPackage; + }; + } + ]; + }; + in + { + key = "nix-bindings-rust-add-checks"; + config.checks = lib.concatMapAttrs ( + k: v: + lib.optionalAttrs (lib.strings.hasPrefix "nix-bindings-" k && !lib.strings.hasSuffix "-clippy" k) { + "dependency-${k}" = v; + } + ) nix-bindings-rust-perSystemConfig.config.checks; + }; + }; + + flake-parts-modules.default = flake-parts-modules.tested; + + in { imports = [ inputs.nix-cargo-integration.flakeModule inputs.flake-parts.flakeModules.partitions + inputs.flake-parts.flakeModules.modules + # dogfood + flake-parts-modules.tested ./nci.nix ]; systems = [ @@ -44,10 +188,40 @@ partitionedAttrs.devShells = "dev"; partitionedAttrs.checks = "dev"; partitionedAttrs.herculesCI = "dev"; + # Packages are basically just checks in this project; a library by + # itself is not useful. That's just not how the Rust integration works. + # By taking `packages` from `dev` we benefit from this dev-only definition: + # nix-bindings-rust.nixPackage = inputs'.nix.packages.default; + partitionedAttrs.packages = "dev"; + partitions.dev.extraInputsFlake = ./dev; partitions.dev.module = { imports = [ ./dev/flake-module.nix ]; }; + + # A partition that doesn't dogfood the flake-parts-modules.tested module + # so that we can actually retrieve `checks` without infinite recursions + # from trying to include the dogfooded attrs. + partitions.testing-support.module = + { withSystem, ... }: + { + # Make a clean withSystem available for consumers + options.nix-bindings-rust.internalWithSystem = lib.mkOption { internal = true; }; + config = { + nix-bindings-rust.internalWithSystem = withSystem; + perSystem = { + # Remove dogfooded checks. This configuration's checks are + # *consumed* by nix-bindings-rust-add-checks, so they should + # *NOT* also be *produced* by it. + disabledModules = [ { key = "nix-bindings-rust-add-checks"; } ]; + }; + }; + }; + + # flake output attributes + flake = { + modules.flake = flake-parts-modules; + }; } ); } diff --git a/nci.nix b/nci.nix index 1509336..f0b4b50 100644 --- a/nci.nix +++ b/nci.nix @@ -11,33 +11,12 @@ nci.projects.nix-bindings = { path = ./.; drvConfig = { + imports = [ + # Downstream projects import this into depsDrvConfig instead + config.nix-bindings-rust.nciBuildConfig + ]; + # Extra settings for running the tests mkDerivation = { - buildInputs = [ - # stdbool.h - pkgs.stdenv.cc - ] - ++ ( - if config.packages.nix ? libs then - let - l = config.packages.nix.libs; - in - [ - l.nix-expr-c - l.nix-store-c - l.nix-util-c - l.nix-flake-c - ] - else - [ config.packages.nix ] - ); - nativeBuildInputs = [ - pkgs.pkg-config - ]; - # bindgen uses clang to generate bindings, but it doesn't know where to - # find our stdenv cc's headers, so when it's gcc, we need to tell it. - postConfigure = lib.optionalString pkgs.stdenv.cc.isGNU '' - source ${./bindgen-gcc.sh} - ''; # Prepare the environment for Nix to work. # Nix does not provide a suitable environment for running itself in # the sandbox - not by default. We configure it to use a relocated store. @@ -61,26 +40,11 @@ echo "experimental-features = ca-derivations flakes" > "$NIX_CONF_DIR/nix.conf" # Init ahead of time, because concurrent initialization is flaky - ${ - # Not using nativeBuildInputs because this should (hopefully) be - # the only place where we need a nix binary. Let's stay in control. - pkgs.buildPackages.nix - }/bin/nix-store --init + ${config.nix-bindings-rust.nixPackage}/bin/nix-store --init echo "Store initialized." ''; }; - # NOTE: duplicated in flake.nix devShell - env = { - LIBCLANG_PATH = lib.makeLibraryPath [ pkgs.buildPackages.llvmPackages.clang-unwrapped ]; - BINDGEN_EXTRA_CLANG_ARGS = - # Work around missing [[deprecated]] in clang - "-x c++ -std=c++2a"; - } - // lib.optionalAttrs pkgs.stdenv.cc.isGNU { - # Avoid cc wrapper, because we only need to add the compiler/"system" dirs - NIX_CC_UNWRAPPED = "${pkgs.stdenv.cc.cc}/bin/gcc"; - }; }; }; }; From db84b15d8f5d6bab3c6f754a3550e917a1eab7c9 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 27 Oct 2025 13:57:51 +0100 Subject: [PATCH 198/306] maint: Fix warnings --- dev/flake-module.nix | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dev/flake-module.nix b/dev/flake-module.nix index 6fb0317..d19b37b 100644 --- a/dev/flake-module.nix +++ b/dev/flake-module.nix @@ -1,7 +1,5 @@ { - lib, inputs, - withSystem, ... }: { @@ -92,7 +90,7 @@ }; }; herculesCI = - hci@{ config, ... }: + { config, ... }: { ciSystems = [ "x86_64-linux" ]; }; From ae7709def86897e0405c5508dad067bb6aae7abb Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 27 Oct 2025 14:59:29 +0100 Subject: [PATCH 199/306] doc: Render declarations without the deferredModule indirection deferredModule sets a fake `file` with "via option ...", which interferes with rendering on flake.parts. --- flake.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/flake.nix b/flake.nix index f752825..7414299 100644 --- a/flake.nix +++ b/flake.nix @@ -30,6 +30,7 @@ ... }: { + _file = ./flake.nix; options.perSystem = flake-parts-lib.mkPerSystemOption ( { config, pkgs, ... }: let @@ -115,6 +116,7 @@ # Consumer toplevel { options, config, ... }: { + _file = ./flake.nix; imports = [ flake-parts-modules.basic ]; config.perSystem = # Consumer perSystem From 369e679594848a21c0fa821b1badce652c864860 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 26 Nov 2025 21:34:37 -0500 Subject: [PATCH 200/306] Update to latest Nix master --- flake.lock | 6 ++--- nix-bindings-store/src/store.rs | 40 ++++++++++++++++++++------------- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/flake.lock b/flake.lock index 602976c..2fd1d19 100644 --- a/flake.lock +++ b/flake.lock @@ -170,11 +170,11 @@ "nixpkgs-regression": "nixpkgs-regression" }, "locked": { - "lastModified": 1761069056, - "narHash": "sha256-R6uBB3fef75wVM1OjiM0uYLLf2P5eTCWHPCQAbCaGzA=", + "lastModified": 1764949869, + "narHash": "sha256-esRP6gaYG8d3gLkfr306HBgWYe2jEfHIJq6A1W/pdYw=", "owner": "NixOS", "repo": "nix", - "rev": "7e8db2eb59d8798047e8cc025a3eb18613a8918c", + "rev": "5f42e5ebb797433e3db3cfbc81f9cc3139903636", "type": "github" }, "original": { diff --git a/nix-bindings-store/src/store.rs b/nix-bindings-store/src/store.rs index 5e0885f..33813f2 100644 --- a/nix-bindings-store/src/store.rs +++ b/nix-bindings-store/src/store.rs @@ -581,8 +581,10 @@ mod tests { "out": "/1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9", "system": "{}" }}, - "inputDrvs": {{}}, - "inputSrcs": [], + "inputs": {{ + "drvs": {{}}, + "srcs": [] + }}, "name": "myname", "outputs": {{ "out": {{ @@ -591,7 +593,7 @@ mod tests { }} }}, "system": "{}", - "version": 3 + "version": 4 }}"#, system, system ) @@ -664,7 +666,7 @@ mod tests { format!( r#"{{ - "version": 3, + "version": 4, "name": "multi-output-test", "system": "{}", "builder": "/bin/sh", @@ -684,8 +686,10 @@ mod tests { "outj": "/0gkw1366qklqfqb2lw1pikgdqh3cmi3nw6f1z04an44ia863nxaz", "outa": "/039akv9zfpihrkrv4pl54f3x231x362bll9afblsgfqgvx96h198" }}, - "inputDrvs": {{}}, - "inputSrcs": [], + "inputs": {{ + "drvs": {{}}, + "srcs": [] + }}, "outputs": {{ "outd": {{ "hashAlgo": "sha256", "method": "nar" }}, "outf": {{ "hashAlgo": "sha256", "method": "nar" }}, @@ -740,8 +744,10 @@ mod tests { "out": "/1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9", "system": "{}" }}, - "inputDrvs": {{}}, - "inputSrcs": [], + "inputs": {{ + "drvs": {{}}, + "srcs": [] + }}, "name": "myname", "outputs": {{ "out": {{ @@ -750,7 +756,7 @@ mod tests { }} }}, "system": "{}", - "version": 3 + "version": 4 }}"#, system, system ); @@ -794,8 +800,10 @@ mod tests { "out": "/1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9", "system": "{}" }}, - "inputDrvs": {{}}, - "inputSrcs": [], + "inputs": {{ + "drvs": {{}}, + "srcs": [] + }}, "name": "failing", "outputs": {{ "out": {{ @@ -804,7 +812,7 @@ mod tests { }} }}, "system": "{}", - "version": 3 + "version": 4 }}"#, system, system ); @@ -848,8 +856,10 @@ mod tests { "out": "/1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9", "system": "{}" }}, - "inputDrvs": {{}}, - "inputSrcs": [], + "inputs": {{ + "drvs": {{}}, + "srcs": [] + }}, "name": "no-output", "outputs": {{ "out": {{ @@ -858,7 +868,7 @@ mod tests { }} }}, "system": "{}", - "version": 3 + "version": 4 }}"#, system, system ); From cfda626614853f7227a362ced715956ac12e1982 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 5 Dec 2025 16:51:15 -0500 Subject: [PATCH 201/306] Format, and keep formatted --- dev/flake-module.nix | 2 +- flake.lock | 23 +++++++++- flake.nix | 26 ++++++++---- nci.nix | 2 - nix-bindings-expr/src/eval_state.rs | 11 ++--- nix-bindings-flake/src/lib.rs | 5 ++- nix-bindings-store/src/store.rs | 66 +++++++++++++++++++---------- 7 files changed, 94 insertions(+), 41 deletions(-) diff --git a/dev/flake-module.nix b/dev/flake-module.nix index d19b37b..374fba3 100644 --- a/dev/flake-module.nix +++ b/dev/flake-module.nix @@ -90,7 +90,7 @@ }; }; herculesCI = - { config, ... }: + { ... }: { ciSystems = [ "x86_64-linux" ]; }; diff --git a/flake.lock b/flake.lock index 602976c..0d023db 100644 --- a/flake.lock +++ b/flake.lock @@ -344,7 +344,8 @@ "flake-parts": "flake-parts", "nix": "nix", "nix-cargo-integration": "nix-cargo-integration", - "nixpkgs": "nixpkgs" + "nixpkgs": "nixpkgs", + "treefmt-nix": "treefmt-nix" } }, "rust-overlay": { @@ -411,6 +412,26 @@ "repo": "treefmt-nix", "type": "github" } + }, + "treefmt-nix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1762938485, + "narHash": "sha256-AlEObg0syDl+Spi4LsZIBrjw+snSVU4T8MOeuZJUJjM=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "5b4ee75aeefd1e2d5a1cc43cf6ba65eba75e83e4", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index 7414299..b33e9ce 100644 --- a/flake.nix +++ b/flake.nix @@ -8,14 +8,15 @@ nix-cargo-integration.url = "github:yusdacra/nix-cargo-integration"; nix-cargo-integration.inputs.nixpkgs.follows = "nixpkgs"; nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + treefmt-nix.url = "github:numtide/treefmt-nix"; + treefmt-nix.inputs.nixpkgs.follows = "nixpkgs"; }; outputs = - inputs@{ self, flake-parts, ... }: + inputs@{ flake-parts, ... }: flake-parts.lib.mkFlake { inherit inputs; } ( toplevel@{ lib, - withSystem, ... }: let @@ -24,9 +25,7 @@ */ flake-parts-modules.basic = { - config, flake-parts-lib, - withSystem, ... }: { @@ -114,7 +113,7 @@ */ flake-parts-modules.tested = # Consumer toplevel - { options, config, ... }: + { config, ... }: { _file = ./flake.nix; imports = [ flake-parts-modules.basic ]; @@ -165,6 +164,7 @@ inputs.nix-cargo-integration.flakeModule inputs.flake-parts.flakeModules.partitions inputs.flake-parts.flakeModules.modules + inputs.treefmt-nix.flakeModule # dogfood flake-parts-modules.tested ./nci.nix @@ -177,14 +177,24 @@ ]; perSystem = { - config, - self', inputs', - pkgs, ... }: { packages.nix = inputs'.nix.packages.nix; + + treefmt = { + # Used to find the project root + projectRootFile = "flake.lock"; + + programs.rustfmt = { + enable = true; + edition = "2021"; + }; + programs.nixfmt.enable = true; + programs.deadnix.enable = true; + #programs.clang-format.enable = true; + }; }; partitionedAttrs.devShells = "dev"; diff --git a/nci.nix b/nci.nix index f0b4b50..053a832 100644 --- a/nci.nix +++ b/nci.nix @@ -1,9 +1,7 @@ { perSystem = { - lib, config, - pkgs, ... }: { diff --git a/nix-bindings-expr/src/eval_state.rs b/nix-bindings-expr/src/eval_state.rs index 0cdde8c..5f92b92 100644 --- a/nix-bindings-expr/src/eval_state.rs +++ b/nix-bindings-expr/src/eval_state.rs @@ -138,7 +138,9 @@ use nix_bindings_bindgen_raw as raw; use nix_bindings_store::path::StorePath; use nix_bindings_store::store::{Store, StoreWeak}; use nix_bindings_util::context::Context; -use nix_bindings_util::string_return::{callback_get_result_string, callback_get_result_string_data}; +use nix_bindings_util::string_return::{ + callback_get_result_string, callback_get_result_string_data, +}; use nix_bindings_util::{check_call, check_call_opt_key, result_string_init}; use std::ffi::{c_char, CString}; use std::iter::FromIterator; @@ -1217,7 +1219,8 @@ pub fn test_init() { // which causes an error. So we set a custom build dir here. // Only available on linux if cfg!(target_os = "linux") { - nix_bindings_util::settings::set("sandbox-build-dir", "/custom-build-dir-for-test").unwrap(); + nix_bindings_util::settings::set("sandbox-build-dir", "/custom-build-dir-for-test") + .unwrap(); } std::env::set_var("_NIX_TEST_NO_SANDBOX", "1"); @@ -1784,9 +1787,7 @@ mod tests { gc_registering_current_thread(|| { let store = Store::open(None, HashMap::new()).unwrap(); let mut es = EvalState::new(store, []).unwrap(); - let v = es - .eval_from_string("[ ]", "") - .unwrap(); + let v = es.eval_from_string("[ ]", "").unwrap(); es.force(&v).unwrap(); let t = es.value_type_unforced(&v); assert!(t == Some(ValueType::List)); diff --git a/nix-bindings-flake/src/lib.rs b/nix-bindings-flake/src/lib.rs index 96d5852..91b4bb2 100644 --- a/nix-bindings-flake/src/lib.rs +++ b/nix-bindings-flake/src/lib.rs @@ -45,7 +45,10 @@ impl FlakeSettings { pub trait EvalStateBuilderExt { /// Configures the eval state to provide flakes features such as `builtins.getFlake`. - fn flakes(self, settings: &FlakeSettings) -> Result; + fn flakes( + self, + settings: &FlakeSettings, + ) -> Result; } impl EvalStateBuilderExt for nix_bindings_expr::eval_state::EvalStateBuilder { /// Configures the eval state to provide flakes features such as `builtins.getFlake`. diff --git a/nix-bindings-store/src/store.rs b/nix-bindings-store/src/store.rs index 5e0885f..bf6e54a 100644 --- a/nix-bindings-store/src/store.rs +++ b/nix-bindings-store/src/store.rs @@ -2,11 +2,13 @@ use anyhow::{bail, Error, Result}; use lazy_static::lazy_static; use nix_bindings_bindgen_raw as raw; use nix_bindings_util::context::Context; -use nix_bindings_util::string_return::{callback_get_result_string, callback_get_result_string_data}; +use nix_bindings_util::string_return::{ + callback_get_result_string, callback_get_result_string_data, +}; use nix_bindings_util::{check_call, result_string_init}; -use std::collections::HashMap; #[cfg(nix_at_least = "2.33")] use std::collections::BTreeMap; +use std::collections::HashMap; use std::ffi::{c_char, CString}; use std::ptr::null_mut; use std::ptr::NonNull; @@ -309,8 +311,8 @@ impl Store { self.inner.ptr(), drv.inner.as_ptr() ))?; - let path = NonNull::new(path) - .ok_or_else(|| Error::msg("add_derivation returned null"))?; + let path = + NonNull::new(path).ok_or_else(|| Error::msg("add_derivation returned null"))?; Ok(StorePath::new_raw(path)) } } @@ -333,7 +335,8 @@ impl Store { #[doc(alias = "nix_store_realise")] pub fn realise(&mut self, path: &StorePath) -> Result> { let mut outputs = BTreeMap::new(); - let userdata = &mut outputs as *mut BTreeMap as *mut std::os::raw::c_void; + let userdata = + &mut outputs as *mut BTreeMap as *mut std::os::raw::c_void; unsafe extern "C" fn callback( userdata: *mut std::os::raw::c_void, @@ -428,8 +431,8 @@ impl Clone for Store { #[cfg(test)] mod tests { - use std::collections::HashMap; use ctor::ctor; + use std::collections::HashMap; use super::*; @@ -446,7 +449,8 @@ mod tests { // Set custom build dir for sandbox if cfg!(target_os = "linux") { - nix_bindings_util::settings::set("sandbox-build-dir", "/custom-build-dir-for-test").ok(); + nix_bindings_util::settings::set("sandbox-build-dir", "/custom-build-dir-for-test") + .ok(); } std::env::set_var("_NIX_TEST_NO_SANDBOX", "1"); @@ -658,9 +662,8 @@ mod tests { #[cfg(nix_at_least = "2.33")] fn create_multi_output_derivation_json() -> String { - let system = current_system().unwrap_or_else(|_| { - format!("{}-{}", std::env::consts::ARCH, std::env::consts::OS) - }); + let system = current_system() + .unwrap_or_else(|_| format!("{}-{}", std::env::consts::ARCH, std::env::consts::OS)); format!( r#"{{ @@ -716,7 +719,9 @@ mod tests { // Verify outputs are complete (BTreeMap guarantees ordering) let output_names: Vec<&String> = outputs.keys().collect(); - let expected_order = vec!["outa", "outb", "outc", "outd", "oute", "outf", "outg", "outh", "outi", "outj"]; + let expected_order = vec![ + "outa", "outb", "outc", "outd", "oute", "outf", "outg", "outh", "outi", "outj", + ]; assert_eq!(output_names, expected_order); drop(store); @@ -779,9 +784,8 @@ mod tests { fn realise_builder_fails() { let (mut store, temp_dir) = create_temp_store(); - let system = current_system().unwrap_or_else(|_| { - format!("{}-{}", std::env::consts::ARCH, std::env::consts::OS) - }); + let system = current_system() + .unwrap_or_else(|_| format!("{}-{}", std::env::consts::ARCH, std::env::consts::OS)); // Create a derivation where the builder exits with error let drv_json = format!( @@ -833,9 +837,8 @@ mod tests { fn realise_builder_no_output() { let (mut store, temp_dir) = create_temp_store(); - let system = current_system().unwrap_or_else(|_| { - format!("{}-{}", std::env::consts::ARCH, std::env::consts::OS) - }); + let system = current_system() + .unwrap_or_else(|_| format!("{}-{}", std::env::consts::ARCH, std::env::consts::OS)); // Create a derivation where the builder succeeds but produces no output let drv_json = format!( @@ -899,11 +902,17 @@ mod tests { let closure = store.get_fs_closure(&drv_path, false, true, false).unwrap(); // The closure should contain at least the derivation and its output - assert!(closure.len() >= 2, "Closure should contain at least drv and output"); + assert!( + closure.len() >= 2, + "Closure should contain at least drv and output" + ); // Verify the output path is in the closure let out_in_closure = closure.iter().any(|p| p.name().unwrap() == out_path_name); - assert!(out_in_closure, "Output path should be in closure when include_outputs=true"); + assert!( + out_in_closure, + "Output path should be in closure when include_outputs=true" + ); drop(store); drop(temp_dir); @@ -923,11 +932,16 @@ mod tests { let out_path_name = out_path.name().unwrap(); // Get closure with include_outputs=false - let closure = store.get_fs_closure(&drv_path, false, false, false).unwrap(); + let closure = store + .get_fs_closure(&drv_path, false, false, false) + .unwrap(); // Verify the output path is NOT in the closure let out_in_closure = closure.iter().any(|p| p.name().unwrap() == out_path_name); - assert!(!out_in_closure, "Output path should not be in closure when include_outputs=false"); + assert!( + !out_in_closure, + "Output path should not be in closure when include_outputs=false" + ); drop(store); drop(temp_dir); @@ -951,7 +965,10 @@ mod tests { // Verify the output path is NOT in the closure when direction is flipped let out_in_closure = closure.iter().any(|p| p.name().unwrap() == out_path_name); - assert!(!out_in_closure, "Output path should not be in closure when flip_direction=true"); + assert!( + !out_in_closure, + "Output path should not be in closure when flip_direction=true" + ); drop(store); drop(temp_dir); @@ -975,7 +992,10 @@ mod tests { // Verify the derivation path is in the closure let drv_in_closure = closure.iter().any(|p| p.name().unwrap() == drv_path_name); - assert!(drv_in_closure, "Derivation should be in closure when include_derivers=true"); + assert!( + drv_in_closure, + "Derivation should be in closure when include_derivers=true" + ); drop(store); drop(temp_dir); From 4a1889fb0cf9d559793ec3339bcd2b8add888413 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 6 Dec 2025 12:04:26 +0100 Subject: [PATCH 202/306] maint: Move treefmt to dev partition Moves treefmt-nix input and configuration from the main flake to the dev partition, since it's a development-only dependency. Also switches pre-commit hooks from individual formatter hooks to the unified treefmt hook for better consistency. --- dev/flake-module.nix | 16 +++++++++- dev/flake.lock | 72 ++++---------------------------------------- dev/flake.nix | 2 ++ flake.lock | 23 +------------- flake.nix | 16 ---------- 5 files changed, 24 insertions(+), 105 deletions(-) diff --git a/dev/flake-module.nix b/dev/flake-module.nix index 374fba3..504a523 100644 --- a/dev/flake-module.nix +++ b/dev/flake-module.nix @@ -6,6 +6,7 @@ imports = [ inputs.pre-commit-hooks-nix.flakeModule inputs.hercules-ci-effects.flakeModule + inputs.treefmt-nix.flakeModule ]; perSystem = { @@ -17,7 +18,20 @@ { nix-bindings-rust.nixPackage = inputs'.nix.packages.default; - pre-commit.settings.hooks.nixfmt-rfc-style.enable = true; + treefmt = { + # Used to find the project root + projectRootFile = "flake.lock"; + + programs.rustfmt = { + enable = true; + edition = "2021"; + }; + programs.nixfmt.enable = true; + programs.deadnix.enable = true; + #programs.clang-format.enable = true; + }; + + pre-commit.settings.hooks.treefmt.enable = true; # Temporarily disable rustfmt due to configuration issues # pre-commit.settings.hooks.rustfmt.enable = true; pre-commit.settings.settings.rust.cargoManifestPath = "./Cargo.toml"; diff --git a/dev/flake.lock b/dev/flake.lock index d9508ff..c988f3b 100644 --- a/dev/flake.lock +++ b/dev/flake.lock @@ -36,27 +36,6 @@ "type": "indirect" } }, - "flake-parts_2": { - "inputs": { - "nixpkgs-lib": [ - "nix-unit", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1754487366, - "narHash": "sha256-pHYj8gUBapuUzKV/kN/tR3Zvqc7o6gdFB9XKXIp1SQ8=", - "owner": "hercules-ci", - "repo": "flake-parts", - "rev": "af66ad14b28a127c5c0f3bbb298218fc63528a18", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "flake-parts", - "type": "github" - } - }, "gitignore": { "inputs": { "nixpkgs": [ @@ -97,26 +76,6 @@ "type": "github" } }, - "nix-unit": { - "inputs": { - "flake-parts": "flake-parts_2", - "nixpkgs": "nixpkgs_2", - "treefmt-nix": "treefmt-nix" - }, - "locked": { - "lastModified": 1754985033, - "narHash": "sha256-OLaoMtlNL5AvYuaQ5P4p7riy7M9J8XpFdWKpD5RzQfU=", - "owner": "nix-community", - "repo": "nix-unit", - "rev": "388045c7bee5a2617cca8e94444bea1862df19ac", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "nix-unit", - "type": "github" - } - }, "nixpkgs": { "locked": { "lastModified": 1755027561, @@ -133,22 +92,6 @@ "type": "github" } }, - "nixpkgs_2": { - "locked": { - "lastModified": 1754800730, - "narHash": "sha256-HfVZCXic9XLBgybP0318ym3cDnGwBs/+H5MgxFVYF4I=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "641d909c4a7538f1539da9240dedb1755c907e40", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixpkgs-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, "pre-commit-hooks-nix": { "inputs": { "flake-compat": "flake-compat", @@ -172,23 +115,20 @@ "root": { "inputs": { "hercules-ci-effects": "hercules-ci-effects", - "nix-unit": "nix-unit", - "pre-commit-hooks-nix": "pre-commit-hooks-nix" + "pre-commit-hooks-nix": "pre-commit-hooks-nix", + "treefmt-nix": "treefmt-nix" } }, "treefmt-nix": { "inputs": { - "nixpkgs": [ - "nix-unit", - "nixpkgs" - ] + "nixpkgs": [] }, "locked": { - "lastModified": 1754847726, - "narHash": "sha256-2vX8QjO5lRsDbNYvN9hVHXLU6oMl+V/PsmIiJREG4rE=", + "lastModified": 1762938485, + "narHash": "sha256-AlEObg0syDl+Spi4LsZIBrjw+snSVU4T8MOeuZJUJjM=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "7d81f6fb2e19bf84f1c65135d1060d829fae2408", + "rev": "5b4ee75aeefd1e2d5a1cc43cf6ba65eba75e83e4", "type": "github" }, "original": { diff --git a/dev/flake.nix b/dev/flake.nix index 062e27a..09a06a1 100644 --- a/dev/flake.nix +++ b/dev/flake.nix @@ -4,6 +4,8 @@ pre-commit-hooks-nix.url = "github:cachix/pre-commit-hooks.nix"; pre-commit-hooks-nix.inputs.nixpkgs.follows = ""; hercules-ci-effects.url = "github:hercules-ci/hercules-ci-effects"; + treefmt-nix.url = "github:numtide/treefmt-nix"; + treefmt-nix.inputs.nixpkgs.follows = ""; }; outputs = { ... }: { }; } diff --git a/flake.lock b/flake.lock index 0d023db..602976c 100644 --- a/flake.lock +++ b/flake.lock @@ -344,8 +344,7 @@ "flake-parts": "flake-parts", "nix": "nix", "nix-cargo-integration": "nix-cargo-integration", - "nixpkgs": "nixpkgs", - "treefmt-nix": "treefmt-nix" + "nixpkgs": "nixpkgs" } }, "rust-overlay": { @@ -412,26 +411,6 @@ "repo": "treefmt-nix", "type": "github" } - }, - "treefmt-nix": { - "inputs": { - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1762938485, - "narHash": "sha256-AlEObg0syDl+Spi4LsZIBrjw+snSVU4T8MOeuZJUJjM=", - "owner": "numtide", - "repo": "treefmt-nix", - "rev": "5b4ee75aeefd1e2d5a1cc43cf6ba65eba75e83e4", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "treefmt-nix", - "type": "github" - } } }, "root": "root", diff --git a/flake.nix b/flake.nix index b33e9ce..c8f24c0 100644 --- a/flake.nix +++ b/flake.nix @@ -8,8 +8,6 @@ nix-cargo-integration.url = "github:yusdacra/nix-cargo-integration"; nix-cargo-integration.inputs.nixpkgs.follows = "nixpkgs"; nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; - treefmt-nix.url = "github:numtide/treefmt-nix"; - treefmt-nix.inputs.nixpkgs.follows = "nixpkgs"; }; outputs = @@ -164,7 +162,6 @@ inputs.nix-cargo-integration.flakeModule inputs.flake-parts.flakeModules.partitions inputs.flake-parts.flakeModules.modules - inputs.treefmt-nix.flakeModule # dogfood flake-parts-modules.tested ./nci.nix @@ -182,19 +179,6 @@ }: { packages.nix = inputs'.nix.packages.nix; - - treefmt = { - # Used to find the project root - projectRootFile = "flake.lock"; - - programs.rustfmt = { - enable = true; - edition = "2021"; - }; - programs.nixfmt.enable = true; - programs.deadnix.enable = true; - #programs.clang-format.enable = true; - }; }; partitionedAttrs.devShells = "dev"; From 5cf732957ccb8bcce23b0315a0be800013f10197 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 26 Nov 2025 22:28:34 -0500 Subject: [PATCH 203/306] `Clone` for `StorePath` This will be tested once we get the harmonia bindings in. --- nix-bindings-store/src/path.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/nix-bindings-store/src/path.rs b/nix-bindings-store/src/path.rs index d8aea0c..b07bf11 100644 --- a/nix-bindings-store/src/path.rs +++ b/nix-bindings-store/src/path.rs @@ -64,6 +64,13 @@ impl StorePath { self.raw.as_ptr() } } + +impl Clone for StorePath { + fn clone(&self) -> Self { + unsafe { Self::new_raw_clone(self.raw) } + } +} + impl Drop for StorePath { fn drop(&mut self) { unsafe { From 07058ecfe2be590caf1c012b9ef81999e062129f Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 26 Nov 2025 22:34:05 -0500 Subject: [PATCH 204/306] `Clone` for `Derivation` And `as_ptr` too. This will be tested once we get the harmonia bindings in. --- nix-bindings-store/src/derivation.rs | 32 ++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/nix-bindings-store/src/derivation.rs b/nix-bindings-store/src/derivation.rs index ff13c08..4b10127 100644 --- a/nix-bindings-store/src/derivation.rs +++ b/nix-bindings-store/src/derivation.rs @@ -14,6 +14,38 @@ impl Derivation { pub(crate) fn new_raw(inner: NonNull) -> Self { Derivation { inner } } + + /// This is a low level function that you shouldn't have to call unless you are developing the Nix bindings. + /// + /// Construct a new `Derivation` by first cloning the C derivation. + /// + /// # Safety + /// + /// This does not take ownership of the C derivation, so it should be a borrowed pointer, or you should free it. + pub unsafe fn new_raw_clone(inner: NonNull) -> Self { + Self::new_raw( + NonNull::new(raw::derivation_clone(inner.as_ptr())) + .or_else(|| panic!("nix_derivation_clone returned a null pointer")) + .unwrap(), + ) + } + + /// This is a low level function that you shouldn't have to call unless you are developing the Nix bindings. + /// + /// Get a pointer to the underlying Nix C API derivation. + /// + /// # Safety + /// + /// This function is unsafe because it returns a raw pointer. The caller must ensure that the pointer is not used beyond the lifetime of this `Derivation`. + pub unsafe fn as_ptr(&self) -> *mut raw::derivation { + self.inner.as_ptr() + } +} + +impl Clone for Derivation { + fn clone(&self) -> Self { + unsafe { Self::new_raw_clone(self.inner) } + } } impl Drop for Derivation { From 46656f6056e862286b45ec420a2f1424265593c7 Mon Sep 17 00:00:00 2001 From: Tristan Ross Date: Thu, 6 Nov 2025 11:02:27 -0800 Subject: [PATCH 205/306] fix(nix-bindings-rust): fix aarch64 abi support --- nix-bindings-util/src/context.rs | 2 +- nix-bindings-util/src/string_return.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/nix-bindings-util/src/context.rs b/nix-bindings-util/src/context.rs index 33b657a..e28ecb8 100644 --- a/nix-bindings-util/src/context.rs +++ b/nix-bindings-util/src/context.rs @@ -144,7 +144,7 @@ mod tests { raw::set_err_msg( ctx_ptr, raw::err_NIX_ERR_UNKNOWN.try_into().unwrap(), - b"dummy error message\0".as_ptr() as *const i8, + b"dummy error message\0".as_ptr() as *const std::os::raw::c_char, ); } } diff --git a/nix-bindings-util/src/string_return.rs b/nix-bindings-util/src/string_return.rs index b7bf010..9851a0d 100644 --- a/nix-bindings-util/src/string_return.rs +++ b/nix-bindings-util/src/string_return.rs @@ -75,7 +75,7 @@ mod tests { #[test] fn test_callback_result_string() { let mut ret: Result = result_string_init!(); - let start: *const std::os::raw::c_char = b"helloGARBAGE".as_ptr() as *const i8; + let start = b"helloGARBAGE".as_ptr() as *const std::os::raw::c_char; let n: std::os::raw::c_uint = 5; let user_data: *mut std::os::raw::c_void = callback_get_result_string_data(&mut ret); unsafe { From 388dc735b95804dcb8389d553c69d7dbb1c8c42a Mon Sep 17 00:00:00 2001 From: Morgan Jones Date: Sat, 1 Nov 2025 15:19:37 -0700 Subject: [PATCH 206/306] flake: Add shellHook to mkDerivation Otherwise, we don't run the bindgen script while entering a devShell. --- flake.nix | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index c8f24c0..0058049 100644 --- a/flake.nix +++ b/flake.nix @@ -60,7 +60,7 @@ }; config.nix-bindings-rust = { nciBuildConfig = { - mkDerivation = { + mkDerivation = rec { buildInputs = [ # stdbool.h pkgs.stdenv.cc @@ -88,6 +88,7 @@ postConfigure = lib.optionalString pkgs.stdenv.cc.isGNU '' source ${./bindgen-gcc.sh} ''; + shellHook = postConfigure; }; # NOTE: duplicated in flake.nix devShell env = { From d496ee3539b8d413487f350ff9d2a8c5b9cf42ac Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 6 Dec 2025 15:07:48 -0500 Subject: [PATCH 207/306] Work around dead code analysis bug Some tool forgot that `builtins.functionArgs` makes removing seemingly unused arguments with `@` patterns fraught. --- flake.nix | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/flake.nix b/flake.nix index c8f24c0..f638eb7 100644 --- a/flake.nix +++ b/flake.nix @@ -111,13 +111,13 @@ */ flake-parts-modules.tested = # Consumer toplevel - { config, ... }: + { ... }: { _file = ./flake.nix; imports = [ flake-parts-modules.basic ]; config.perSystem = # Consumer perSystem - consumerPerSystem@{ + { lib, config, system, @@ -135,9 +135,9 @@ { config = { # Overriding our `perSystem` to use the consumer's `pkgs` - _module.args.pkgs = lib.mkForce consumerPerSystem.pkgs; + _module.args.pkgs = lib.mkForce pkgs; # ... and `nixPackage` - nix-bindings-rust.nixPackage = lib.mkForce consumerPerSystem.config.nix-bindings-rust.nixPackage; + nix-bindings-rust.nixPackage = lib.mkForce config.nix-bindings-rust.nixPackage; }; } ]; From a04e903a7de9608585fa3c7287d9f06725dfe9e9 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 6 Dec 2025 14:42:44 -0500 Subject: [PATCH 208/306] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'flake-parts': 'github:hercules-ci/flake-parts/4524271976b625a4a605beefd893f270620fd751?narHash=sha256-%2BuWLQZccFHwqpGqr2Yt5VsW/PbeJVTn9Dk6SHWhNRPw%3D' (2025-09-01) → 'github:hercules-ci/flake-parts/2cccadc7357c0ba201788ae99c4dfa90728ef5e0?narHash=sha256-LlLt2Jo/gMNYAwOgdRQBrsRoOz7BPRkzvNaI/fzXi2Q%3D' (2025-11-21) • Updated input 'flake-parts/nixpkgs-lib': 'github:nix-community/nixpkgs.lib/a73b9c743612e4244d865a2fdee11865283c04e6?narHash=sha256-x2rJ%2BOvzq0sCMpgfgGaaqgBSwY%2BLST%2BWbZ6TytnT9Rk%3D' (2025-08-10) → 'github:nix-community/nixpkgs.lib/719359f4562934ae99f5443f20aa06c2ffff91fc?narHash=sha256-b0yj6kfvO8ApcSE%2BQmA6mUfu8IYG6/uU28OFn4PaC8M%3D' (2025-10-29) • Updated input 'nix': 'github:NixOS/nix/5f42e5ebb797433e3db3cfbc81f9cc3139903636?narHash=sha256-esRP6gaYG8d3gLkfr306HBgWYe2jEfHIJq6A1W/pdYw%3D' (2025-12-05) → 'github:NixOS/nix/525755dadc153c68e5ada8d287ab19af26383fff?narHash=sha256-oMSKV0NFQpfwYQ4wcIIDuH%2B0AwCQl%2BbYRgPznx38i8k%3D' (2025-12-06) • Updated input 'nix-cargo-integration': 'github:yusdacra/nix-cargo-integration/529758e12981b62d6977a7bc61c86e19c3e11594?narHash=sha256-v3MasfpOtYUV4MzG5JGRS4JIo3lccjBzH9qZi7zQzSM%3D' (2025-09-01) → 'github:yusdacra/nix-cargo-integration/0feff946314851edc88b9c783ad7d2dccd49abcc?narHash=sha256-S5k0HAxjq2sbf5BC5DOly66joE08HfiyEJD8v2U3yLc%3D' (2025-12-06) • Updated input 'nix-cargo-integration/crane': 'github:ipetkov/crane/5b03654ce046b5167e7b0bccbd8244cb56c16f0e?narHash=sha256-/mumx8AQ5xFuCJqxCIOFCHTVlxHkMT21idpbgbm/TIE%3D' (2024-09-26) → 'github:ipetkov/crane/95d528a5f54eaba0d12102249ce42f4d01f4e364?narHash=sha256-NU5WaEdfwF6i8faJ2Yh%2BjcK9vVFrofLcwlD/mP65JrI%3D' (2025-09-25) • Updated input 'nix-cargo-integration/dream2nix': 'github:nix-community/dream2nix/fbec3263cb4895ac86ee9506cdc4e6919a1a2214?narHash=sha256-nrDovydywSKRbWim9Ynmgj8SBm8LK3DI2WuhIqzOHYI%3D' (2025-08-12) → 'github:nix-community/dream2nix/ee20942e4524d3458a91108716c847a2d4299d2e?narHash=sha256-4OlkDA0yJyqt5iTX9NqtHNghvkWNzYqmtX7FxDmEXt4%3D' (2025-11-24) • Updated input 'nix-cargo-integration/mk-naked-shell': 'github:yusdacra/mk-naked-shell/7612f828dd6f22b7fb332cc69440e839d7ffe6bd?narHash=sha256-3XlJrwlR0nBiREnuogoa5i1b4%2Bw/XPe0z8bbrJASw0g%3D' (2023-04-12) → 'github:90-008/mk-naked-shell/7612f828dd6f22b7fb332cc69440e839d7ffe6bd?narHash=sha256-3XlJrwlR0nBiREnuogoa5i1b4%2Bw/XPe0z8bbrJASw0g%3D' (2023-04-12) • Updated input 'nix-cargo-integration/parts': 'github:hercules-ci/flake-parts/af66ad14b28a127c5c0f3bbb298218fc63528a18?narHash=sha256-pHYj8gUBapuUzKV/kN/tR3Zvqc7o6gdFB9XKXIp1SQ8%3D' (2025-08-06) → 'github:hercules-ci/flake-parts/2cccadc7357c0ba201788ae99c4dfa90728ef5e0?narHash=sha256-LlLt2Jo/gMNYAwOgdRQBrsRoOz7BPRkzvNaI/fzXi2Q%3D' (2025-11-21) • Updated input 'nix-cargo-integration/rust-overlay': 'github:oxalica/rust-overlay/b29e5365120f344fe7161f14fc9e272fcc41ee56?narHash=sha256-z/Iy4qvcMqzhA2IAAg71Sw4BrMwbBHvCS90ZoPLsnIk%3D' (2025-09-01) → 'github:oxalica/rust-overlay/086fd19a68e80fcc8a298e9df4674982e4c498a6?narHash=sha256-FIJtt3Zil89/hLy9i7f0R2xXcJDPc3CeqiiCLfsFV0Y%3D' (2025-12-06) • Updated input 'nix-cargo-integration/treefmt': 'github:numtide/treefmt-nix/1aabc6c05ccbcbf4a635fb7a90400e44282f61c4?narHash=sha256-F1oFfV51AE259I85av%2BMAia221XwMHCOtZCMcZLK2Jk%3D' (2025-08-31) → 'github:numtide/treefmt-nix/5b4ee75aeefd1e2d5a1cc43cf6ba65eba75e83e4?narHash=sha256-AlEObg0syDl%2BSpi4LsZIBrjw%2BsnSVU4T8MOeuZJUJjM%3D' (2025-11-12) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/d7600c775f877cd87b4f5a831c28aa94137377aa?narHash=sha256-tlOn88coG5fzdyqz6R93SQL5Gpq%2Bm/DsWpekNFhqPQk%3D' (2025-08-30) → 'github:NixOS/nixpkgs/f61125a668a320878494449750330ca58b78c557?narHash=sha256-BmPWzogsG2GsXZtlT%2BMTcAWeDK5hkbGRZTeZNW42fwA%3D' (2025-12-05) --- flake.lock | 66 +++++++++++++++++++++++++++--------------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/flake.lock b/flake.lock index 2fd1d19..d098b91 100644 --- a/flake.lock +++ b/flake.lock @@ -3,16 +3,16 @@ "crane": { "flake": false, "locked": { - "lastModified": 1727316705, - "narHash": "sha256-/mumx8AQ5xFuCJqxCIOFCHTVlxHkMT21idpbgbm/TIE=", + "lastModified": 1758758545, + "narHash": "sha256-NU5WaEdfwF6i8faJ2Yh+jcK9vVFrofLcwlD/mP65JrI=", "owner": "ipetkov", "repo": "crane", - "rev": "5b03654ce046b5167e7b0bccbd8244cb56c16f0e", + "rev": "95d528a5f54eaba0d12102249ce42f4d01f4e364", "type": "github" }, "original": { "owner": "ipetkov", - "ref": "v0.19.0", + "ref": "v0.21.1", "repo": "crane", "type": "github" } @@ -27,11 +27,11 @@ "pyproject-nix": "pyproject-nix" }, "locked": { - "lastModified": 1754978539, - "narHash": "sha256-nrDovydywSKRbWim9Ynmgj8SBm8LK3DI2WuhIqzOHYI=", + "lastModified": 1764021028, + "narHash": "sha256-4OlkDA0yJyqt5iTX9NqtHNghvkWNzYqmtX7FxDmEXt4=", "owner": "nix-community", "repo": "dream2nix", - "rev": "fbec3263cb4895ac86ee9506cdc4e6919a1a2214", + "rev": "ee20942e4524d3458a91108716c847a2d4299d2e", "type": "github" }, "original": { @@ -77,11 +77,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1756770412, - "narHash": "sha256-+uWLQZccFHwqpGqr2Yt5VsW/PbeJVTn9Dk6SHWhNRPw=", + "lastModified": 1763759067, + "narHash": "sha256-LlLt2Jo/gMNYAwOgdRQBrsRoOz7BPRkzvNaI/fzXi2Q=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "4524271976b625a4a605beefd893f270620fd751", + "rev": "2cccadc7357c0ba201788ae99c4dfa90728ef5e0", "type": "github" }, "original": { @@ -147,13 +147,13 @@ "locked": { "lastModified": 1681286841, "narHash": "sha256-3XlJrwlR0nBiREnuogoa5i1b4+w/XPe0z8bbrJASw0g=", - "owner": "yusdacra", + "owner": "90-008", "repo": "mk-naked-shell", "rev": "7612f828dd6f22b7fb332cc69440e839d7ffe6bd", "type": "github" }, "original": { - "owner": "yusdacra", + "owner": "90-008", "repo": "mk-naked-shell", "type": "github" } @@ -170,11 +170,11 @@ "nixpkgs-regression": "nixpkgs-regression" }, "locked": { - "lastModified": 1764949869, - "narHash": "sha256-esRP6gaYG8d3gLkfr306HBgWYe2jEfHIJq6A1W/pdYw=", + "lastModified": 1764979397, + "narHash": "sha256-oMSKV0NFQpfwYQ4wcIIDuH+0AwCQl+bYRgPznx38i8k=", "owner": "NixOS", "repo": "nix", - "rev": "5f42e5ebb797433e3db3cfbc81f9cc3139903636", + "rev": "525755dadc153c68e5ada8d287ab19af26383fff", "type": "github" }, "original": { @@ -196,11 +196,11 @@ "treefmt": "treefmt" }, "locked": { - "lastModified": 1756707650, - "narHash": "sha256-v3MasfpOtYUV4MzG5JGRS4JIo3lccjBzH9qZi7zQzSM=", + "lastModified": 1765001919, + "narHash": "sha256-S5k0HAxjq2sbf5BC5DOly66joE08HfiyEJD8v2U3yLc=", "owner": "yusdacra", "repo": "nix-cargo-integration", - "rev": "529758e12981b62d6977a7bc61c86e19c3e11594", + "rev": "0feff946314851edc88b9c783ad7d2dccd49abcc", "type": "github" }, "original": { @@ -211,11 +211,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1756542300, - "narHash": "sha256-tlOn88coG5fzdyqz6R93SQL5Gpq+m/DsWpekNFhqPQk=", + "lastModified": 1764950072, + "narHash": "sha256-BmPWzogsG2GsXZtlT+MTcAWeDK5hkbGRZTeZNW42fwA=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "d7600c775f877cd87b4f5a831c28aa94137377aa", + "rev": "f61125a668a320878494449750330ca58b78c557", "type": "github" }, "original": { @@ -243,11 +243,11 @@ }, "nixpkgs-lib": { "locked": { - "lastModified": 1754788789, - "narHash": "sha256-x2rJ+Ovzq0sCMpgfgGaaqgBSwY+LST+WbZ6TytnT9Rk=", + "lastModified": 1761765539, + "narHash": "sha256-b0yj6kfvO8ApcSE+QmA6mUfu8IYG6/uU28OFn4PaC8M=", "owner": "nix-community", "repo": "nixpkgs.lib", - "rev": "a73b9c743612e4244d865a2fdee11865283c04e6", + "rev": "719359f4562934ae99f5443f20aa06c2ffff91fc", "type": "github" }, "original": { @@ -280,11 +280,11 @@ ] }, "locked": { - "lastModified": 1754487366, - "narHash": "sha256-pHYj8gUBapuUzKV/kN/tR3Zvqc7o6gdFB9XKXIp1SQ8=", + "lastModified": 1763759067, + "narHash": "sha256-LlLt2Jo/gMNYAwOgdRQBrsRoOz7BPRkzvNaI/fzXi2Q=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "af66ad14b28a127c5c0f3bbb298218fc63528a18", + "rev": "2cccadc7357c0ba201788ae99c4dfa90728ef5e0", "type": "github" }, "original": { @@ -355,11 +355,11 @@ ] }, "locked": { - "lastModified": 1756694554, - "narHash": "sha256-z/Iy4qvcMqzhA2IAAg71Sw4BrMwbBHvCS90ZoPLsnIk=", + "lastModified": 1764988672, + "narHash": "sha256-FIJtt3Zil89/hLy9i7f0R2xXcJDPc3CeqiiCLfsFV0Y=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "b29e5365120f344fe7161f14fc9e272fcc41ee56", + "rev": "086fd19a68e80fcc8a298e9df4674982e4c498a6", "type": "github" }, "original": { @@ -399,11 +399,11 @@ ] }, "locked": { - "lastModified": 1756662192, - "narHash": "sha256-F1oFfV51AE259I85av+MAia221XwMHCOtZCMcZLK2Jk=", + "lastModified": 1762938485, + "narHash": "sha256-AlEObg0syDl+Spi4LsZIBrjw+snSVU4T8MOeuZJUJjM=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "1aabc6c05ccbcbf4a635fb7a90400e44282f61c4", + "rev": "5b4ee75aeefd1e2d5a1cc43cf6ba65eba75e83e4", "type": "github" }, "original": { From 92161f300af5b3a6330ab43029ce015f7aff7b00 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 6 Dec 2025 14:43:12 -0500 Subject: [PATCH 209/306] dev/flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'hercules-ci-effects': 'github:hercules-ci/hercules-ci-effects/99e03e72e3f7e13506f80ef9ebaedccb929d84d0?narHash=sha256-AavrbMltJKcC2Fx0lfJoZfmy7g87ebXU0ddVenhajLA%3D' (2025-08-15) → 'github:hercules-ci/hercules-ci-effects/b0585849abe7d02a774a853f7952d07bb910fd9e?narHash=sha256-jZi%2B9yKmeTMsJ4ZNqRei/wL16%2BQwYGrCl4EJ3QHfoDU%3D' (2025-11-15) • Updated input 'hercules-ci-effects/flake-parts': 'github:hercules-ci/flake-parts/af66ad14b28a127c5c0f3bbb298218fc63528a18?narHash=sha256-pHYj8gUBapuUzKV/kN/tR3Zvqc7o6gdFB9XKXIp1SQ8%3D' (2025-08-06) → 'github:hercules-ci/flake-parts/52a2caecc898d0b46b2b905f058ccc5081f842da?narHash=sha256-8oNVE8TrD19ulHinjaqONf9QWCKK%2Bw4url56cdStMpM%3D' (2025-11-12) • Updated input 'hercules-ci-effects/nixpkgs': 'github:NixOS/nixpkgs/005433b926e16227259a1843015b5b2b7f7d1fc3?narHash=sha256-IVft239Bc8p8Dtvf7UAACMG5P3ZV%2B3/aO28gXpGtMXI%3D' (2025-08-12) → 'github:NixOS/nixpkgs/c5ae371f1a6a7fd27823bc500d9390b38c05fa55?narHash=sha256-4PqRErxfe%2B2toFJFgcRKZ0UI9NSIOJa%2B7RXVtBhy4KE%3D' (2025-11-12) • Updated input 'pre-commit-hooks-nix': 'github:cachix/pre-commit-hooks.nix/e891a93b193fcaf2fc8012d890dc7f0befe86ec2?narHash=sha256-RF7j6C1TmSTK9tYWO6CdEMtg6XZaUKcvZwOCD2SICZs%3D' (2025-08-23) → 'github:cachix/pre-commit-hooks.nix/548fc44fca28a5e81c5d6b846e555e6b9c2a5a3c?narHash=sha256-rhSqPNxDVow7OQKi4qS5H8Au0P4S3AYbawBSmJNUtBQ%3D' (2025-12-06) • Updated input 'pre-commit-hooks-nix/flake-compat': 'github:edolstra/flake-compat/9100a0f413b0c601e0533d1d94ffd501ce2e7885?narHash=sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX%2BfjA8Xf8PUmqCY%3D' (2025-05-12) → 'github:edolstra/flake-compat/f387cd2afec9419c8ee37694406ca490c3f34ee5?narHash=sha256-XKUZz9zewJNUj46b4AJdiRZJAvSZ0Dqj2BNfXvFlJC4%3D' (2025-10-27) --- dev/flake.lock | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/dev/flake.lock b/dev/flake.lock index c988f3b..8e0c0f4 100644 --- a/dev/flake.lock +++ b/dev/flake.lock @@ -3,11 +3,11 @@ "flake-compat": { "flake": false, "locked": { - "lastModified": 1747046372, - "narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=", + "lastModified": 1761588595, + "narHash": "sha256-XKUZz9zewJNUj46b4AJdiRZJAvSZ0Dqj2BNfXvFlJC4=", "owner": "edolstra", "repo": "flake-compat", - "rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885", + "rev": "f387cd2afec9419c8ee37694406ca490c3f34ee5", "type": "github" }, "original": { @@ -24,11 +24,11 @@ ] }, "locked": { - "lastModified": 1754487366, - "narHash": "sha256-pHYj8gUBapuUzKV/kN/tR3Zvqc7o6gdFB9XKXIp1SQ8=", + "lastModified": 1762980239, + "narHash": "sha256-8oNVE8TrD19ulHinjaqONf9QWCKK+w4url56cdStMpM=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "af66ad14b28a127c5c0f3bbb298218fc63528a18", + "rev": "52a2caecc898d0b46b2b905f058ccc5081f842da", "type": "github" }, "original": { @@ -63,11 +63,11 @@ "nixpkgs": "nixpkgs" }, "locked": { - "lastModified": 1755233722, - "narHash": "sha256-AavrbMltJKcC2Fx0lfJoZfmy7g87ebXU0ddVenhajLA=", + "lastModified": 1763182882, + "narHash": "sha256-jZi+9yKmeTMsJ4ZNqRei/wL16+QwYGrCl4EJ3QHfoDU=", "owner": "hercules-ci", "repo": "hercules-ci-effects", - "rev": "99e03e72e3f7e13506f80ef9ebaedccb929d84d0", + "rev": "b0585849abe7d02a774a853f7952d07bb910fd9e", "type": "github" }, "original": { @@ -78,11 +78,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1755027561, - "narHash": "sha256-IVft239Bc8p8Dtvf7UAACMG5P3ZV+3/aO28gXpGtMXI=", + "lastModified": 1762977756, + "narHash": "sha256-4PqRErxfe+2toFJFgcRKZ0UI9NSIOJa+7RXVtBhy4KE=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "005433b926e16227259a1843015b5b2b7f7d1fc3", + "rev": "c5ae371f1a6a7fd27823bc500d9390b38c05fa55", "type": "github" }, "original": { @@ -99,11 +99,11 @@ "nixpkgs": [] }, "locked": { - "lastModified": 1755960406, - "narHash": "sha256-RF7j6C1TmSTK9tYWO6CdEMtg6XZaUKcvZwOCD2SICZs=", + "lastModified": 1765016596, + "narHash": "sha256-rhSqPNxDVow7OQKi4qS5H8Au0P4S3AYbawBSmJNUtBQ=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "e891a93b193fcaf2fc8012d890dc7f0befe86ec2", + "rev": "548fc44fca28a5e81c5d6b846e555e6b9c2a5a3c", "type": "github" }, "original": { From 53641a179b3851cbd04de78a9c5e1533ed1d0057 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 9 Dec 2025 22:14:02 +0100 Subject: [PATCH 210/306] feat: Enable Nix 2.33 APIs for pre-release testing/validation Pre-release versions (2.33.0pre) now enable 2.33 APIs, allowing development and integration testing before the stable release. Tests remain disabled for 2.33 until stable to avoid blocking on unrelated pre-release issues. --- nix-bindings-store/Cargo.toml | 2 + nix-bindings-store/build.rs | 39 +---------- nix-bindings-store/src/derivation.rs | 2 +- nix-bindings-store/src/store.rs | 19 ++--- nix-bindings-util/src/lib.rs | 1 + nix-bindings-util/src/nix_version.rs | 101 +++++++++++++++++++++++++++ 6 files changed, 118 insertions(+), 46 deletions(-) create mode 100644 nix-bindings-util/src/nix_version.rs diff --git a/nix-bindings-store/Cargo.toml b/nix-bindings-store/Cargo.toml index c1300b7..c9bc96b 100644 --- a/nix-bindings-store/Cargo.toml +++ b/nix-bindings-store/Cargo.toml @@ -20,3 +20,5 @@ tempfile = "3.10" [build-dependencies] pkg-config = "0.3" +# Needed for version parsing in build.rs +nix-bindings-util = { path = "../nix-bindings-util" } diff --git a/nix-bindings-store/build.rs b/nix-bindings-store/build.rs index e97be70..85a20d6 100644 --- a/nix-bindings-store/build.rs +++ b/nix-bindings-store/build.rs @@ -1,39 +1,6 @@ +use nix_bindings_util::nix_version::emit_version_cfg; + fn main() { - // Get nix version let nix_version = pkg_config::probe_library("nix-store-c").unwrap().version; - - // Generate version flags - // Unfortunately, Rust doesn't give us a "greater than" operator in conditional - // compilation, so we pre-evaluate the version comparisons here, making use - // of the multi-valued nature of Rust cfgs. - let relevant_versions = vec!["2.26", "2.33"]; - let versions = relevant_versions - .iter() - .map(|v| format!("\"{}\"", v)) - .collect::>() - .join(","); - - // Declare the known versions, so that Rust can warn about unknown versions - // that aren't part of `relevant_versions` yet - feel free to add entries. - println!( - "cargo:rustc-check-cfg=cfg(nix_at_least,values({}))", - versions - ); - - let nix_version = nix_version.split('.').collect::>(); - let nix_version = ( - nix_version[0].parse::().unwrap(), - nix_version[1].parse::().unwrap(), - ); - - for version_str in relevant_versions { - let version = version_str.split('.').collect::>(); - let version = ( - version[0].parse::().unwrap(), - version[1].parse::().unwrap(), - ); - if nix_version >= version { - println!("cargo:rustc-cfg=nix_at_least=\"{}\"", version_str); - } - } + emit_version_cfg(&nix_version, &["2.26", "2.33.0pre", "2.33"]); } diff --git a/nix-bindings-store/src/derivation.rs b/nix-bindings-store/src/derivation.rs index 4b10127..6be5e75 100644 --- a/nix-bindings-store/src/derivation.rs +++ b/nix-bindings-store/src/derivation.rs @@ -1,4 +1,4 @@ -#![cfg(nix_at_least = "2.33")] +#![cfg(nix_at_least = "2.33.0pre")] use nix_bindings_bindgen_raw as raw; use std::ptr::NonNull; diff --git a/nix-bindings-store/src/store.rs b/nix-bindings-store/src/store.rs index 0f01e76..aff5a66 100644 --- a/nix-bindings-store/src/store.rs +++ b/nix-bindings-store/src/store.rs @@ -6,7 +6,7 @@ use nix_bindings_util::string_return::{ callback_get_result_string, callback_get_result_string_data, }; use nix_bindings_util::{check_call, result_string_init}; -#[cfg(nix_at_least = "2.33")] +#[cfg(nix_at_least = "2.33.0pre")] use std::collections::BTreeMap; use std::collections::HashMap; use std::ffi::{c_char, CString}; @@ -14,7 +14,7 @@ use std::ptr::null_mut; use std::ptr::NonNull; use std::sync::{Arc, Mutex, Weak}; -#[cfg(nix_at_least = "2.33")] +#[cfg(nix_at_least = "2.33.0pre")] use crate::derivation::Derivation; use crate::path::StorePath; @@ -71,7 +71,7 @@ lazy_static! { static ref STORE_CACHE: Arc> = Arc::new(Mutex::new(HashMap::new())); } -#[cfg(nix_at_least = "2.33")] +#[cfg(nix_at_least = "2.33.0pre")] unsafe extern "C" fn callback_get_result_store_path_set( _context: *mut raw::c_context, user_data: *mut std::os::raw::c_void, @@ -88,7 +88,7 @@ unsafe extern "C" fn callback_get_result_store_path_set( ret.push(store_path); } -#[cfg(nix_at_least = "2.33")] +#[cfg(nix_at_least = "2.33.0pre")] fn callback_get_result_store_path_set_data(vec: &mut Vec) -> *mut std::os::raw::c_void { vec as *mut Vec as *mut std::os::raw::c_void } @@ -274,7 +274,7 @@ impl Store { /// # Returns /// A [`Derivation`] object if parsing succeeds, or an error if the JSON is invalid /// or malformed. - #[cfg(nix_at_least = "2.33")] + #[cfg(nix_at_least = "2.33.0pre")] #[doc(alias = "nix_derivation_from_json")] pub fn derivation_from_json(&mut self, json: &str) -> Result { let json_cstr = CString::new(json)?; @@ -302,7 +302,7 @@ impl Store { /// /// # Returns /// The store path of the derivation (ending in `.drv`). - #[cfg(nix_at_least = "2.33")] + #[cfg(nix_at_least = "2.33.0pre")] #[doc(alias = "nix_add_derivation")] pub fn add_derivation(&mut self, drv: &Derivation) -> Result { unsafe { @@ -331,7 +331,7 @@ impl Store { /// # Returns /// A [`BTreeMap`] mapping output names (e.g., "out", "dev", "doc") to their store paths. /// The map is ordered alphabetically by output name for deterministic iteration. - #[cfg(nix_at_least = "2.33")] + #[cfg(nix_at_least = "2.33.0pre")] #[doc(alias = "nix_store_realise")] pub fn realise(&mut self, path: &StorePath) -> Result> { let mut outputs = BTreeMap::new(); @@ -388,7 +388,7 @@ impl Store { /// /// # Returns /// A vector of store paths in the closure, in no particular order. - #[cfg(nix_at_least = "2.33")] + #[cfg(nix_at_least = "2.33.0pre")] #[doc(alias = "nix_store_get_fs_closure")] pub fn get_fs_closure( &mut self, @@ -544,6 +544,7 @@ mod tests { assert!(weak.inner.upgrade().is_none()); } + #[cfg(nix_at_least = "2.33.0pre")] fn create_temp_store() -> (Store, tempfile::TempDir) { let temp_dir = tempfile::tempdir().unwrap(); @@ -616,7 +617,7 @@ mod tests { } #[test] - #[cfg(nix_at_least = "2.33")] + #[cfg(nix_at_least = "2.33.0pre")] fn derivation_from_invalid_json() { let (mut store, temp_dir) = create_temp_store(); let result = store.derivation_from_json("not valid json"); diff --git a/nix-bindings-util/src/lib.rs b/nix-bindings-util/src/lib.rs index 29ad7dd..c74208c 100644 --- a/nix-bindings-util/src/lib.rs +++ b/nix-bindings-util/src/lib.rs @@ -2,3 +2,4 @@ pub mod context; pub mod settings; #[macro_use] pub mod string_return; +pub mod nix_version; diff --git a/nix-bindings-util/src/nix_version.rs b/nix-bindings-util/src/nix_version.rs new file mode 100644 index 0000000..a577261 --- /dev/null +++ b/nix-bindings-util/src/nix_version.rs @@ -0,0 +1,101 @@ +//! Nix version parsing and conditional compilation support. + +/// Emit [`cargo:rustc-cfg`] directives for Nix version-based conditional compilation. +/// +/// Call from build.rs with the Nix version and desired version gates. +/// +/// [`cargo:rustc-cfg`]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-cfg +/// +/// # Example +/// +/// ``` +/// use nix_bindings_util::nix_version::emit_version_cfg; +/// # // Stub pkg_config so that we can render a full usage example +/// # mod pkg_config { pub fn probe_library(_: &str) -> Result { Ok(Library { version: "2.33.0pre".into() }) } +/// # pub struct Library { pub version: String } } +/// +/// let nix_version = pkg_config::probe_library("nix-store-c").unwrap().version; +/// emit_version_cfg(&nix_version, &["2.26", "2.33.0pre", "2.33"]); +/// ``` +/// +/// Emits `nix_at_least="2.26"` and `nix_at_least="2.33.0pre"` for version 2.33.0pre, +/// usable as `#[cfg(nix_at_least = "2.26")]`. +pub fn emit_version_cfg(nix_version: &str, relevant_versions: &[&str]) { + // Declare the known versions for cargo check-cfg + let versions = relevant_versions + .iter() + .map(|v| format!("\"{}\"", v)) + .collect::>() + .join(","); + + println!( + "cargo:rustc-check-cfg=cfg(nix_at_least,values({}))", + versions + ); + + let nix_version = parse_version(nix_version); + + for version_str in relevant_versions { + let version = parse_version(version_str); + if nix_version >= version { + println!("cargo:rustc-cfg=nix_at_least=\"{}\"", version_str); + } + } +} + +/// Parse a Nix version string into a comparable tuple `(major, minor, patch)`. +/// +/// Pre-release versions (containing `"pre"`) get patch = -1, sorting before stable releases. +/// Omitted patch defaults to 0. +/// +/// # Examples +/// +/// ``` +/// use nix_bindings_util::nix_version::parse_version; +/// +/// assert_eq!(parse_version("2.26"), (2, 26, 0)); +/// assert_eq!(parse_version("2.33.0pre"), (2, 33, -1)); +/// assert_eq!(parse_version("2.33"), (2, 33, 0)); +/// assert_eq!(parse_version("2.33.1"), (2, 33, 1)); +/// +/// // Pre-release versions sort before stable +/// assert!(parse_version("2.33.0pre") < parse_version("2.33")); +/// ``` +pub fn parse_version(version_str: &str) -> (u32, u32, i32) { + let parts = version_str.split('.').collect::>(); + let major = parts[0].parse::().unwrap(); + let minor = parts[1].parse::().unwrap(); + let patch = if parts.get(2).map_or(false, |s| s.contains("pre")) { + -1i32 + } else { + parts + .get(2) + .and_then(|s| s.parse::().ok()) + .unwrap_or(0) + }; + (major, minor, patch) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_parse_version() { + assert_eq!(parse_version("2.26"), (2, 26, 0)); + assert_eq!(parse_version("2.33.0pre"), (2, 33, -1)); + assert_eq!(parse_version("2.33"), (2, 33, 0)); + assert_eq!(parse_version("2.33.1"), (2, 33, 1)); + } + + #[test] + fn test_version_ordering() { + // Pre-release versions should sort before stable + assert!(parse_version("2.33.0pre") < parse_version("2.33")); + assert!(parse_version("2.33.0pre") < parse_version("2.33.0")); + + // Normal version ordering + assert!(parse_version("2.26") < parse_version("2.33")); + assert!(parse_version("2.33") < parse_version("2.33.1")); + } +} From 1bb495190a02621340c8aca5fbeb1513c605df47 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 9 Dec 2025 22:17:25 +0100 Subject: [PATCH 211/306] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nix': 'github:NixOS/nix/525755dadc153c68e5ada8d287ab19af26383fff?narHash=sha256-oMSKV0NFQpfwYQ4wcIIDuH%2B0AwCQl%2BbYRgPznx38i8k%3D' (2025-12-06) → 'github:NixOS/nix/7448aedd74e28174bfa33aad0d148c0070c86dfb?narHash=sha256-/Rr90FVpncaSWprG6%2B4t5VXHgDKDvFitzJPpdZgo%2B8I%3D' (2025-12-09) • Updated input 'nix-cargo-integration': 'github:yusdacra/nix-cargo-integration/0feff946314851edc88b9c783ad7d2dccd49abcc?narHash=sha256-S5k0HAxjq2sbf5BC5DOly66joE08HfiyEJD8v2U3yLc%3D' (2025-12-06) → 'github:yusdacra/nix-cargo-integration/92639295bbe953e4c8c32cbffa32dcb0a441118b?narHash=sha256-Ll29VFrT%2BVpnP4HG5LiL33IXuiz6cF2WfTwXONwMj7E%3D' (2025-12-09) • Updated input 'nix-cargo-integration/dream2nix': 'github:nix-community/dream2nix/ee20942e4524d3458a91108716c847a2d4299d2e?narHash=sha256-4OlkDA0yJyqt5iTX9NqtHNghvkWNzYqmtX7FxDmEXt4%3D' (2025-11-24) → 'github:nix-community/dream2nix/83c430ce6b6aedf149c5259f066bfff808722dbd?narHash=sha256-duTz4J4NP1edl/ZBdwZPduPM8j6g0yzjb8YH91T9vU0%3D' (2025-12-08) • Updated input 'nix-cargo-integration/rust-overlay': 'github:oxalica/rust-overlay/086fd19a68e80fcc8a298e9df4674982e4c498a6?narHash=sha256-FIJtt3Zil89/hLy9i7f0R2xXcJDPc3CeqiiCLfsFV0Y%3D' (2025-12-06) → 'github:oxalica/rust-overlay/7b50ad68415ae5be7ee4cc68fa570c420741b644?narHash=sha256-ngar%2ByP06x3%2B2k2Iey29uU0DWx5ur06h3iPBQXlU%2ByI%3D' (2025-12-09) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/f61125a668a320878494449750330ca58b78c557?narHash=sha256-BmPWzogsG2GsXZtlT%2BMTcAWeDK5hkbGRZTeZNW42fwA%3D' (2025-12-05) → 'github:NixOS/nixpkgs/addf7cf5f383a3101ecfba091b98d0a1263dc9b8?narHash=sha256-hM20uyap1a0M9d344I692r%2Bik4gTMyj60cQWO%2BhAYP8%3D' (2025-12-08) --- flake.lock | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/flake.lock b/flake.lock index d098b91..358d842 100644 --- a/flake.lock +++ b/flake.lock @@ -27,11 +27,11 @@ "pyproject-nix": "pyproject-nix" }, "locked": { - "lastModified": 1764021028, - "narHash": "sha256-4OlkDA0yJyqt5iTX9NqtHNghvkWNzYqmtX7FxDmEXt4=", + "lastModified": 1765228272, + "narHash": "sha256-duTz4J4NP1edl/ZBdwZPduPM8j6g0yzjb8YH91T9vU0=", "owner": "nix-community", "repo": "dream2nix", - "rev": "ee20942e4524d3458a91108716c847a2d4299d2e", + "rev": "83c430ce6b6aedf149c5259f066bfff808722dbd", "type": "github" }, "original": { @@ -170,11 +170,11 @@ "nixpkgs-regression": "nixpkgs-regression" }, "locked": { - "lastModified": 1764979397, - "narHash": "sha256-oMSKV0NFQpfwYQ4wcIIDuH+0AwCQl+bYRgPznx38i8k=", + "lastModified": 1765241125, + "narHash": "sha256-/Rr90FVpncaSWprG6+4t5VXHgDKDvFitzJPpdZgo+8I=", "owner": "NixOS", "repo": "nix", - "rev": "525755dadc153c68e5ada8d287ab19af26383fff", + "rev": "7448aedd74e28174bfa33aad0d148c0070c86dfb", "type": "github" }, "original": { @@ -196,11 +196,11 @@ "treefmt": "treefmt" }, "locked": { - "lastModified": 1765001919, - "narHash": "sha256-S5k0HAxjq2sbf5BC5DOly66joE08HfiyEJD8v2U3yLc=", + "lastModified": 1765261286, + "narHash": "sha256-Ll29VFrT+VpnP4HG5LiL33IXuiz6cF2WfTwXONwMj7E=", "owner": "yusdacra", "repo": "nix-cargo-integration", - "rev": "0feff946314851edc88b9c783ad7d2dccd49abcc", + "rev": "92639295bbe953e4c8c32cbffa32dcb0a441118b", "type": "github" }, "original": { @@ -211,11 +211,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1764950072, - "narHash": "sha256-BmPWzogsG2GsXZtlT+MTcAWeDK5hkbGRZTeZNW42fwA=", + "lastModified": 1765186076, + "narHash": "sha256-hM20uyap1a0M9d344I692r+ik4gTMyj60cQWO+hAYP8=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "f61125a668a320878494449750330ca58b78c557", + "rev": "addf7cf5f383a3101ecfba091b98d0a1263dc9b8", "type": "github" }, "original": { @@ -355,11 +355,11 @@ ] }, "locked": { - "lastModified": 1764988672, - "narHash": "sha256-FIJtt3Zil89/hLy9i7f0R2xXcJDPc3CeqiiCLfsFV0Y=", + "lastModified": 1765248027, + "narHash": "sha256-ngar+yP06x3+2k2Iey29uU0DWx5ur06h3iPBQXlU+yI=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "086fd19a68e80fcc8a298e9df4674982e4c498a6", + "rev": "7b50ad68415ae5be7ee4cc68fa570c420741b644", "type": "github" }, "original": { From 41a71b8e34c0c3a33c6eb9f2010eafac9f363348 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 9 Dec 2025 23:00:05 +0100 Subject: [PATCH 212/306] dev: Adjust shell - pre-commit shellHook doesn't add itself to PATH anymore - rust shellHook from module is now picked up by inputsFrom iiuc - add treefmt wrapper --- dev/flake-module.nix | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dev/flake-module.nix b/dev/flake-module.nix index 504a523..ff31f41 100644 --- a/dev/flake-module.nix +++ b/dev/flake-module.nix @@ -81,6 +81,9 @@ config.packages.nix ]; nativeBuildInputs = [ + pkgs.pre-commit + config.treefmt.build.wrapper + pkgs.rust-analyzer pkgs.nixfmt-rfc-style pkgs.rustfmt @@ -96,7 +99,6 @@ ]; shellHook = '' ${config.pre-commit.installationScript} - source ${../bindgen-gcc.sh} echo 1>&2 "Welcome to the development shell!" ''; # rust-analyzer needs a NIX_PATH for some reason From 9c215f2d4bcda1941e49a9ec7015a4a154d09e3a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 16 Dec 2025 01:48:19 +0100 Subject: [PATCH 213/306] maint: nci has changed owners --- flake.lock | 34 +++++++++++++++++----------------- flake.nix | 2 +- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/flake.lock b/flake.lock index 358d842..a2e04cd 100644 --- a/flake.lock +++ b/flake.lock @@ -27,11 +27,11 @@ "pyproject-nix": "pyproject-nix" }, "locked": { - "lastModified": 1765228272, - "narHash": "sha256-duTz4J4NP1edl/ZBdwZPduPM8j6g0yzjb8YH91T9vU0=", + "lastModified": 1765450682, + "narHash": "sha256-nZjNYEvNKZRF0+koTN2azGWbGlsvLNUTF6PcC0eqTa4=", "owner": "nix-community", "repo": "dream2nix", - "rev": "83c430ce6b6aedf149c5259f066bfff808722dbd", + "rev": "343053a4dfa53ce8abfc35e918cd4ab990abc58d", "type": "github" }, "original": { @@ -196,15 +196,15 @@ "treefmt": "treefmt" }, "locked": { - "lastModified": 1765261286, - "narHash": "sha256-Ll29VFrT+VpnP4HG5LiL33IXuiz6cF2WfTwXONwMj7E=", - "owner": "yusdacra", + "lastModified": 1765606778, + "narHash": "sha256-Rngkpanmds2XYIpFav634N2EJnmCVGlT4j04rd/XCLE=", + "owner": "90-008", "repo": "nix-cargo-integration", - "rev": "92639295bbe953e4c8c32cbffa32dcb0a441118b", + "rev": "f5b7a1543357cd2071cc847a9ae378328d7caa57", "type": "github" }, "original": { - "owner": "yusdacra", + "owner": "90-008", "repo": "nix-cargo-integration", "type": "github" } @@ -280,11 +280,11 @@ ] }, "locked": { - "lastModified": 1763759067, - "narHash": "sha256-LlLt2Jo/gMNYAwOgdRQBrsRoOz7BPRkzvNaI/fzXi2Q=", + "lastModified": 1765495779, + "narHash": "sha256-MhA7wmo/7uogLxiewwRRmIax70g6q1U/YemqTGoFHlM=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "2cccadc7357c0ba201788ae99c4dfa90728ef5e0", + "rev": "5635c32d666a59ec9a55cab87e898889869f7b71", "type": "github" }, "original": { @@ -326,11 +326,11 @@ ] }, "locked": { - "lastModified": 1752481895, - "narHash": "sha256-luVj97hIMpCbwhx3hWiRwjP2YvljWy8FM+4W9njDhLA=", + "lastModified": 1763017646, + "narHash": "sha256-Z+R2lveIp6Skn1VPH3taQIuMhABg1IizJd8oVdmdHsQ=", "owner": "pyproject-nix", "repo": "pyproject.nix", - "rev": "16ee295c25107a94e59a7fc7f2e5322851781162", + "rev": "47bd6f296502842643078d66128f7b5e5370790c", "type": "github" }, "original": { @@ -355,11 +355,11 @@ ] }, "locked": { - "lastModified": 1765248027, - "narHash": "sha256-ngar+yP06x3+2k2Iey29uU0DWx5ur06h3iPBQXlU+yI=", + "lastModified": 1765593578, + "narHash": "sha256-qbl874bCIy9+OLImdfBfZ9ITUDDjjTAB04Dk4PlZFV0=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "7b50ad68415ae5be7ee4cc68fa570c420741b644", + "rev": "348b94ed9ddffccdf1a65582a2dcff0a4a3eeeb4", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index e323a3c..5d386f1 100644 --- a/flake.nix +++ b/flake.nix @@ -5,7 +5,7 @@ flake-parts.url = "github:hercules-ci/flake-parts"; nix.url = "github:NixOS/nix"; nix.inputs.nixpkgs.follows = "nixpkgs"; - nix-cargo-integration.url = "github:yusdacra/nix-cargo-integration"; + nix-cargo-integration.url = "github:90-008/nix-cargo-integration"; nix-cargo-integration.inputs.nixpkgs.follows = "nixpkgs"; nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; }; From 351a2067765f3f908d68b3f6220776d486d5d5c4 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 15 Dec 2025 19:48:30 -0500 Subject: [PATCH 214/306] Fix clippy and track in CI `flake check` will run clippy. --- Cargo.toml | 9 ++++ flake.nix | 2 +- nix-bindings-expr/Cargo.toml | 3 ++ nix-bindings-expr/src/eval_state.rs | 55 ++++++++++++------------ nix-bindings-expr/src/value/__private.rs | 8 ++++ nix-bindings-fetchers/Cargo.toml | 3 ++ nix-bindings-flake/Cargo.toml | 3 ++ nix-bindings-flake/src/lib.rs | 22 +++++----- nix-bindings-store/Cargo.toml | 3 ++ nix-bindings-store/src/store.rs | 2 +- nix-bindings-util/Cargo.toml | 3 ++ nix-bindings-util/src/context.rs | 17 ++++---- nix-bindings-util/src/nix_version.rs | 2 +- 13 files changed, 82 insertions(+), 50 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 02f532b..d90bd3c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,3 +8,12 @@ members = [ "nix-bindings-util", ] resolver = "2" + +[workspace.lints.rust] +warnings = "deny" +dead-code = "allow" + +[workspace.lints.clippy] +type-complexity = "allow" +# We're still trying to make Nix more thread-safe, want forward-compat +arc-with-non-send-sync = "allow" diff --git a/flake.nix b/flake.nix index e323a3c..7646649 100644 --- a/flake.nix +++ b/flake.nix @@ -148,7 +148,7 @@ key = "nix-bindings-rust-add-checks"; config.checks = lib.concatMapAttrs ( k: v: - lib.optionalAttrs (lib.strings.hasPrefix "nix-bindings-" k && !lib.strings.hasSuffix "-clippy" k) { + lib.optionalAttrs (lib.strings.hasPrefix "nix-bindings-" k) { "dependency-${k}" = v; } ) nix-bindings-rust-perSystemConfig.config.checks; diff --git a/nix-bindings-expr/Cargo.toml b/nix-bindings-expr/Cargo.toml index 3c97117..1c1dfe0 100644 --- a/nix-bindings-expr/Cargo.toml +++ b/nix-bindings-expr/Cargo.toml @@ -16,3 +16,6 @@ lazy_static = "1.4" ctor = "0.2" tempfile = "3.10" cstr = "0.2" + +[lints] +workspace = true diff --git a/nix-bindings-expr/src/eval_state.rs b/nix-bindings-expr/src/eval_state.rs index 5f92b92..7d0808c 100644 --- a/nix-bindings-expr/src/eval_state.rs +++ b/nix-bindings-expr/src/eval_state.rs @@ -1382,7 +1382,7 @@ mod tests { let a = es.eval_from_string("2", "").unwrap(); let v = es.new_value_apply(&f, &a).unwrap(); let t = es.value_type_unforced(&v); - assert!(t == None); + assert!(t.is_none()); let i = es.require_int(&v).unwrap(); assert!(i == 3); }) @@ -1398,9 +1398,9 @@ mod tests { let a = es.eval_from_string("true", "").unwrap(); let v = es.new_value_apply(&f, &a).unwrap(); let t = es.value_type_unforced(&v); - assert!(t == None); + assert!(t.is_none()); let i = es.require_bool(&v).unwrap(); - assert!(i == false); + assert!(!i); }) .unwrap(); } @@ -1423,7 +1423,7 @@ mod tests { let mut es = EvalState::new(store, []).unwrap(); let v = make_thunk(&mut es, "1"); let t = es.value_type_unforced(&v); - assert!(t == None); + assert!(t.is_none()); }) .unwrap(); } @@ -1450,7 +1450,7 @@ mod tests { let mut es = EvalState::new(store, []).unwrap(); let v = make_thunk(&mut es, "{ a = 1; b = 2; }"); let t = es.value_type_unforced(&v); - assert!(t == None); + assert!(t.is_none()); let attrs = es.require_attrs_names_unsorted(&v).unwrap(); assert_eq!(attrs.len(), 2); }) @@ -1507,7 +1507,7 @@ mod tests { let s = format!("{e:#}"); if !s.contains("attribute `c` not found") { eprintln!("unexpected error message: {}", s); - assert!(false); + panic!(); } } } @@ -1542,7 +1542,7 @@ mod tests { Err(e) => { if !e.to_string().contains("oh no the error") { eprintln!("unexpected error message: {}", e); - assert!(false); + panic!(); } } } @@ -1594,7 +1594,7 @@ mod tests { Err(e) => { if !e.to_string().contains("oh no the error") { eprintln!("unexpected error message: {}", e); - assert!(false); + panic!(); } } } @@ -1730,7 +1730,7 @@ mod tests { let t = es.value_type_unforced(&v); assert!(t == Some(ValueType::String)); let s = es.require_string(&v).unwrap(); - assert!(s == ""); + assert!(s.is_empty()); }) .unwrap(); } @@ -1746,7 +1746,7 @@ mod tests { Err(e) => { if !e.to_string().contains("contains null byte") { eprintln!("{}", e); - assert!(false); + panic!(); } } } @@ -1832,7 +1832,7 @@ mod tests { let list: Vec = es.require_list_strict(&v).unwrap(); assert_eq!(list.len(), 2); assert_eq!(es.require_int(&list[0]).unwrap(), 42); - assert_eq!(es.require_bool(&list[1]).unwrap(), true); + assert!(es.require_bool(&list[1]).unwrap()); }) .unwrap(); } @@ -1971,7 +1971,7 @@ mod tests { let f = es.eval_from_string("x: x + 1", "").unwrap(); let a = es.eval_from_string("2", "").unwrap(); let v = es.new_value_apply(&f, &a).unwrap(); - assert!(es.value_type_unforced(&v) == None); + assert!(es.value_type_unforced(&v).is_none()); es.force(&v).unwrap(); let t = es.value_type_unforced(&v); assert!(t == Some(ValueType::Int)); @@ -1994,7 +1994,7 @@ mod tests { Err(e) => { if !e.to_string().contains("cannot coerce") { eprintln!("{}", e); - assert!(false); + panic!(); } } } @@ -2017,7 +2017,7 @@ mod tests { Err(e) => { if !e.to_string().contains("expected an integer but found") { eprintln!("{}", e); - assert!(false); + panic!(); } } } @@ -2041,7 +2041,7 @@ mod tests { Err(e) => { if !e.to_string().contains("cannot coerce") { eprintln!("{}", e); - assert!(false); + panic!(); } } } @@ -2063,7 +2063,7 @@ mod tests { Err(e) => { if !e.to_string().contains("called without required argument") { eprintln!("{}", e); - assert!(false); + panic!(); } } } @@ -2086,7 +2086,7 @@ mod tests { Err(e) => { if !e.to_string().contains("called without required argument") { eprintln!("{}", e); - assert!(false); + panic!(); } } } @@ -2111,7 +2111,7 @@ mod tests { Err(e) => { if !e.to_string().contains("called without required argument") { eprintln!("{}", e); - assert!(false); + panic!(); } } } @@ -2179,8 +2179,7 @@ mod tests { for derivation in derivations { assert!(store_contents .iter() - .find(|f| f.as_encoded_bytes().ends_with(derivation)) - .is_some()); + .any(|f| f.as_encoded_bytes().ends_with(derivation))); } assert!(!empty(read_dir(state.path()).unwrap())); @@ -2214,7 +2213,7 @@ mod tests { let a = es.require_int(a)?; let b = es.require_int(b)?; let c = *bias.lock().unwrap(); - Ok(es.new_value_int(a + b + c)?) + es.new_value_int(a + b + c) }), ) .unwrap(); @@ -2268,7 +2267,7 @@ mod tests { Err(e) => { if !e.to_string().contains("error with arg [2]") { eprintln!("unexpected error message: {}", e); - assert!(false); + panic!(); } } } @@ -2284,7 +2283,7 @@ mod tests { let v = es .new_value_thunk( "test_thunk", - Box::new(move |es: &mut EvalState| Ok(es.new_value_int(42)?)), + Box::new(move |es: &mut EvalState| es.new_value_int(42)), ) .unwrap(); es.force(&v).unwrap(); @@ -2318,11 +2317,11 @@ mod tests { "error message in test case eval_state_primop_anon_call_no_args_lazy", ) { eprintln!("unexpected error message: {}", e); - assert!(false); + panic!(); } if !e.to_string().contains("test_thunk") { eprintln!("unexpected error message: {}", e); - assert!(false); + panic!(); } } } @@ -2345,7 +2344,7 @@ mod tests { Box::new(|es, args| { let a = es.require_int(&args[0])?; let b = es.require_int(&args[1])?; - Ok(es.new_value_int(a + b)?) + es.new_value_int(a + b) }), ) .unwrap(); @@ -2385,11 +2384,11 @@ mod tests { Err(e) => { if !e.to_string().contains("The frob unexpectedly fizzled") { eprintln!("unexpected error message: {}", e); - assert!(false); + panic!(); } if !e.to_string().contains("frobnicate") { eprintln!("unexpected error message: {}", e); - assert!(false); + panic!(); } } } diff --git a/nix-bindings-expr/src/value/__private.rs b/nix-bindings-expr/src/value/__private.rs index 571dcd0..558bcbc 100644 --- a/nix-bindings-expr/src/value/__private.rs +++ b/nix-bindings-expr/src/value/__private.rs @@ -3,11 +3,19 @@ use super::Value; use nix_bindings_bindgen_raw as raw; /// See [Value::new]. +/// +/// # Safety +/// +/// See underlying function. pub unsafe fn raw_value_new(ptr: *mut raw::Value) -> Value { Value::new(ptr) } /// See [Value::new_borrowed]. +/// +/// # Safety +/// +/// See underlying function. pub unsafe fn raw_value_new_borrowed(ptr: *mut raw::Value) -> Value { Value::new_borrowed(ptr) } diff --git a/nix-bindings-fetchers/Cargo.toml b/nix-bindings-fetchers/Cargo.toml index fa75657..e8b2787 100644 --- a/nix-bindings-fetchers/Cargo.toml +++ b/nix-bindings-fetchers/Cargo.toml @@ -15,3 +15,6 @@ nix-bindings-bindgen-raw = { path = "../nix-bindings-bindgen-raw" } ctor = "0.2" tempfile = "3.10" cstr = "0.2" + +[lints] +workspace = true diff --git a/nix-bindings-flake/Cargo.toml b/nix-bindings-flake/Cargo.toml index 2b70980..67905cb 100644 --- a/nix-bindings-flake/Cargo.toml +++ b/nix-bindings-flake/Cargo.toml @@ -18,3 +18,6 @@ lazy_static = "1.4" ctor = "0.2" tempfile = "3.10" cstr = "0.2" + +[lints] +workspace = true diff --git a/nix-bindings-flake/src/lib.rs b/nix-bindings-flake/src/lib.rs index 91b4bb2..bd531b9 100644 --- a/nix-bindings-flake/src/lib.rs +++ b/nix-bindings-flake/src/lib.rs @@ -138,7 +138,7 @@ impl FlakeReference { }?; let ptr = NonNull::new(ptr) .context("flake_reference_and_fragment_from_string unexpectedly returned null")?; - Ok((FlakeReference { ptr: ptr }, r?)) + Ok((FlakeReference { ptr }, r?)) } } @@ -293,7 +293,7 @@ mod tests { let b = eval_state.require_bool(&v).unwrap(); - assert_eq!(b, true); + assert!(b); drop(gc_registration); } @@ -353,7 +353,7 @@ mod tests { .outputs(&flake_settings, &mut eval_state) .unwrap(); - let hello = eval_state.require_attrs_select(&outputs, &"hello").unwrap(); + let hello = eval_state.require_attrs_select(&outputs, "hello").unwrap(); let hello = eval_state.require_string(&hello).unwrap(); assert_eq!(hello, "potato"); @@ -394,7 +394,7 @@ mod tests { // a std::fs::write( - &tmp_dir.path().join("a/flake.nix"), + tmp_dir.path().join("a/flake.nix"), r#" { inputs.b.url = "@flake_dir_b@"; @@ -409,7 +409,7 @@ mod tests { // b std::fs::write( - &tmp_dir.path().join("b/flake.nix"), + tmp_dir.path().join("b/flake.nix"), r#" { outputs = { ... }: { @@ -422,7 +422,7 @@ mod tests { // c std::fs::write( - &tmp_dir.path().join("c/flake.nix"), + tmp_dir.path().join("c/flake.nix"), r#" { outputs = { ... }: { @@ -486,7 +486,7 @@ mod tests { .outputs(&flake_settings, &mut eval_state) .unwrap(); - let hello = eval_state.require_attrs_select(&outputs, &"hello").unwrap(); + let hello = eval_state.require_attrs_select(&outputs, "hello").unwrap(); let hello = eval_state.require_string(&hello).unwrap(); assert_eq!(hello, "BOB"); @@ -527,7 +527,7 @@ mod tests { let outputs = locked_flake .outputs(&flake_settings, &mut eval_state) .unwrap(); - let hello = eval_state.require_attrs_select(&outputs, &"hello").unwrap(); + let hello = eval_state.require_attrs_select(&outputs, "hello").unwrap(); let hello = eval_state.require_string(&hello).unwrap(); assert_eq!(hello, "BOB"); @@ -547,7 +547,7 @@ mod tests { let outputs = locked_flake .outputs(&flake_settings, &mut eval_state) .unwrap(); - let hello = eval_state.require_attrs_select(&outputs, &"hello").unwrap(); + let hello = eval_state.require_attrs_select(&outputs, "hello").unwrap(); let hello = eval_state.require_string(&hello).unwrap(); assert_eq!(hello, "BOB"); @@ -581,7 +581,7 @@ mod tests { let outputs = locked_flake .outputs(&flake_settings, &mut eval_state) .unwrap(); - let hello = eval_state.require_attrs_select(&outputs, &"hello").unwrap(); + let hello = eval_state.require_attrs_select(&outputs, "hello").unwrap(); let hello = eval_state.require_string(&hello).unwrap(); assert_eq!(hello, "Claire"); @@ -604,7 +604,7 @@ mod tests { let outputs = locked_flake .outputs(&flake_settings, &mut eval_state) .unwrap(); - let hello = eval_state.require_attrs_select(&outputs, &"hello").unwrap(); + let hello = eval_state.require_attrs_select(&outputs, "hello").unwrap(); let hello = eval_state.require_string(&hello).unwrap(); assert_eq!(hello, "BOB"); diff --git a/nix-bindings-store/Cargo.toml b/nix-bindings-store/Cargo.toml index c9bc96b..60cf958 100644 --- a/nix-bindings-store/Cargo.toml +++ b/nix-bindings-store/Cargo.toml @@ -22,3 +22,6 @@ tempfile = "3.10" pkg-config = "0.3" # Needed for version parsing in build.rs nix-bindings-util = { path = "../nix-bindings-util" } + +[lints] +workspace = true diff --git a/nix-bindings-store/src/store.rs b/nix-bindings-store/src/store.rs index aff5a66..fa9adad 100644 --- a/nix-bindings-store/src/store.rs +++ b/nix-bindings-store/src/store.rs @@ -514,7 +514,7 @@ mod tests { #[test] fn parse_store_path_fail() { let mut store = crate::store::Store::open(Some("dummy://"), []).unwrap(); - let store_path_string = format!("bash-interactive-5.2p26"); + let store_path_string = "bash-interactive-5.2p26".to_string(); let r = store.parse_store_path(store_path_string.as_str()); match r { Err(e) => { diff --git a/nix-bindings-util/Cargo.toml b/nix-bindings-util/Cargo.toml index a0fda9d..dfa6863 100644 --- a/nix-bindings-util/Cargo.toml +++ b/nix-bindings-util/Cargo.toml @@ -11,3 +11,6 @@ path = "src/lib.rs" anyhow = "1.0" nix-bindings-bindgen-raw = { path = "../nix-bindings-bindgen-raw" } ctor = "0.2" + +[lints] +workspace = true diff --git a/nix-bindings-util/src/context.rs b/nix-bindings-util/src/context.rs index e28ecb8..4999a7b 100644 --- a/nix-bindings-util/src/context.rs +++ b/nix-bindings-util/src/context.rs @@ -1,6 +1,5 @@ use anyhow::{bail, Result}; use nix_bindings_bindgen_raw as raw; -use std::os::raw::c_char; use std::ptr::null_mut; use std::ptr::NonNull; @@ -11,6 +10,12 @@ pub struct Context { inner: NonNull, } +impl Default for Context { + fn default() -> Self { + Self::new() + } +} + impl Context { pub fn new() -> Self { let ctx = unsafe { raw::c_context_create() }; @@ -48,11 +53,7 @@ impl Context { pub fn clear(&mut self) { unsafe { - raw::set_err_msg( - self.inner.as_ptr(), - raw::err_NIX_OK, - b"\0".as_ptr() as *const c_char, - ); + raw::set_err_msg(self.inner.as_ptr(), raw::err_NIX_OK, c"".as_ptr()); } } @@ -143,8 +144,8 @@ mod tests { unsafe { raw::set_err_msg( ctx_ptr, - raw::err_NIX_ERR_UNKNOWN.try_into().unwrap(), - b"dummy error message\0".as_ptr() as *const std::os::raw::c_char, + raw::err_NIX_ERR_UNKNOWN, + c"dummy error message".as_ptr(), ); } } diff --git a/nix-bindings-util/src/nix_version.rs b/nix-bindings-util/src/nix_version.rs index a577261..b6da8ce 100644 --- a/nix-bindings-util/src/nix_version.rs +++ b/nix-bindings-util/src/nix_version.rs @@ -65,7 +65,7 @@ pub fn parse_version(version_str: &str) -> (u32, u32, i32) { let parts = version_str.split('.').collect::>(); let major = parts[0].parse::().unwrap(); let minor = parts[1].parse::().unwrap(); - let patch = if parts.get(2).map_or(false, |s| s.contains("pre")) { + let patch = if parts.get(2).is_some_and(|s| s.contains("pre")) { -1i32 } else { parts From a02c057666c4e854db628fe7e8eec0098c4e0ca0 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 16 Dec 2025 01:48:56 +0100 Subject: [PATCH 215/306] doc: Update README --- README.md | 202 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 186 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 5e85853..5538048 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,149 @@ +# Nix Bindings for Rust -# `nix_bindings_*` crates +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. -Use the Nix [C API] from Rust. +## Overview -## Build with `nix-cargo-integration` +This workspace provides multiple crates that wrap different layers of the Nix C API: -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)). +- **`nix-bindings-util`** - Utility types and helpers (settings, context, version detection, string handling) +- **`nix-bindings-store`** - Store operations (paths, derivations, store management) +- **`nix-bindings-expr`** - Expression evaluation and type extraction +- **`nix-bindings-flake`** - Flake operations +- **`nix-bindings-fetchers`** - Fetcher functionality (requires Nix ≥ 2.29) -The dependency on Nix is taken care of with the [`nix-bindings-rust` flake-parts module](). +The `nix-bindings-bindgen-raw` crate contains the generated FFI bindings and is not intended for direct use. -Example usage: +## Features + +- **Nix evaluation** - Evaluate Nix expressions and create and extract values +- **Store integration** - Interact with the Nix store, manage paths, build derivations +- **Threading** - GC registration and memory management via `Drop` +- **Lazy evaluation** - Fine-grained control over evaluation strictness +- **Version compatibility** - Conditional compilation for different Nix versions + +## Quick Start + +Add the crates you need to your `Cargo.toml`: + +```toml +[dependencies] +nix-bindings-store = { git = "https://github.com/nixops4/nix-bindings-rust" } +nix-bindings-expr = { git = "https://github.com/nixops4/nix-bindings-rust" } +``` + +Basic example: + +```rust +use nix_bindings_expr::eval_state::{EvalState, init, gc_register_my_thread}; +use nix_bindings_store::store::Store; +use std::collections::HashMap; + +fn main() -> anyhow::Result<()> { + // Initialize Nix library and register thread with GC + init()?; + let guard = gc_register_my_thread()?; + + // Open a store connection and create an evaluation state + let store = Store::open(None, HashMap::new())?; + let mut eval_state = EvalState::new(store, [])?; + + // Evaluate a Nix expression + let value = eval_state.eval_from_string("[1 2 3]", "")?; + + // Extract typed values + let elements: Vec<_> = eval_state.require_list_strict(&value)?; + for element in elements { + let num = eval_state.require_int(&element)?; + println!("Element: {}", num); + } + + drop(guard); + Ok(()) +} +``` + +## Usage Examples + +### Evaluating Nix Expressions + +```rust +use nix_bindings_expr::eval_state::EvalState; + +// Evaluate and extract different types +let int_value = eval_state.eval_from_string("42", "")?; +let num = eval_state.require_int(&int_value)?; + +let str_value = eval_state.eval_from_string("\"hello\"", "")?; +let text = eval_state.require_string(&str_value)?; + +let attr_value = eval_state.eval_from_string("{ x = 1; y = 2; }", "")?; +let attrs = eval_state.require_attrs(&attr_value)?; +``` + +### Working with Lists + +```rust +let list_value = eval_state.eval_from_string("[1 2 3 4 5]", "")?; + +// Lazy: check size without evaluating elements +let size = eval_state.require_list_size(&list_value)?; + +// Selective: evaluate only accessed elements +if let Some(first) = eval_state.require_list_select_idx_strict(&list_value, 0)? { + let value = eval_state.require_int(&first)?; +} + +// Strict: evaluate all elements +let all_elements: Vec<_> = eval_state.require_list_strict(&list_value)?; +``` + +### Thread Safety + +Before using `EvalState` in a thread, register with the garbage collector: + +```rust +use nix_bindings_expr::eval_state::{init, gc_register_my_thread}; + +init()?; // Once per process +let guard = gc_register_my_thread()?; // Once per thread +// ... use EvalState ... +drop(guard); // Unregister when done +``` + +For more examples, see the documentation in each crate's source code. + +## Nix Version Compatibility + +The crates use conditional compilation to support multiple Nix versions: + +- **`nix-bindings-fetchers`** requires Nix ≥ 2.29 +- Some features in other crates require specific Nix versions + +The build system automatically detects the Nix version and enables appropriate features. + +## Integration with Nix Projects + +These crates use [nix-cargo-integration] for seamless integration with Nix builds. To use them in your Nix project: ```nix { - outputs = - inputs@{ self, flake-parts, ... }: - flake-parts.lib.mkFlake { inherit inputs; } - { + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-parts.url = "github:hercules-ci/flake-parts"; + nix-cargo-integration.url = "github:90-008/nix-cargo-integration"; + nix-bindings-rust.url = "github:nixops4/nix-bindings-rust"; + }; + + outputs = inputs@{ flake-parts, ... }: + flake-parts.lib.mkFlake { inherit inputs; } { imports = [ inputs.nix-cargo-integration.flakeModule inputs.nix-bindings-rust.modules.flake.default ]; perSystem = { config, pkgs, ... }: { - # optional: + # Optional: override Nix package nix-bindings-rust.nixPackage = pkgs.nix; nci.projects."myproject" = { @@ -36,18 +156,68 @@ Example usage: } ``` -## Hacking +See the [nix-cargo-integration documentation][nix-cargo-integration] for more options. -The following will open a shell with dependencies, and install pre-commit for automatic formatting. +## Development + +### Getting Started ```console $ nix develop ``` -### VSCode +### Building -#### rust-analyzer +```bash +# Build specific crates (release mode) +nix build .#nix-bindings-store-release +nix build .#nix-bindings-expr-release -If the rust-analyzer extension fails, make sure the devShell was loaded into VSCode via Nix Env Selector or direnv. +# Build with Cargo (in dev shell) +cargo build +cargo build --release +``` + +### Testing + +```bash +# Run tests for specific crates via Nix (recommended - includes proper store setup) +nix build .#checks.x86_64-linux.nix-bindings-store-tests +nix build .#checks.x86_64-linux.nix-bindings-expr-tests + +# Run all checks (tests + clippy + formatting) +nix flake check + +# Run tests with Cargo (in dev shell) +cargo test + +# Run specific test +cargo test test_name +``` + +### Memory Testing + +For FFI memory leak testing with valgrind, see [doc/hacking/test-ffi.md](doc/hacking/test-ffi.md). + +### Code Formatting + +```bash +treefmt +``` + +### IDE Setup + +For VSCode, load the dev shell via Nix Env Selector extension or direnv. + +## Documentation + +- [Nix C API Reference][C API] +- [nix-cargo-integration][nix-cargo-integration] +- [Hacking Guide](doc/hacking/test-ffi.md) + +## License + +See [LICENSE](LICENSE) file in the repository. [C API]: https://nix.dev/manual/nix/latest/c-api.html +[nix-cargo-integration]: https://github.com/90-008/nix-cargo-integration#readme From 278a1379e2c9812446f71eb812f44e5d1e95f9f0 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 16 Dec 2025 02:05:44 +0100 Subject: [PATCH 216/306] Document safety requirements for __private functions The previous "See underlying function" text didn't provide a way to find the underlying function, forcing users to search the codebase. Stating the safety contracts explicitly makes the API usable. --- nix-bindings-expr/src/value/__private.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/nix-bindings-expr/src/value/__private.rs b/nix-bindings-expr/src/value/__private.rs index 558bcbc..84151e1 100644 --- a/nix-bindings-expr/src/value/__private.rs +++ b/nix-bindings-expr/src/value/__private.rs @@ -2,20 +2,25 @@ use super::Value; use nix_bindings_bindgen_raw as raw; -/// See [Value::new]. +/// Take ownership of a new [`Value`]. +/// +/// This does not call `nix_gc_incref`, but does call `nix_gc_decref` when dropped. /// /// # Safety /// -/// See underlying function. +/// The caller must ensure that the provided `ptr` has a positive reference count, +/// and that `ptr` is not used after the returned `Value` is dropped. pub unsafe fn raw_value_new(ptr: *mut raw::Value) -> Value { Value::new(ptr) } -/// See [Value::new_borrowed]. +/// Borrow a reference to a [`Value`]. +/// +/// This calls `value_incref`, and the returned Value will call `value_decref` when dropped. /// /// # Safety /// -/// See underlying function. +/// The caller must ensure that the provided `ptr` has a positive reference count. pub unsafe fn raw_value_new_borrowed(ptr: *mut raw::Value) -> Value { Value::new_borrowed(ptr) } From e9fa877e85ec71d6f5fc9a0d8166e3353b69e6a9 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 15 Dec 2025 21:04:04 -0500 Subject: [PATCH 217/306] Bump Nix --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 358d842..38d5500 100644 --- a/flake.lock +++ b/flake.lock @@ -170,11 +170,11 @@ "nixpkgs-regression": "nixpkgs-regression" }, "locked": { - "lastModified": 1765241125, - "narHash": "sha256-/Rr90FVpncaSWprG6+4t5VXHgDKDvFitzJPpdZgo+8I=", + "lastModified": 1765843746, + "narHash": "sha256-w2sPb/1UH3q5HJambT0CkdUhOxY30MHB2gPI6hStGG8=", "owner": "NixOS", "repo": "nix", - "rev": "7448aedd74e28174bfa33aad0d148c0070c86dfb", + "rev": "a38fc659cc4f318a45df5b61ec21745ba972f5e6", "type": "github" }, "original": { From b78dd97051c46c743ae7887157799a87192c27a6 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 16 Dec 2025 03:06:58 +0100 Subject: [PATCH 218/306] Dereference workspace lints for nix-cargo-integration compatibility Replace `[lints] workspace = true` with explicit lint configuration in each crate's Cargo.toml. This allows nix-cargo-integration to build individual crates in isolation, as it cannot resolve workspace-level lint configuration when building crates independently. --- nix-bindings-expr/Cargo.toml | 10 ++++++++-- nix-bindings-fetchers/Cargo.toml | 10 ++++++++-- nix-bindings-flake/Cargo.toml | 10 ++++++++-- nix-bindings-store/Cargo.toml | 10 ++++++++-- nix-bindings-util/Cargo.toml | 10 ++++++++-- 5 files changed, 40 insertions(+), 10 deletions(-) diff --git a/nix-bindings-expr/Cargo.toml b/nix-bindings-expr/Cargo.toml index 1c1dfe0..044e70a 100644 --- a/nix-bindings-expr/Cargo.toml +++ b/nix-bindings-expr/Cargo.toml @@ -17,5 +17,11 @@ ctor = "0.2" tempfile = "3.10" cstr = "0.2" -[lints] -workspace = true +[lints.rust] +warnings = "deny" +dead-code = "allow" + +[lints.clippy] +type-complexity = "allow" +# We're still trying to make Nix more thread-safe, want forward-compat +arc-with-non-send-sync = "allow" diff --git a/nix-bindings-fetchers/Cargo.toml b/nix-bindings-fetchers/Cargo.toml index e8b2787..6cbb103 100644 --- a/nix-bindings-fetchers/Cargo.toml +++ b/nix-bindings-fetchers/Cargo.toml @@ -16,5 +16,11 @@ ctor = "0.2" tempfile = "3.10" cstr = "0.2" -[lints] -workspace = true +[lints.rust] +warnings = "deny" +dead-code = "allow" + +[lints.clippy] +type-complexity = "allow" +# We're still trying to make Nix more thread-safe, want forward-compat +arc-with-non-send-sync = "allow" diff --git a/nix-bindings-flake/Cargo.toml b/nix-bindings-flake/Cargo.toml index 67905cb..3606a7c 100644 --- a/nix-bindings-flake/Cargo.toml +++ b/nix-bindings-flake/Cargo.toml @@ -19,5 +19,11 @@ ctor = "0.2" tempfile = "3.10" cstr = "0.2" -[lints] -workspace = true +[lints.rust] +warnings = "deny" +dead-code = "allow" + +[lints.clippy] +type-complexity = "allow" +# We're still trying to make Nix more thread-safe, want forward-compat +arc-with-non-send-sync = "allow" diff --git a/nix-bindings-store/Cargo.toml b/nix-bindings-store/Cargo.toml index 60cf958..f3d7211 100644 --- a/nix-bindings-store/Cargo.toml +++ b/nix-bindings-store/Cargo.toml @@ -23,5 +23,11 @@ pkg-config = "0.3" # Needed for version parsing in build.rs nix-bindings-util = { path = "../nix-bindings-util" } -[lints] -workspace = true +[lints.rust] +warnings = "deny" +dead-code = "allow" + +[lints.clippy] +type-complexity = "allow" +# We're still trying to make Nix more thread-safe, want forward-compat +arc-with-non-send-sync = "allow" diff --git a/nix-bindings-util/Cargo.toml b/nix-bindings-util/Cargo.toml index dfa6863..9242e99 100644 --- a/nix-bindings-util/Cargo.toml +++ b/nix-bindings-util/Cargo.toml @@ -12,5 +12,11 @@ anyhow = "1.0" nix-bindings-bindgen-raw = { path = "../nix-bindings-bindgen-raw" } ctor = "0.2" -[lints] -workspace = true +[lints.rust] +warnings = "deny" +dead-code = "allow" + +[lints.clippy] +type-complexity = "allow" +# We're still trying to make Nix more thread-safe, want forward-compat +arc-with-non-send-sync = "allow" From 424e23f9dc8cd7a7fa8f3b15ce73e42ca9a5ab52 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 28 Dec 2025 18:10:36 +0100 Subject: [PATCH 219/306] Exclude clippy checks from flake module for dependents Dependents don't need to run clippy on nix-bindings-rust code; that's handled by this repo's own CI. The flake module provides build/test checks for dependents, not linting. This partially reverts 351a206. --- flake.nix | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/flake.nix b/flake.nix index 0cd0142..f9896ad 100644 --- a/flake.nix +++ b/flake.nix @@ -108,7 +108,13 @@ }; /** - Adds flake checks that test the bindings with the provided nix package. + A flake-parts module for dependents to import. Also dogfooded locally + (extra, not required for normal CI). + + Adds flake checks that test the nix-bindings crates with the + dependent's nix package. + + See https://github.com/nixops4/nix-bindings-rust?tab=readme-ov-file#integration-with-nix-projects */ flake-parts-modules.tested = # Consumer toplevel @@ -146,9 +152,12 @@ in { key = "nix-bindings-rust-add-checks"; + # Exclude clippy checks; those are part of this repo's local CI. + # This module is for dependents (and local dogfooding), which + # don't need to run clippy on nix-bindings-rust. config.checks = lib.concatMapAttrs ( k: v: - lib.optionalAttrs (lib.strings.hasPrefix "nix-bindings-" k) { + lib.optionalAttrs (lib.strings.hasPrefix "nix-bindings-" k && !lib.strings.hasSuffix "-clippy" k) { "dependency-${k}" = v; } ) nix-bindings-rust-perSystemConfig.config.checks; From eb056b27d996810d36538721d841616f21ffdb72 Mon Sep 17 00:00:00 2001 From: Hercules CI Effects Date: Thu, 1 Jan 2026 02:32:56 +0000 Subject: [PATCH 220/306] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'flake-parts': 'github:hercules-ci/flake-parts/2cccadc7357c0ba201788ae99c4dfa90728ef5e0?narHash=sha256-LlLt2Jo/gMNYAwOgdRQBrsRoOz7BPRkzvNaI/fzXi2Q%3D' (2025-11-21) → 'github:hercules-ci/flake-parts/a34fae9c08a15ad73f295041fec82323541400a9?narHash=sha256-XswHlK/Qtjasvhd1nOa1e8MgZ8GS//jBoTqWtrS1Giw%3D' (2025-12-15) • Updated input 'flake-parts/nixpkgs-lib': 'github:nix-community/nixpkgs.lib/719359f4562934ae99f5443f20aa06c2ffff91fc?narHash=sha256-b0yj6kfvO8ApcSE%2BQmA6mUfu8IYG6/uU28OFn4PaC8M%3D' (2025-10-29) → 'github:nix-community/nixpkgs.lib/2075416fcb47225d9b68ac469a5c4801a9c4dd85?narHash=sha256-k00uTP4JNfmejrCLJOwdObYC9jHRrr/5M/a/8L2EIdo%3D' (2025-12-14) • Updated input 'nix': 'github:NixOS/nix/a38fc659cc4f318a45df5b61ec21745ba972f5e6?narHash=sha256-w2sPb/1UH3q5HJambT0CkdUhOxY30MHB2gPI6hStGG8%3D' (2025-12-16) → 'github:NixOS/nix/843395a2c87c9b0f0eb9b39bd8567c77cdabcc4e?narHash=sha256-%2BrJcHQE8yjZTR/PZpHr3eya6/Dt1DoGe/9cRoPXTI3k%3D' (2025-12-31) • Updated input 'nix-cargo-integration': 'github:90-008/nix-cargo-integration/f5b7a1543357cd2071cc847a9ae378328d7caa57?narHash=sha256-Rngkpanmds2XYIpFav634N2EJnmCVGlT4j04rd/XCLE%3D' (2025-12-13) → 'github:90-008/nix-cargo-integration/525ccaf24a3e3b5d28a4dbec8a9c4e1ca759db8e?narHash=sha256-5hrqsS9pnPl1ptnJVtEUkUcbr9feYH8OG14pKRnbG2U%3D' (2025-12-31) • Updated input 'nix-cargo-integration/dream2nix': 'github:nix-community/dream2nix/343053a4dfa53ce8abfc35e918cd4ab990abc58d?narHash=sha256-nZjNYEvNKZRF0%2BkoTN2azGWbGlsvLNUTF6PcC0eqTa4%3D' (2025-12-11) → 'github:nix-community/dream2nix/69eb01fa0995e1e90add49d8ca5bcba213b0416f?narHash=sha256-5FBZbbWR1Csp3Y2icfRkxMJw/a/5FGg8hCXej2//bbI%3D' (2025-12-17) • Updated input 'nix-cargo-integration/parts': 'github:hercules-ci/flake-parts/5635c32d666a59ec9a55cab87e898889869f7b71?narHash=sha256-MhA7wmo/7uogLxiewwRRmIax70g6q1U/YemqTGoFHlM%3D' (2025-12-11) → 'github:hercules-ci/flake-parts/a34fae9c08a15ad73f295041fec82323541400a9?narHash=sha256-XswHlK/Qtjasvhd1nOa1e8MgZ8GS//jBoTqWtrS1Giw%3D' (2025-12-15) • Updated input 'nix-cargo-integration/rust-overlay': 'github:oxalica/rust-overlay/348b94ed9ddffccdf1a65582a2dcff0a4a3eeeb4?narHash=sha256-qbl874bCIy9%2BOLImdfBfZ9ITUDDjjTAB04Dk4PlZFV0%3D' (2025-12-13) → 'github:oxalica/rust-overlay/6d14586a5917a1ec7f045ac97e6d00c68ea5d9f3?narHash=sha256-TjfAb58Ybz/93e2jP0qD846dj%2BVqiY7wk%2BEqsxcZ708%3D' (2025-12-31) • Updated input 'nix-cargo-integration/treefmt': 'github:numtide/treefmt-nix/5b4ee75aeefd1e2d5a1cc43cf6ba65eba75e83e4?narHash=sha256-AlEObg0syDl%2BSpi4LsZIBrjw%2BsnSVU4T8MOeuZJUJjM%3D' (2025-11-12) → 'github:numtide/treefmt-nix/dec15f37015ac2e774c84d0952d57fcdf169b54d?narHash=sha256-yOt/FTB7oSEKQH9EZMFMeuldK1HGpQs2eAzdS9hNS/o%3D' (2025-12-30) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/addf7cf5f383a3101ecfba091b98d0a1263dc9b8?narHash=sha256-hM20uyap1a0M9d344I692r%2Bik4gTMyj60cQWO%2BhAYP8%3D' (2025-12-08) → 'github:NixOS/nixpkgs/c0b0e0fddf73fd517c3471e546c0df87a42d53f4?narHash=sha256-coBu0ONtFzlwwVBzmjacUQwj3G%2BlybcZ1oeNSQkgC0M%3D' (2025-12-28) --- flake.lock | 54 +++++++++++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/flake.lock b/flake.lock index 724b1e6..c9d2ea9 100644 --- a/flake.lock +++ b/flake.lock @@ -27,11 +27,11 @@ "pyproject-nix": "pyproject-nix" }, "locked": { - "lastModified": 1765450682, - "narHash": "sha256-nZjNYEvNKZRF0+koTN2azGWbGlsvLNUTF6PcC0eqTa4=", + "lastModified": 1765953015, + "narHash": "sha256-5FBZbbWR1Csp3Y2icfRkxMJw/a/5FGg8hCXej2//bbI=", "owner": "nix-community", "repo": "dream2nix", - "rev": "343053a4dfa53ce8abfc35e918cd4ab990abc58d", + "rev": "69eb01fa0995e1e90add49d8ca5bcba213b0416f", "type": "github" }, "original": { @@ -77,11 +77,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1763759067, - "narHash": "sha256-LlLt2Jo/gMNYAwOgdRQBrsRoOz7BPRkzvNaI/fzXi2Q=", + "lastModified": 1765835352, + "narHash": "sha256-XswHlK/Qtjasvhd1nOa1e8MgZ8GS//jBoTqWtrS1Giw=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "2cccadc7357c0ba201788ae99c4dfa90728ef5e0", + "rev": "a34fae9c08a15ad73f295041fec82323541400a9", "type": "github" }, "original": { @@ -170,11 +170,11 @@ "nixpkgs-regression": "nixpkgs-regression" }, "locked": { - "lastModified": 1765843746, - "narHash": "sha256-w2sPb/1UH3q5HJambT0CkdUhOxY30MHB2gPI6hStGG8=", + "lastModified": 1767212231, + "narHash": "sha256-+rJcHQE8yjZTR/PZpHr3eya6/Dt1DoGe/9cRoPXTI3k=", "owner": "NixOS", "repo": "nix", - "rev": "a38fc659cc4f318a45df5b61ec21745ba972f5e6", + "rev": "843395a2c87c9b0f0eb9b39bd8567c77cdabcc4e", "type": "github" }, "original": { @@ -196,11 +196,11 @@ "treefmt": "treefmt" }, "locked": { - "lastModified": 1765606778, - "narHash": "sha256-Rngkpanmds2XYIpFav634N2EJnmCVGlT4j04rd/XCLE=", + "lastModified": 1767162115, + "narHash": "sha256-5hrqsS9pnPl1ptnJVtEUkUcbr9feYH8OG14pKRnbG2U=", "owner": "90-008", "repo": "nix-cargo-integration", - "rev": "f5b7a1543357cd2071cc847a9ae378328d7caa57", + "rev": "525ccaf24a3e3b5d28a4dbec8a9c4e1ca759db8e", "type": "github" }, "original": { @@ -211,11 +211,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1765186076, - "narHash": "sha256-hM20uyap1a0M9d344I692r+ik4gTMyj60cQWO+hAYP8=", + "lastModified": 1766902085, + "narHash": "sha256-coBu0ONtFzlwwVBzmjacUQwj3G+lybcZ1oeNSQkgC0M=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "addf7cf5f383a3101ecfba091b98d0a1263dc9b8", + "rev": "c0b0e0fddf73fd517c3471e546c0df87a42d53f4", "type": "github" }, "original": { @@ -243,11 +243,11 @@ }, "nixpkgs-lib": { "locked": { - "lastModified": 1761765539, - "narHash": "sha256-b0yj6kfvO8ApcSE+QmA6mUfu8IYG6/uU28OFn4PaC8M=", + "lastModified": 1765674936, + "narHash": "sha256-k00uTP4JNfmejrCLJOwdObYC9jHRrr/5M/a/8L2EIdo=", "owner": "nix-community", "repo": "nixpkgs.lib", - "rev": "719359f4562934ae99f5443f20aa06c2ffff91fc", + "rev": "2075416fcb47225d9b68ac469a5c4801a9c4dd85", "type": "github" }, "original": { @@ -280,11 +280,11 @@ ] }, "locked": { - "lastModified": 1765495779, - "narHash": "sha256-MhA7wmo/7uogLxiewwRRmIax70g6q1U/YemqTGoFHlM=", + "lastModified": 1765835352, + "narHash": "sha256-XswHlK/Qtjasvhd1nOa1e8MgZ8GS//jBoTqWtrS1Giw=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "5635c32d666a59ec9a55cab87e898889869f7b71", + "rev": "a34fae9c08a15ad73f295041fec82323541400a9", "type": "github" }, "original": { @@ -355,11 +355,11 @@ ] }, "locked": { - "lastModified": 1765593578, - "narHash": "sha256-qbl874bCIy9+OLImdfBfZ9ITUDDjjTAB04Dk4PlZFV0=", + "lastModified": 1767149068, + "narHash": "sha256-TjfAb58Ybz/93e2jP0qD846dj+VqiY7wk+EqsxcZ708=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "348b94ed9ddffccdf1a65582a2dcff0a4a3eeeb4", + "rev": "6d14586a5917a1ec7f045ac97e6d00c68ea5d9f3", "type": "github" }, "original": { @@ -399,11 +399,11 @@ ] }, "locked": { - "lastModified": 1762938485, - "narHash": "sha256-AlEObg0syDl+Spi4LsZIBrjw+snSVU4T8MOeuZJUJjM=", + "lastModified": 1767122417, + "narHash": "sha256-yOt/FTB7oSEKQH9EZMFMeuldK1HGpQs2eAzdS9hNS/o=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "5b4ee75aeefd1e2d5a1cc43cf6ba65eba75e83e4", + "rev": "dec15f37015ac2e774c84d0952d57fcdf169b54d", "type": "github" }, "original": { From 1135ff8c7504b86972989278655dceeb021e947c Mon Sep 17 00:00:00 2001 From: Hercules CI Effects Date: Thu, 1 Jan 2026 02:32:59 +0000 Subject: [PATCH 221/306] dev/flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'hercules-ci-effects': 'github:hercules-ci/hercules-ci-effects/b0585849abe7d02a774a853f7952d07bb910fd9e?narHash=sha256-jZi%2B9yKmeTMsJ4ZNqRei/wL16%2BQwYGrCl4EJ3QHfoDU%3D' (2025-11-15) → 'github:hercules-ci/hercules-ci-effects/edcbb19948b6caf1700434e369fde6ff9e6a3c93?narHash=sha256-UQhfCggNGDc7eam%2BEittlYmeW89CZVT1KkFIHZWBH7k%3D' (2025-12-15) • Updated input 'hercules-ci-effects/flake-parts': 'github:hercules-ci/flake-parts/52a2caecc898d0b46b2b905f058ccc5081f842da?narHash=sha256-8oNVE8TrD19ulHinjaqONf9QWCKK%2Bw4url56cdStMpM%3D' (2025-11-12) → 'github:hercules-ci/flake-parts/5635c32d666a59ec9a55cab87e898889869f7b71?narHash=sha256-MhA7wmo/7uogLxiewwRRmIax70g6q1U/YemqTGoFHlM%3D' (2025-12-11) • Updated input 'hercules-ci-effects/nixpkgs': 'github:NixOS/nixpkgs/c5ae371f1a6a7fd27823bc500d9390b38c05fa55?narHash=sha256-4PqRErxfe%2B2toFJFgcRKZ0UI9NSIOJa%2B7RXVtBhy4KE%3D' (2025-11-12) → 'github:NixOS/nixpkgs/2fbfb1d73d239d2402a8fe03963e37aab15abe8b?narHash=sha256-9VvC20PJPsleGMewwcWYKGzDIyjckEz8uWmT0vCDYK0%3D' (2025-12-11) • Updated input 'pre-commit-hooks-nix': 'github:cachix/pre-commit-hooks.nix/548fc44fca28a5e81c5d6b846e555e6b9c2a5a3c?narHash=sha256-rhSqPNxDVow7OQKi4qS5H8Au0P4S3AYbawBSmJNUtBQ%3D' (2025-12-06) → 'github:cachix/pre-commit-hooks.nix/b68b780b69702a090c8bb1b973bab13756cc7a27?narHash=sha256-t3T/xm8zstHRLx%2BpIHxVpQTiySbKqcQbK%2Br%2B01XVKc0%3D' (2025-12-16) • Updated input 'treefmt-nix': 'github:numtide/treefmt-nix/5b4ee75aeefd1e2d5a1cc43cf6ba65eba75e83e4?narHash=sha256-AlEObg0syDl%2BSpi4LsZIBrjw%2BsnSVU4T8MOeuZJUJjM%3D' (2025-11-12) → 'github:numtide/treefmt-nix/dec15f37015ac2e774c84d0952d57fcdf169b54d?narHash=sha256-yOt/FTB7oSEKQH9EZMFMeuldK1HGpQs2eAzdS9hNS/o%3D' (2025-12-30) --- dev/flake.lock | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/dev/flake.lock b/dev/flake.lock index 8e0c0f4..b1ca88e 100644 --- a/dev/flake.lock +++ b/dev/flake.lock @@ -24,11 +24,11 @@ ] }, "locked": { - "lastModified": 1762980239, - "narHash": "sha256-8oNVE8TrD19ulHinjaqONf9QWCKK+w4url56cdStMpM=", + "lastModified": 1765495779, + "narHash": "sha256-MhA7wmo/7uogLxiewwRRmIax70g6q1U/YemqTGoFHlM=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "52a2caecc898d0b46b2b905f058ccc5081f842da", + "rev": "5635c32d666a59ec9a55cab87e898889869f7b71", "type": "github" }, "original": { @@ -63,11 +63,11 @@ "nixpkgs": "nixpkgs" }, "locked": { - "lastModified": 1763182882, - "narHash": "sha256-jZi+9yKmeTMsJ4ZNqRei/wL16+QwYGrCl4EJ3QHfoDU=", + "lastModified": 1765774562, + "narHash": "sha256-UQhfCggNGDc7eam+EittlYmeW89CZVT1KkFIHZWBH7k=", "owner": "hercules-ci", "repo": "hercules-ci-effects", - "rev": "b0585849abe7d02a774a853f7952d07bb910fd9e", + "rev": "edcbb19948b6caf1700434e369fde6ff9e6a3c93", "type": "github" }, "original": { @@ -78,11 +78,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1762977756, - "narHash": "sha256-4PqRErxfe+2toFJFgcRKZ0UI9NSIOJa+7RXVtBhy4KE=", + "lastModified": 1765472234, + "narHash": "sha256-9VvC20PJPsleGMewwcWYKGzDIyjckEz8uWmT0vCDYK0=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "c5ae371f1a6a7fd27823bc500d9390b38c05fa55", + "rev": "2fbfb1d73d239d2402a8fe03963e37aab15abe8b", "type": "github" }, "original": { @@ -99,11 +99,11 @@ "nixpkgs": [] }, "locked": { - "lastModified": 1765016596, - "narHash": "sha256-rhSqPNxDVow7OQKi4qS5H8Au0P4S3AYbawBSmJNUtBQ=", + "lastModified": 1765911976, + "narHash": "sha256-t3T/xm8zstHRLx+pIHxVpQTiySbKqcQbK+r+01XVKc0=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "548fc44fca28a5e81c5d6b846e555e6b9c2a5a3c", + "rev": "b68b780b69702a090c8bb1b973bab13756cc7a27", "type": "github" }, "original": { @@ -124,11 +124,11 @@ "nixpkgs": [] }, "locked": { - "lastModified": 1762938485, - "narHash": "sha256-AlEObg0syDl+Spi4LsZIBrjw+snSVU4T8MOeuZJUJjM=", + "lastModified": 1767122417, + "narHash": "sha256-yOt/FTB7oSEKQH9EZMFMeuldK1HGpQs2eAzdS9hNS/o=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "5b4ee75aeefd1e2d5a1cc43cf6ba65eba75e83e4", + "rev": "dec15f37015ac2e774c84d0952d57fcdf169b54d", "type": "github" }, "original": { From 8f6ec2ec5c3ba8ab33126ec79de7702835592902 Mon Sep 17 00:00:00 2001 From: Aaron Andersen Date: Mon, 5 Jan 2026 20:57:51 -0500 Subject: [PATCH 222/306] Fix path coercion by calling eval_state_builder_load() Without this call, settings from the global Nix configuration are never loaded, leaving readOnlyMode = true (the default). This prevents Nix from adding paths to the store during evaluation, causing errors like: error: path '/some/local/path' does not exist This fix loads global settings before creating the EvalState, enabling path coercion for local file references. --- nix-bindings-expr/src/eval_state.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/nix-bindings-expr/src/eval_state.rs b/nix-bindings-expr/src/eval_state.rs index 7d0808c..6fdedab 100644 --- a/nix-bindings-expr/src/eval_state.rs +++ b/nix-bindings-expr/src/eval_state.rs @@ -288,6 +288,15 @@ impl EvalStateBuilder { let mut context = Context::new(); + // Load settings from global configuration (including readOnlyMode = false). + // This is necessary for path coercion to work (adding files to the store). + unsafe { + check_call!(raw::eval_state_builder_load( + &mut context, + self.eval_state_builder + ))?; + } + // Note: these raw C string pointers borrow from self.lookup_path let mut lookup_path: Vec<*const c_char> = self .lookup_path From 8aad50d4dc9b67802d36d00012bd4e45a0dae627 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 10 Jan 2026 01:28:32 +0100 Subject: [PATCH 223/306] clippy: Suppress missing_safety_doc for bindgen output --- nix-bindings-bindgen-raw/src/lib.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/nix-bindings-bindgen-raw/src/lib.rs b/nix-bindings-bindgen-raw/src/lib.rs index 05c7e41..63f3849 100644 --- a/nix-bindings-bindgen-raw/src/lib.rs +++ b/nix-bindings-bindgen-raw/src/lib.rs @@ -7,10 +7,19 @@ //! Normally you don't have to use this crate directly. //! Instead use `nix-store` and `nix-expr`. +// This file must only contain generated code, so that the module-level +// #![allow(...)] attributes don't suppress warnings in hand-written code. +// If you need to add hand-written code, use a submodule to isolate the +// generated code. See: +// https://github.com/nixops4/nixops4/pull/138/commits/330c3881be3d3cf3e59adebbe0ab1c0f15f6d2c9 + // Standard bindgen suppressions for C naming conventions #![allow(non_upper_case_globals)] #![allow(non_camel_case_types)] #![allow(non_snake_case)] +// Clippy suppressions for generated C bindings +// bindgen doesn't generate safety docs +#![allow(clippy::missing_safety_doc)] // Rustdoc suppressions for generated C documentation // The C headers contain Doxygen-style documentation that doesn't translate // well to Rust's rustdoc format, causing various warnings: From 5b1988eb420200322400d3999fff7737697dd3a8 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 10 Jan 2026 01:28:46 +0100 Subject: [PATCH 224/306] nci: Deny warnings in dev profile --- nci.nix | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/nci.nix b/nci.nix index 053a832..cc864b0 100644 --- a/nci.nix +++ b/nci.nix @@ -8,6 +8,10 @@ # https://flake.parts/options/nix-cargo-integration nci.projects.nix-bindings = { path = ./.; + profiles = { + dev.drvConfig.env.RUSTFLAGS = "-D warnings"; + release.runTests = true; + }; drvConfig = { imports = [ # Downstream projects import this into depsDrvConfig instead From e6148b587f7141a3d25ada47f3093d869f576d6e Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 12 Jan 2026 19:56:04 +0100 Subject: [PATCH 225/306] Rename nix-bindings-bindgen-raw to nix-bindings-util-sys Adopt idiomatic Rust `-sys` crate naming convention, aligning with the direction in PR #37 without adopting the full crate splitting. --- Cargo.lock | 119 ++++++++---------- Cargo.toml | 2 +- README.md | 2 +- nix-bindings-expr/Cargo.toml | 2 +- nix-bindings-expr/src/eval_state.rs | 2 +- nix-bindings-expr/src/primop.rs | 2 +- nix-bindings-expr/src/value.rs | 2 +- nix-bindings-expr/src/value/__private.rs | 2 +- nix-bindings-fetchers/Cargo.toml | 2 +- nix-bindings-fetchers/src/lib.rs | 2 +- nix-bindings-flake/Cargo.toml | 2 +- nix-bindings-flake/src/lib.rs | 2 +- nix-bindings-store/Cargo.toml | 2 +- nix-bindings-store/src/derivation.rs | 2 +- nix-bindings-store/src/path.rs | 2 +- nix-bindings-store/src/store.rs | 2 +- .../Cargo.toml | 2 +- .../README.md | 2 +- .../build.rs | 0 .../include/nix-c-raw.h | 0 .../src/lib.rs | 0 nix-bindings-util/Cargo.toml | 2 +- nix-bindings-util/src/context.rs | 4 +- nix-bindings-util/src/settings.rs | 2 +- nix-bindings-util/src/string_return.rs | 4 +- 25 files changed, 78 insertions(+), 87 deletions(-) rename {nix-bindings-bindgen-raw => nix-bindings-util-sys}/Cargo.toml (83%) rename {nix-bindings-bindgen-raw => nix-bindings-util-sys}/README.md (96%) rename {nix-bindings-bindgen-raw => nix-bindings-util-sys}/build.rs (100%) rename {nix-bindings-bindgen-raw => nix-bindings-util-sys}/include/nix-c-raw.h (100%) rename {nix-bindings-bindgen-raw => nix-bindings-util-sys}/src/lib.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 416f121..e4a8151 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 4 [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] @@ -42,9 +42,9 @@ dependencies = [ [[package]] name = "bitflags" -version = "2.9.4" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] name = "cexpr" @@ -57,9 +57,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "clang-sys" @@ -105,7 +105,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] @@ -116,14 +116,14 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "getrandom" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "libc", "r-efi", - "wasi", + "wasip2", ] [[package]] @@ -134,11 +134,11 @@ checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "home" -version = "0.5.11" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -164,9 +164,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.176" +version = "0.2.180" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" [[package]] name = "libloading" @@ -192,9 +192,9 @@ checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "log" -version = "0.4.28" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "memchr" @@ -208,14 +208,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" -[[package]] -name = "nix-bindings-bindgen-raw" -version = "0.1.0" -dependencies = [ - "bindgen", - "pkg-config", -] - [[package]] name = "nix-bindings-expr" version = "0.1.0" @@ -224,9 +216,9 @@ dependencies = [ "cstr", "ctor", "lazy_static", - "nix-bindings-bindgen-raw", "nix-bindings-store", "nix-bindings-util", + "nix-bindings-util-sys", "tempfile", ] @@ -237,9 +229,9 @@ dependencies = [ "anyhow", "cstr", "ctor", - "nix-bindings-bindgen-raw", "nix-bindings-store", "nix-bindings-util", + "nix-bindings-util-sys", "tempfile", ] @@ -251,11 +243,11 @@ dependencies = [ "cstr", "ctor", "lazy_static", - "nix-bindings-bindgen-raw", "nix-bindings-expr", "nix-bindings-fetchers", "nix-bindings-store", "nix-bindings-util", + "nix-bindings-util-sys", "tempfile", ] @@ -266,8 +258,8 @@ dependencies = [ "anyhow", "ctor", "lazy_static", - "nix-bindings-bindgen-raw", "nix-bindings-util", + "nix-bindings-util-sys", "pkg-config", "tempfile", ] @@ -278,7 +270,15 @@ version = "0.1.0" dependencies = [ "anyhow", "ctor", - "nix-bindings-bindgen-raw", + "nix-bindings-util-sys", +] + +[[package]] +name = "nix-bindings-util-sys" +version = "0.1.0" +dependencies = [ + "bindgen", + "pkg-config", ] [[package]] @@ -315,18 +315,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.101" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.41" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" +checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" dependencies = [ "proc-macro2", ] @@ -339,9 +339,9 @@ checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "regex" -version = "1.11.3" +version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b5288124840bee7b386bc413c487869b360b2b4ec421ea56425128692f2a82c" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" dependencies = [ "aho-corasick", "memchr", @@ -351,9 +351,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.11" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "833eb9ce86d40ef33cb1306d8accf7bc8ec2bfea4355cbdebb3df68b40925cad" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" dependencies = [ "aho-corasick", "memchr", @@ -362,9 +362,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "rustc-hash" @@ -387,15 +387,15 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys 0.11.0", - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] @@ -406,9 +406,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "syn" -version = "2.0.106" +version = "2.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" dependencies = [ "proc-macro2", "quote", @@ -417,31 +417,22 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.23.0" +version = "3.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" +checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" dependencies = [ "fastrand", "getrandom", "once_cell", - "rustix 1.1.2", - "windows-sys 0.61.1", + "rustix 1.1.3", + "windows-sys 0.61.2", ] [[package]] name = "unicode-ident" -version = "1.0.19" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" - -[[package]] -name = "wasi" -version = "0.14.7+wasi-0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" -dependencies = [ - "wasip2", -] +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "wasip2" @@ -466,9 +457,9 @@ dependencies = [ [[package]] name = "windows-link" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-sys" @@ -481,9 +472,9 @@ dependencies = [ [[package]] name = "windows-sys" -version = "0.61.1" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f109e41dd4a3c848907eb83d5a42ea98b3769495597450cf6d153507b166f0f" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ "windows-link", ] diff --git a/Cargo.toml b/Cargo.toml index d90bd3c..7cfd424 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] members = [ - "nix-bindings-bindgen-raw", + "nix-bindings-util-sys", "nix-bindings-expr", "nix-bindings-fetchers", "nix-bindings-flake", diff --git a/README.md b/README.md index 5538048..44c0975 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ This workspace provides multiple crates that wrap different layers of the Nix C - **`nix-bindings-flake`** - Flake operations - **`nix-bindings-fetchers`** - Fetcher functionality (requires Nix ≥ 2.29) -The `nix-bindings-bindgen-raw` crate contains the generated FFI bindings and is not intended for direct use. +The `nix-bindings-util-sys` crate contains the generated FFI bindings and is not intended for direct use. ## Features diff --git a/nix-bindings-expr/Cargo.toml b/nix-bindings-expr/Cargo.toml index 044e70a..50f2d8a 100644 --- a/nix-bindings-expr/Cargo.toml +++ b/nix-bindings-expr/Cargo.toml @@ -11,7 +11,7 @@ path = "src/lib.rs" anyhow = "1.0" nix-bindings-store = { path = "../nix-bindings-store" } nix-bindings-util = { path = "../nix-bindings-util" } -nix-bindings-bindgen-raw = { path = "../nix-bindings-bindgen-raw" } +nix-bindings-util-sys = { path = "../nix-bindings-util-sys" } lazy_static = "1.4" ctor = "0.2" tempfile = "3.10" diff --git a/nix-bindings-expr/src/eval_state.rs b/nix-bindings-expr/src/eval_state.rs index 7d0808c..04749a9 100644 --- a/nix-bindings-expr/src/eval_state.rs +++ b/nix-bindings-expr/src/eval_state.rs @@ -134,7 +134,6 @@ use anyhow::Context as _; use anyhow::{bail, Result}; use cstr::cstr; use lazy_static::lazy_static; -use nix_bindings_bindgen_raw as raw; use nix_bindings_store::path::StorePath; use nix_bindings_store::store::{Store, StoreWeak}; use nix_bindings_util::context::Context; @@ -142,6 +141,7 @@ use nix_bindings_util::string_return::{ callback_get_result_string, callback_get_result_string_data, }; use nix_bindings_util::{check_call, check_call_opt_key, result_string_init}; +use nix_bindings_util_sys as raw; use std::ffi::{c_char, CString}; use std::iter::FromIterator; use std::os::raw::c_uint; diff --git a/nix-bindings-expr/src/primop.rs b/nix-bindings-expr/src/primop.rs index f430d4d..7996605 100644 --- a/nix-bindings-expr/src/primop.rs +++ b/nix-bindings-expr/src/primop.rs @@ -1,8 +1,8 @@ use crate::eval_state::{EvalState, EvalStateWeak}; use crate::value::Value; use anyhow::Result; -use nix_bindings_bindgen_raw as raw; use nix_bindings_util::check_call; +use nix_bindings_util_sys as raw; use std::ffi::{c_int, c_void, CStr, CString}; use std::mem::ManuallyDrop; use std::ptr::{null, null_mut}; diff --git a/nix-bindings-expr/src/value.rs b/nix-bindings-expr/src/value.rs index 086a343..534eafa 100644 --- a/nix-bindings-expr/src/value.rs +++ b/nix-bindings-expr/src/value.rs @@ -1,7 +1,7 @@ pub mod __private; -use nix_bindings_bindgen_raw as raw; use nix_bindings_util::{check_call, context::Context}; +use nix_bindings_util_sys as raw; use std::ptr::{null_mut, NonNull}; // TODO: test: cloning a thunk does not duplicate the evaluation. diff --git a/nix-bindings-expr/src/value/__private.rs b/nix-bindings-expr/src/value/__private.rs index 84151e1..4d59514 100644 --- a/nix-bindings-expr/src/value/__private.rs +++ b/nix-bindings-expr/src/value/__private.rs @@ -1,6 +1,6 @@ //! Functions that are relevant for other bindings modules, but normally not end users. use super::Value; -use nix_bindings_bindgen_raw as raw; +use nix_bindings_util_sys as raw; /// Take ownership of a new [`Value`]. /// diff --git a/nix-bindings-fetchers/Cargo.toml b/nix-bindings-fetchers/Cargo.toml index 6cbb103..411e890 100644 --- a/nix-bindings-fetchers/Cargo.toml +++ b/nix-bindings-fetchers/Cargo.toml @@ -11,7 +11,7 @@ path = "src/lib.rs" anyhow = "1.0" nix-bindings-store = { path = "../nix-bindings-store" } nix-bindings-util = { path = "../nix-bindings-util" } -nix-bindings-bindgen-raw = { path = "../nix-bindings-bindgen-raw" } +nix-bindings-util-sys = { path = "../nix-bindings-util-sys" } ctor = "0.2" tempfile = "3.10" cstr = "0.2" diff --git a/nix-bindings-fetchers/src/lib.rs b/nix-bindings-fetchers/src/lib.rs index 06bf45f..2ea0b84 100644 --- a/nix-bindings-fetchers/src/lib.rs +++ b/nix-bindings-fetchers/src/lib.rs @@ -1,6 +1,6 @@ use anyhow::{Context as _, Result}; -use nix_bindings_bindgen_raw as raw; use nix_bindings_util::context::{self, Context}; +use nix_bindings_util_sys as raw; use std::ptr::NonNull; pub struct FetchersSettings { diff --git a/nix-bindings-flake/Cargo.toml b/nix-bindings-flake/Cargo.toml index 3606a7c..3aa2c51 100644 --- a/nix-bindings-flake/Cargo.toml +++ b/nix-bindings-flake/Cargo.toml @@ -13,7 +13,7 @@ nix-bindings-expr = { path = "../nix-bindings-expr" } nix-bindings-fetchers = { path = "../nix-bindings-fetchers" } nix-bindings-store = { path = "../nix-bindings-store" } nix-bindings-util = { path = "../nix-bindings-util" } -nix-bindings-bindgen-raw = { path = "../nix-bindings-bindgen-raw" } +nix-bindings-util-sys = { path = "../nix-bindings-util-sys" } lazy_static = "1.4" ctor = "0.2" tempfile = "3.10" diff --git a/nix-bindings-flake/src/lib.rs b/nix-bindings-flake/src/lib.rs index bd531b9..5da43dc 100644 --- a/nix-bindings-flake/src/lib.rs +++ b/nix-bindings-flake/src/lib.rs @@ -1,7 +1,6 @@ use std::{ffi::CString, os::raw::c_char, ptr::NonNull}; use anyhow::{Context as _, Result}; -use nix_bindings_bindgen_raw as raw; use nix_bindings_expr::eval_state::EvalState; use nix_bindings_fetchers::FetchersSettings; use nix_bindings_util::{ @@ -9,6 +8,7 @@ use nix_bindings_util::{ result_string_init, string_return::{callback_get_result_string, callback_get_result_string_data}, }; +use nix_bindings_util_sys as raw; /// Store settings for the flakes feature. pub struct FlakeSettings { diff --git a/nix-bindings-store/Cargo.toml b/nix-bindings-store/Cargo.toml index f3d7211..aeed512 100644 --- a/nix-bindings-store/Cargo.toml +++ b/nix-bindings-store/Cargo.toml @@ -11,7 +11,7 @@ path = "src/lib.rs" [dependencies] anyhow = "1.0" nix-bindings-util = { path = "../nix-bindings-util" } -nix-bindings-bindgen-raw = { path = "../nix-bindings-bindgen-raw" } +nix-bindings-util-sys = { path = "../nix-bindings-util-sys" } lazy_static = "1.4" [dev-dependencies] diff --git a/nix-bindings-store/src/derivation.rs b/nix-bindings-store/src/derivation.rs index 6be5e75..f036dce 100644 --- a/nix-bindings-store/src/derivation.rs +++ b/nix-bindings-store/src/derivation.rs @@ -1,6 +1,6 @@ #![cfg(nix_at_least = "2.33.0pre")] -use nix_bindings_bindgen_raw as raw; +use nix_bindings_util_sys as raw; use std::ptr::NonNull; /// A Nix derivation diff --git a/nix-bindings-store/src/path.rs b/nix-bindings-store/src/path.rs index b07bf11..ca694f2 100644 --- a/nix-bindings-store/src/path.rs +++ b/nix-bindings-store/src/path.rs @@ -1,11 +1,11 @@ use std::ptr::NonNull; use anyhow::Result; -use nix_bindings_bindgen_raw as raw; use nix_bindings_util::{ result_string_init, string_return::{callback_get_result_string, callback_get_result_string_data}, }; +use nix_bindings_util_sys as raw; pub struct StorePath { raw: NonNull, diff --git a/nix-bindings-store/src/store.rs b/nix-bindings-store/src/store.rs index fa9adad..8f03942 100644 --- a/nix-bindings-store/src/store.rs +++ b/nix-bindings-store/src/store.rs @@ -1,11 +1,11 @@ use anyhow::{bail, Error, Result}; use lazy_static::lazy_static; -use nix_bindings_bindgen_raw as raw; use nix_bindings_util::context::Context; use nix_bindings_util::string_return::{ callback_get_result_string, callback_get_result_string_data, }; use nix_bindings_util::{check_call, result_string_init}; +use nix_bindings_util_sys as raw; #[cfg(nix_at_least = "2.33.0pre")] use std::collections::BTreeMap; use std::collections::HashMap; diff --git a/nix-bindings-bindgen-raw/Cargo.toml b/nix-bindings-util-sys/Cargo.toml similarity index 83% rename from nix-bindings-bindgen-raw/Cargo.toml rename to nix-bindings-util-sys/Cargo.toml index 7668b60..433c6cd 100644 --- a/nix-bindings-bindgen-raw/Cargo.toml +++ b/nix-bindings-util-sys/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "nix-bindings-bindgen-raw" +name = "nix-bindings-util-sys" version = "0.1.0" edition = "2021" build = "build.rs" diff --git a/nix-bindings-bindgen-raw/README.md b/nix-bindings-util-sys/README.md similarity index 96% rename from nix-bindings-bindgen-raw/README.md rename to nix-bindings-util-sys/README.md index 1c7e5f0..b74d9a1 100644 --- a/nix-bindings-bindgen-raw/README.md +++ b/nix-bindings-util-sys/README.md @@ -1,4 +1,4 @@ -# nix-bindings-bindgen-raw +# nix-bindings-util-sys This crate contains generated bindings for the Nix C API. **You should not have to use this crate directly,** and so you should probably not add it to your dependencies. diff --git a/nix-bindings-bindgen-raw/build.rs b/nix-bindings-util-sys/build.rs similarity index 100% rename from nix-bindings-bindgen-raw/build.rs rename to nix-bindings-util-sys/build.rs diff --git a/nix-bindings-bindgen-raw/include/nix-c-raw.h b/nix-bindings-util-sys/include/nix-c-raw.h similarity index 100% rename from nix-bindings-bindgen-raw/include/nix-c-raw.h rename to nix-bindings-util-sys/include/nix-c-raw.h diff --git a/nix-bindings-bindgen-raw/src/lib.rs b/nix-bindings-util-sys/src/lib.rs similarity index 100% rename from nix-bindings-bindgen-raw/src/lib.rs rename to nix-bindings-util-sys/src/lib.rs diff --git a/nix-bindings-util/Cargo.toml b/nix-bindings-util/Cargo.toml index 9242e99..3609dff 100644 --- a/nix-bindings-util/Cargo.toml +++ b/nix-bindings-util/Cargo.toml @@ -9,7 +9,7 @@ path = "src/lib.rs" [dependencies] anyhow = "1.0" -nix-bindings-bindgen-raw = { path = "../nix-bindings-bindgen-raw" } +nix-bindings-util-sys = { path = "../nix-bindings-util-sys" } ctor = "0.2" [lints.rust] diff --git a/nix-bindings-util/src/context.rs b/nix-bindings-util/src/context.rs index 4999a7b..af82b27 100644 --- a/nix-bindings-util/src/context.rs +++ b/nix-bindings-util/src/context.rs @@ -1,9 +1,9 @@ use anyhow::{bail, Result}; -use nix_bindings_bindgen_raw as raw; +use nix_bindings_util_sys as raw; use std::ptr::null_mut; use std::ptr::NonNull; -/// A context for error handling, when interacting directly with the generated bindings for the C API in [nix_bindings_bindgen_raw]. +/// A context for error handling, when interacting directly with the generated bindings for the C API in [nix_bindings_util_sys]. /// /// The `nix-store` and `nix-expr` libraries that consume this type internally store a private context in their `EvalState` and `Store` structs to avoid allocating a new context for each operation. The state of a context is irrelevant when used correctly (e.g. with [check_call!]), so it's safe to reuse, and safe to allocate more contexts in methods such as [Clone::clone]. pub struct Context { diff --git a/nix-bindings-util/src/settings.rs b/nix-bindings-util/src/settings.rs index 5b5b5ef..72af247 100644 --- a/nix-bindings-util/src/settings.rs +++ b/nix-bindings-util/src/settings.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use nix_bindings_bindgen_raw as raw; +use nix_bindings_util_sys as raw; use std::sync::Mutex; use crate::{ diff --git a/nix-bindings-util/src/string_return.rs b/nix-bindings-util/src/string_return.rs index 9851a0d..205cebe 100644 --- a/nix-bindings-util/src/string_return.rs +++ b/nix-bindings-util/src/string_return.rs @@ -52,9 +52,9 @@ macro_rules! result_string_init { #[cfg(test)] mod tests { use super::*; - use nix_bindings_bindgen_raw as raw; + use nix_bindings_util_sys as raw; - /// Typecheck the function signature against the generated bindings in nix_bindings_bindgen_raw. + /// Typecheck the function signature against the generated bindings in nix_bindings_util_sys. static _CALLBACK_GET_RESULT_STRING: raw::get_string_callback = Some(callback_get_result_string); #[test] From 6d941407fe2331f5b3fe2c3b7101d02c23790702 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 12 Jan 2026 20:17:40 +0100 Subject: [PATCH 226/306] Fix broken rustdoc links --- nix-bindings-expr/src/eval_state.rs | 18 +++++++++--------- nix-bindings-expr/src/value.rs | 4 ++-- nix-bindings-store/src/store.rs | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/nix-bindings-expr/src/eval_state.rs b/nix-bindings-expr/src/eval_state.rs index 04749a9..44b6945 100644 --- a/nix-bindings-expr/src/eval_state.rs +++ b/nix-bindings-expr/src/eval_state.rs @@ -660,9 +660,9 @@ impl EvalState { /// /// Returns [`Err`] if evaluation failed or the value is not an attribute set. /// - /// Returns [`Ok(None)`] if the attribute is not present. + /// Returns `Ok(None)` if the attribute is not present. /// - /// Returns [`Ok(Some(value))`] if the attribute is present. + /// Returns `Ok(Some(value))` if the attribute is present. #[doc(alias = "nix_get_attr_byname")] #[doc(alias = "get_attr_byname")] #[doc(alias = "get_attr_opt")] @@ -712,11 +712,11 @@ impl EvalState { /// Extracts an element from a [list][`ValueType::List`] Nix value by index. /// /// Forces [evaluation](https://nix.dev/manual/nix/latest/language/evaluation.html) and verifies the value is a list. - /// Forces evaluation of the selected element, similar to [`require_attrs_select`]. + /// Forces evaluation of the selected element, similar to [`Self::require_attrs_select`]. /// - /// Returns [`Ok(Some(value))`] if the element is found. + /// Returns `Ok(Some(value))` if the element is found. /// - /// Returns [`Ok(None)`] if the index is out of bounds. + /// Returns `Ok(None)` if the index is out of bounds. /// /// Returns [`Err`] if evaluation failed, the element contains an error (e.g., `throw`), or the value is not a list. #[doc(alias = "get")] @@ -908,7 +908,7 @@ impl EvalState { /// Applies a function to an argument and returns the result. /// /// Forces [evaluation](https://nix.dev/manual/nix/latest/language/evaluation.html) of the function application. - /// For a lazy version, see [`new_value_apply`]. + /// For a lazy version, see [`Self::new_value_apply`]. #[doc(alias = "nix_value_call")] #[doc(alias = "value_call")] #[doc(alias = "apply")] @@ -990,7 +990,7 @@ impl EvalState { /// Applies a function to an argument lazily, creating a [thunk](https://nix.dev/manual/nix/latest/language/evaluation.html#laziness). /// /// Does not force [evaluation](https://nix.dev/manual/nix/latest/language/evaluation.html) of the function application. - /// For an eager version, see [`call`]. + /// For an eager version, see [`Self::call`]. #[doc(alias = "lazy_apply")] #[doc(alias = "thunk_apply")] #[doc(alias = "defer_call")] @@ -1037,10 +1037,10 @@ impl EvalState { Ok(value) } - /// Creates a new [attribute set][`ValueType::Attrs`] Nix value from an iterator of name-value pairs. + /// Creates a new [attribute set][`ValueType::AttrSet`] Nix value from an iterator of name-value pairs. /// /// Accepts any iterator that yields `(String, Value)` pairs and has an exact size. - /// Common usage includes [`Vec`], [`HashMap`], and array literals. + /// Common usage includes [`Vec`], [`std::collections::HashMap`], and array literals. /// /// # Examples /// diff --git a/nix-bindings-expr/src/value.rs b/nix-bindings-expr/src/value.rs index 534eafa..1d7f866 100644 --- a/nix-bindings-expr/src/value.rs +++ b/nix-bindings-expr/src/value.rs @@ -71,7 +71,7 @@ pub struct Value { impl Value { /// Take ownership of a new [`Value`]. /// - /// This does not call [`nix_c_raw::gc_incref`], but does call [`nix_c_raw::nix_gc_decref`] when [dropped][`Drop`]. + /// This does not call [`nix_bindings_util_sys::gc_incref`], but does call [`nix_bindings_util_sys::gc_decref`] when [dropped][`Drop`]. /// /// # Safety /// @@ -84,7 +84,7 @@ impl Value { /// Borrow a reference to a [`Value`]. /// - /// This calls [`nix_c_raw::value_incref`], and the returned Value will call [`nix_c_raw::value_decref`] when dropped. + /// This calls [`nix_bindings_util_sys::value_incref`], and the returned Value will call [`nix_bindings_util_sys::value_decref`] when dropped. /// /// # Safety /// diff --git a/nix-bindings-store/src/store.rs b/nix-bindings-store/src/store.rs index 8f03942..61b60b6 100644 --- a/nix-bindings-store/src/store.rs +++ b/nix-bindings-store/src/store.rs @@ -101,7 +101,7 @@ pub struct Store { impl Store { /// Open a store. /// - /// See [nix_c_raw::store_open] for more information. + /// See [`nix_bindings_util_sys::store_open`] for more information. #[doc(alias = "nix_store_open")] pub fn open<'a, 'b>( url: Option<&str>, From 1caf7b85ddc42a20b0012bb4a3dac87cd05d364b Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 12 Jan 2026 20:44:01 +0100 Subject: [PATCH 227/306] Add version requirements to workspace dependencies cargo publish requires all dependencies to have version specifications. --- nix-bindings-expr/Cargo.toml | 6 +++--- nix-bindings-fetchers/Cargo.toml | 6 +++--- nix-bindings-flake/Cargo.toml | 10 +++++----- nix-bindings-store/Cargo.toml | 6 +++--- nix-bindings-util/Cargo.toml | 2 +- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/nix-bindings-expr/Cargo.toml b/nix-bindings-expr/Cargo.toml index 50f2d8a..b2bf44d 100644 --- a/nix-bindings-expr/Cargo.toml +++ b/nix-bindings-expr/Cargo.toml @@ -9,9 +9,9 @@ path = "src/lib.rs" [dependencies] anyhow = "1.0" -nix-bindings-store = { path = "../nix-bindings-store" } -nix-bindings-util = { path = "../nix-bindings-util" } -nix-bindings-util-sys = { path = "../nix-bindings-util-sys" } +nix-bindings-store = { path = "../nix-bindings-store", version = "0.1.0" } +nix-bindings-util = { path = "../nix-bindings-util", version = "0.1.0" } +nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.1.0" } lazy_static = "1.4" ctor = "0.2" tempfile = "3.10" diff --git a/nix-bindings-fetchers/Cargo.toml b/nix-bindings-fetchers/Cargo.toml index 411e890..d262bf5 100644 --- a/nix-bindings-fetchers/Cargo.toml +++ b/nix-bindings-fetchers/Cargo.toml @@ -9,9 +9,9 @@ path = "src/lib.rs" [dependencies] anyhow = "1.0" -nix-bindings-store = { path = "../nix-bindings-store" } -nix-bindings-util = { path = "../nix-bindings-util" } -nix-bindings-util-sys = { path = "../nix-bindings-util-sys" } +nix-bindings-store = { path = "../nix-bindings-store", version = "0.1.0" } +nix-bindings-util = { path = "../nix-bindings-util", version = "0.1.0" } +nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.1.0" } ctor = "0.2" tempfile = "3.10" cstr = "0.2" diff --git a/nix-bindings-flake/Cargo.toml b/nix-bindings-flake/Cargo.toml index 3aa2c51..710f6b2 100644 --- a/nix-bindings-flake/Cargo.toml +++ b/nix-bindings-flake/Cargo.toml @@ -9,11 +9,11 @@ path = "src/lib.rs" [dependencies] anyhow = "1.0" -nix-bindings-expr = { path = "../nix-bindings-expr" } -nix-bindings-fetchers = { path = "../nix-bindings-fetchers" } -nix-bindings-store = { path = "../nix-bindings-store" } -nix-bindings-util = { path = "../nix-bindings-util" } -nix-bindings-util-sys = { path = "../nix-bindings-util-sys" } +nix-bindings-expr = { path = "../nix-bindings-expr", version = "0.1.0" } +nix-bindings-fetchers = { path = "../nix-bindings-fetchers", version = "0.1.0" } +nix-bindings-store = { path = "../nix-bindings-store", version = "0.1.0" } +nix-bindings-util = { path = "../nix-bindings-util", version = "0.1.0" } +nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.1.0" } lazy_static = "1.4" ctor = "0.2" tempfile = "3.10" diff --git a/nix-bindings-store/Cargo.toml b/nix-bindings-store/Cargo.toml index aeed512..47fb22f 100644 --- a/nix-bindings-store/Cargo.toml +++ b/nix-bindings-store/Cargo.toml @@ -10,8 +10,8 @@ path = "src/lib.rs" [dependencies] anyhow = "1.0" -nix-bindings-util = { path = "../nix-bindings-util" } -nix-bindings-util-sys = { path = "../nix-bindings-util-sys" } +nix-bindings-util = { path = "../nix-bindings-util", version = "0.1.0" } +nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.1.0" } lazy_static = "1.4" [dev-dependencies] @@ -21,7 +21,7 @@ tempfile = "3.10" [build-dependencies] pkg-config = "0.3" # Needed for version parsing in build.rs -nix-bindings-util = { path = "../nix-bindings-util" } +nix-bindings-util = { path = "../nix-bindings-util", version = "0.1.0" } [lints.rust] warnings = "deny" diff --git a/nix-bindings-util/Cargo.toml b/nix-bindings-util/Cargo.toml index 3609dff..cf58b44 100644 --- a/nix-bindings-util/Cargo.toml +++ b/nix-bindings-util/Cargo.toml @@ -9,7 +9,7 @@ path = "src/lib.rs" [dependencies] anyhow = "1.0" -nix-bindings-util-sys = { path = "../nix-bindings-util-sys" } +nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.1.0" } ctor = "0.2" [lints.rust] From 862dcd9eebe3bc1b5d40424f173c234a5d614e80 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 12 Jan 2026 20:45:44 +0100 Subject: [PATCH 228/306] Add description and repository metadata to all crates --- nix-bindings-expr/Cargo.toml | 2 ++ nix-bindings-fetchers/Cargo.toml | 2 ++ nix-bindings-flake/Cargo.toml | 2 ++ nix-bindings-store/Cargo.toml | 2 ++ nix-bindings-util-sys/Cargo.toml | 2 ++ nix-bindings-util/Cargo.toml | 2 ++ 6 files changed, 12 insertions(+) diff --git a/nix-bindings-expr/Cargo.toml b/nix-bindings-expr/Cargo.toml index b2bf44d..41b3cc7 100644 --- a/nix-bindings-expr/Cargo.toml +++ b/nix-bindings-expr/Cargo.toml @@ -3,6 +3,8 @@ name = "nix-bindings-expr" version = "0.1.0" edition = "2021" license = "LGPL-2.1" +description = "Rust bindings to Nix expression evaluator" +repository = "https://github.com/nixops4/nix-bindings-rust" [lib] path = "src/lib.rs" diff --git a/nix-bindings-fetchers/Cargo.toml b/nix-bindings-fetchers/Cargo.toml index d262bf5..db0c130 100644 --- a/nix-bindings-fetchers/Cargo.toml +++ b/nix-bindings-fetchers/Cargo.toml @@ -3,6 +3,8 @@ name = "nix-bindings-fetchers" version = "0.1.0" edition = "2021" license = "LGPL-2.1" +description = "Rust bindings to Nix fetchers" +repository = "https://github.com/nixops4/nix-bindings-rust" [lib] path = "src/lib.rs" diff --git a/nix-bindings-flake/Cargo.toml b/nix-bindings-flake/Cargo.toml index 710f6b2..70fd26e 100644 --- a/nix-bindings-flake/Cargo.toml +++ b/nix-bindings-flake/Cargo.toml @@ -3,6 +3,8 @@ name = "nix-bindings-flake" version = "0.1.0" edition = "2021" license = "LGPL-2.1" +description = "Rust bindings to Nix flakes" +repository = "https://github.com/nixops4/nix-bindings-rust" [lib] path = "src/lib.rs" diff --git a/nix-bindings-store/Cargo.toml b/nix-bindings-store/Cargo.toml index 47fb22f..d34afac 100644 --- a/nix-bindings-store/Cargo.toml +++ b/nix-bindings-store/Cargo.toml @@ -4,6 +4,8 @@ version = "0.1.0" edition = "2021" build = "build.rs" license = "LGPL-2.1" +description = "Rust bindings to Nix store library" +repository = "https://github.com/nixops4/nix-bindings-rust" [lib] path = "src/lib.rs" diff --git a/nix-bindings-util-sys/Cargo.toml b/nix-bindings-util-sys/Cargo.toml index 433c6cd..691da18 100644 --- a/nix-bindings-util-sys/Cargo.toml +++ b/nix-bindings-util-sys/Cargo.toml @@ -4,6 +4,8 @@ version = "0.1.0" edition = "2021" build = "build.rs" license = "LGPL-2.1" +description = "Low-level FFI bindings to Nix utility library" +repository = "https://github.com/nixops4/nix-bindings-rust" [lib] path = "src/lib.rs" diff --git a/nix-bindings-util/Cargo.toml b/nix-bindings-util/Cargo.toml index cf58b44..3af13f1 100644 --- a/nix-bindings-util/Cargo.toml +++ b/nix-bindings-util/Cargo.toml @@ -3,6 +3,8 @@ name = "nix-bindings-util" version = "0.1.0" edition = "2021" license = "LGPL-2.1" +description = "Rust bindings to Nix utility library" +repository = "https://github.com/nixops4/nix-bindings-rust" [lib] path = "src/lib.rs" From c87f6f543beb4eb556feee6dc3ca2bd99c888896 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 12 Jan 2026 23:43:00 +0100 Subject: [PATCH 229/306] dev/flake.nix: Switch to hci cargo-publish-module branch --- dev/flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/flake.nix b/dev/flake.nix index 09a06a1..4668d46 100644 --- a/dev/flake.nix +++ b/dev/flake.nix @@ -3,7 +3,7 @@ inputs = { pre-commit-hooks-nix.url = "github:cachix/pre-commit-hooks.nix"; pre-commit-hooks-nix.inputs.nixpkgs.follows = ""; - hercules-ci-effects.url = "github:hercules-ci/hercules-ci-effects"; + hercules-ci-effects.url = "github:hercules-ci/hercules-ci-effects/cargo-publish-module"; treefmt-nix.url = "github:numtide/treefmt-nix"; treefmt-nix.inputs.nixpkgs.follows = ""; }; From c01638c2462bcbcd27aae6b08cd7c48b1e1d8e19 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 12 Jan 2026 23:47:15 +0100 Subject: [PATCH 230/306] dev: Configure cargo-publish --- dev/flake-module.nix | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dev/flake-module.nix b/dev/flake-module.nix index ff31f41..0cbaa8f 100644 --- a/dev/flake-module.nix +++ b/dev/flake-module.nix @@ -122,5 +122,10 @@ "dev" = { }; }; }; + hercules-ci.cargo-publish = { + enable = true; + secretName = "crates-io"; + assertVersions = true; + }; flake = { }; } From 780e0af09cc4a408b4bd07a2f17837b10cf9c0ec Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 12 Jan 2026 23:56:14 +0100 Subject: [PATCH 231/306] dev/flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'hercules-ci-effects': 'github:hercules-ci/hercules-ci-effects/edcbb19948b6caf1700434e369fde6ff9e6a3c93?narHash=sha256-UQhfCggNGDc7eam%2BEittlYmeW89CZVT1KkFIHZWBH7k%3D' (2025-12-15) → 'github:hercules-ci/hercules-ci-effects/bf9e4201dc26334bcb15040c7b932e3cc3f55bdc?narHash=sha256-oXCmakeIrpwpkwovkJDG7c1TV88qh0MHGiEJCX81/Gs%3D' (2026-01-12) • Updated input 'hercules-ci-effects/flake-parts': 'github:hercules-ci/flake-parts/5635c32d666a59ec9a55cab87e898889869f7b71?narHash=sha256-MhA7wmo/7uogLxiewwRRmIax70g6q1U/YemqTGoFHlM%3D' (2025-12-11) → 'github:hercules-ci/flake-parts/52a2caecc898d0b46b2b905f058ccc5081f842da?narHash=sha256-8oNVE8TrD19ulHinjaqONf9QWCKK%2Bw4url56cdStMpM%3D' (2025-11-12) • Updated input 'hercules-ci-effects/nixpkgs': 'github:NixOS/nixpkgs/2fbfb1d73d239d2402a8fe03963e37aab15abe8b?narHash=sha256-9VvC20PJPsleGMewwcWYKGzDIyjckEz8uWmT0vCDYK0%3D' (2025-12-11) → 'github:NixOS/nixpkgs/c5ae371f1a6a7fd27823bc500d9390b38c05fa55?narHash=sha256-4PqRErxfe%2B2toFJFgcRKZ0UI9NSIOJa%2B7RXVtBhy4KE%3D' (2025-11-12) --- dev/flake.lock | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/dev/flake.lock b/dev/flake.lock index b1ca88e..4ea20bd 100644 --- a/dev/flake.lock +++ b/dev/flake.lock @@ -24,11 +24,11 @@ ] }, "locked": { - "lastModified": 1765495779, - "narHash": "sha256-MhA7wmo/7uogLxiewwRRmIax70g6q1U/YemqTGoFHlM=", + "lastModified": 1762980239, + "narHash": "sha256-8oNVE8TrD19ulHinjaqONf9QWCKK+w4url56cdStMpM=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "5635c32d666a59ec9a55cab87e898889869f7b71", + "rev": "52a2caecc898d0b46b2b905f058ccc5081f842da", "type": "github" }, "original": { @@ -63,26 +63,27 @@ "nixpkgs": "nixpkgs" }, "locked": { - "lastModified": 1765774562, - "narHash": "sha256-UQhfCggNGDc7eam+EittlYmeW89CZVT1KkFIHZWBH7k=", + "lastModified": 1768258474, + "narHash": "sha256-oXCmakeIrpwpkwovkJDG7c1TV88qh0MHGiEJCX81/Gs=", "owner": "hercules-ci", "repo": "hercules-ci-effects", - "rev": "edcbb19948b6caf1700434e369fde6ff9e6a3c93", + "rev": "bf9e4201dc26334bcb15040c7b932e3cc3f55bdc", "type": "github" }, "original": { "owner": "hercules-ci", + "ref": "cargo-publish-module", "repo": "hercules-ci-effects", "type": "github" } }, "nixpkgs": { "locked": { - "lastModified": 1765472234, - "narHash": "sha256-9VvC20PJPsleGMewwcWYKGzDIyjckEz8uWmT0vCDYK0=", + "lastModified": 1762977756, + "narHash": "sha256-4PqRErxfe+2toFJFgcRKZ0UI9NSIOJa+7RXVtBhy4KE=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "2fbfb1d73d239d2402a8fe03963e37aab15abe8b", + "rev": "c5ae371f1a6a7fd27823bc500d9390b38c05fa55", "type": "github" }, "original": { From 956e729e90afd3ba9efb6d238c363c6800e2c483 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 13 Jan 2026 01:21:28 +0100 Subject: [PATCH 232/306] dev/flake.nix: Switch to merged hercules-ci-effects --- dev/flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/flake.nix b/dev/flake.nix index 4668d46..09a06a1 100644 --- a/dev/flake.nix +++ b/dev/flake.nix @@ -3,7 +3,7 @@ inputs = { pre-commit-hooks-nix.url = "github:cachix/pre-commit-hooks.nix"; pre-commit-hooks-nix.inputs.nixpkgs.follows = ""; - hercules-ci-effects.url = "github:hercules-ci/hercules-ci-effects/cargo-publish-module"; + hercules-ci-effects.url = "github:hercules-ci/hercules-ci-effects"; treefmt-nix.url = "github:numtide/treefmt-nix"; treefmt-nix.inputs.nixpkgs.follows = ""; }; From 436789bfe1fcbd5c575dc6651347d351b0b2f7e3 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 13 Jan 2026 01:21:32 +0100 Subject: [PATCH 233/306] dev/flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'hercules-ci-effects': 'github:hercules-ci/hercules-ci-effects/bf9e4201dc26334bcb15040c7b932e3cc3f55bdc?narHash=sha256-oXCmakeIrpwpkwovkJDG7c1TV88qh0MHGiEJCX81/Gs%3D' (2026-01-12) → 'github:hercules-ci/hercules-ci-effects/796ba31ee88bcec5c3cbc80ee34c5e157705aab5?narHash=sha256-0bWm54W2kkhrLdvVboT2KVxBliEkc2sNf%2BINaDhvEDU%3D' (2026-01-13) • Updated input 'hercules-ci-effects/flake-parts': 'github:hercules-ci/flake-parts/52a2caecc898d0b46b2b905f058ccc5081f842da?narHash=sha256-8oNVE8TrD19ulHinjaqONf9QWCKK%2Bw4url56cdStMpM%3D' (2025-11-12) → 'github:hercules-ci/flake-parts/5635c32d666a59ec9a55cab87e898889869f7b71?narHash=sha256-MhA7wmo/7uogLxiewwRRmIax70g6q1U/YemqTGoFHlM%3D' (2025-12-11) • Updated input 'hercules-ci-effects/nixpkgs': 'github:NixOS/nixpkgs/c5ae371f1a6a7fd27823bc500d9390b38c05fa55?narHash=sha256-4PqRErxfe%2B2toFJFgcRKZ0UI9NSIOJa%2B7RXVtBhy4KE%3D' (2025-11-12) → 'github:NixOS/nixpkgs/2fbfb1d73d239d2402a8fe03963e37aab15abe8b?narHash=sha256-9VvC20PJPsleGMewwcWYKGzDIyjckEz8uWmT0vCDYK0%3D' (2025-12-11) --- dev/flake.lock | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/dev/flake.lock b/dev/flake.lock index 4ea20bd..4020cf4 100644 --- a/dev/flake.lock +++ b/dev/flake.lock @@ -24,11 +24,11 @@ ] }, "locked": { - "lastModified": 1762980239, - "narHash": "sha256-8oNVE8TrD19ulHinjaqONf9QWCKK+w4url56cdStMpM=", + "lastModified": 1765495779, + "narHash": "sha256-MhA7wmo/7uogLxiewwRRmIax70g6q1U/YemqTGoFHlM=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "52a2caecc898d0b46b2b905f058ccc5081f842da", + "rev": "5635c32d666a59ec9a55cab87e898889869f7b71", "type": "github" }, "original": { @@ -63,27 +63,26 @@ "nixpkgs": "nixpkgs" }, "locked": { - "lastModified": 1768258474, - "narHash": "sha256-oXCmakeIrpwpkwovkJDG7c1TV88qh0MHGiEJCX81/Gs=", + "lastModified": 1768263332, + "narHash": "sha256-0bWm54W2kkhrLdvVboT2KVxBliEkc2sNf+INaDhvEDU=", "owner": "hercules-ci", "repo": "hercules-ci-effects", - "rev": "bf9e4201dc26334bcb15040c7b932e3cc3f55bdc", + "rev": "796ba31ee88bcec5c3cbc80ee34c5e157705aab5", "type": "github" }, "original": { "owner": "hercules-ci", - "ref": "cargo-publish-module", "repo": "hercules-ci-effects", "type": "github" } }, "nixpkgs": { "locked": { - "lastModified": 1762977756, - "narHash": "sha256-4PqRErxfe+2toFJFgcRKZ0UI9NSIOJa+7RXVtBhy4KE=", + "lastModified": 1765472234, + "narHash": "sha256-9VvC20PJPsleGMewwcWYKGzDIyjckEz8uWmT0vCDYK0=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "c5ae371f1a6a7fd27823bc500d9390b38c05fa55", + "rev": "2fbfb1d73d239d2402a8fe03963e37aab15abe8b", "type": "github" }, "original": { From a733bf75c85dd87e6c4e3d63bac99ebb95535224 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 13 Jan 2026 01:21:57 +0100 Subject: [PATCH 234/306] Document release process --- doc/maintainers/release.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 doc/maintainers/release.md diff --git a/doc/maintainers/release.md b/doc/maintainers/release.md new file mode 100644 index 0000000..59056dd --- /dev/null +++ b/doc/maintainers/release.md @@ -0,0 +1,9 @@ + +# Release process + +This project uses simple tags, that trigger a release of all crates using Hercules CI. +See [HCI Effects cargo publish workflow]. + +Dissatisfied with the coarse grained release process? Complain to @roberth and he'll get it done for you. + +[HCI Effects cargo publish workflow]: https://docs.hercules-ci.com/hercules-ci-effects/reference/flake-parts/cargo-publish/#_releasing_a_version From 2b3ce74ef32e9dc4d94b65dc16e4fe625db010b7 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 13 Jan 2026 01:25:43 +0100 Subject: [PATCH 235/306] Bump version to 0.1.1 --- Cargo.lock | 12 ++++++------ nix-bindings-expr/Cargo.toml | 8 ++++---- nix-bindings-fetchers/Cargo.toml | 8 ++++---- nix-bindings-flake/Cargo.toml | 12 ++++++------ nix-bindings-store/Cargo.toml | 8 ++++---- nix-bindings-util-sys/Cargo.toml | 2 +- nix-bindings-util/Cargo.toml | 4 ++-- 7 files changed, 27 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e4a8151..146ff70 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -210,7 +210,7 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "nix-bindings-expr" -version = "0.1.0" +version = "0.1.1" dependencies = [ "anyhow", "cstr", @@ -224,7 +224,7 @@ dependencies = [ [[package]] name = "nix-bindings-fetchers" -version = "0.1.0" +version = "0.1.1" dependencies = [ "anyhow", "cstr", @@ -237,7 +237,7 @@ dependencies = [ [[package]] name = "nix-bindings-flake" -version = "0.1.0" +version = "0.1.1" dependencies = [ "anyhow", "cstr", @@ -253,7 +253,7 @@ dependencies = [ [[package]] name = "nix-bindings-store" -version = "0.1.0" +version = "0.1.1" dependencies = [ "anyhow", "ctor", @@ -266,7 +266,7 @@ dependencies = [ [[package]] name = "nix-bindings-util" -version = "0.1.0" +version = "0.1.1" dependencies = [ "anyhow", "ctor", @@ -275,7 +275,7 @@ dependencies = [ [[package]] name = "nix-bindings-util-sys" -version = "0.1.0" +version = "0.1.1" dependencies = [ "bindgen", "pkg-config", diff --git a/nix-bindings-expr/Cargo.toml b/nix-bindings-expr/Cargo.toml index 41b3cc7..4daee1c 100644 --- a/nix-bindings-expr/Cargo.toml +++ b/nix-bindings-expr/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nix-bindings-expr" -version = "0.1.0" +version = "0.1.1" edition = "2021" license = "LGPL-2.1" description = "Rust bindings to Nix expression evaluator" @@ -11,9 +11,9 @@ path = "src/lib.rs" [dependencies] anyhow = "1.0" -nix-bindings-store = { path = "../nix-bindings-store", version = "0.1.0" } -nix-bindings-util = { path = "../nix-bindings-util", version = "0.1.0" } -nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.1.0" } +nix-bindings-store = { path = "../nix-bindings-store", version = "0.1.1" } +nix-bindings-util = { path = "../nix-bindings-util", version = "0.1.1" } +nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.1.1" } lazy_static = "1.4" ctor = "0.2" tempfile = "3.10" diff --git a/nix-bindings-fetchers/Cargo.toml b/nix-bindings-fetchers/Cargo.toml index db0c130..e816590 100644 --- a/nix-bindings-fetchers/Cargo.toml +++ b/nix-bindings-fetchers/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nix-bindings-fetchers" -version = "0.1.0" +version = "0.1.1" edition = "2021" license = "LGPL-2.1" description = "Rust bindings to Nix fetchers" @@ -11,9 +11,9 @@ path = "src/lib.rs" [dependencies] anyhow = "1.0" -nix-bindings-store = { path = "../nix-bindings-store", version = "0.1.0" } -nix-bindings-util = { path = "../nix-bindings-util", version = "0.1.0" } -nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.1.0" } +nix-bindings-store = { path = "../nix-bindings-store", version = "0.1.1" } +nix-bindings-util = { path = "../nix-bindings-util", version = "0.1.1" } +nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.1.1" } ctor = "0.2" tempfile = "3.10" cstr = "0.2" diff --git a/nix-bindings-flake/Cargo.toml b/nix-bindings-flake/Cargo.toml index 70fd26e..7a76906 100644 --- a/nix-bindings-flake/Cargo.toml +++ b/nix-bindings-flake/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nix-bindings-flake" -version = "0.1.0" +version = "0.1.1" edition = "2021" license = "LGPL-2.1" description = "Rust bindings to Nix flakes" @@ -11,11 +11,11 @@ path = "src/lib.rs" [dependencies] anyhow = "1.0" -nix-bindings-expr = { path = "../nix-bindings-expr", version = "0.1.0" } -nix-bindings-fetchers = { path = "../nix-bindings-fetchers", version = "0.1.0" } -nix-bindings-store = { path = "../nix-bindings-store", version = "0.1.0" } -nix-bindings-util = { path = "../nix-bindings-util", version = "0.1.0" } -nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.1.0" } +nix-bindings-expr = { path = "../nix-bindings-expr", version = "0.1.1" } +nix-bindings-fetchers = { path = "../nix-bindings-fetchers", version = "0.1.1" } +nix-bindings-store = { path = "../nix-bindings-store", version = "0.1.1" } +nix-bindings-util = { path = "../nix-bindings-util", version = "0.1.1" } +nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.1.1" } lazy_static = "1.4" ctor = "0.2" tempfile = "3.10" diff --git a/nix-bindings-store/Cargo.toml b/nix-bindings-store/Cargo.toml index d34afac..2754072 100644 --- a/nix-bindings-store/Cargo.toml +++ b/nix-bindings-store/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nix-bindings-store" -version = "0.1.0" +version = "0.1.1" edition = "2021" build = "build.rs" license = "LGPL-2.1" @@ -12,8 +12,8 @@ path = "src/lib.rs" [dependencies] anyhow = "1.0" -nix-bindings-util = { path = "../nix-bindings-util", version = "0.1.0" } -nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.1.0" } +nix-bindings-util = { path = "../nix-bindings-util", version = "0.1.1" } +nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.1.1" } lazy_static = "1.4" [dev-dependencies] @@ -23,7 +23,7 @@ tempfile = "3.10" [build-dependencies] pkg-config = "0.3" # Needed for version parsing in build.rs -nix-bindings-util = { path = "../nix-bindings-util", version = "0.1.0" } +nix-bindings-util = { path = "../nix-bindings-util", version = "0.1.1" } [lints.rust] warnings = "deny" diff --git a/nix-bindings-util-sys/Cargo.toml b/nix-bindings-util-sys/Cargo.toml index 691da18..f450317 100644 --- a/nix-bindings-util-sys/Cargo.toml +++ b/nix-bindings-util-sys/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nix-bindings-util-sys" -version = "0.1.0" +version = "0.1.1" edition = "2021" build = "build.rs" license = "LGPL-2.1" diff --git a/nix-bindings-util/Cargo.toml b/nix-bindings-util/Cargo.toml index 3af13f1..fcf1aba 100644 --- a/nix-bindings-util/Cargo.toml +++ b/nix-bindings-util/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nix-bindings-util" -version = "0.1.0" +version = "0.1.1" edition = "2021" license = "LGPL-2.1" description = "Rust bindings to Nix utility library" @@ -11,7 +11,7 @@ path = "src/lib.rs" [dependencies] anyhow = "1.0" -nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.1.0" } +nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.1.1" } ctor = "0.2" [lints.rust] From dbb00333b1acd74900e5ae94b2220db4ff2a9eb9 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 6 Dec 2025 15:44:20 -0500 Subject: [PATCH 236/306] Split monolithic raw crates into sys crates Creating a crate for bwd-gc highlights the fact that it would be nice to fix 2! The file blocklist is a lost less unmaintainable then the more fine-grained one we had before. Fix #9 --- Cargo.lock | 69 ++++++++++++++++--- Cargo.toml | 5 ++ flake.nix | 41 +++++------ nci.nix | 52 +++++++++++++- nix-bindings-bdwgc-sys/Cargo.toml | 15 ++++ nix-bindings-bdwgc-sys/README.md | 4 ++ nix-bindings-bdwgc-sys/build.rs | 27 ++++++++ nix-bindings-bdwgc-sys/include/bdwgc.h | 2 + nix-bindings-bdwgc-sys/src/lib.rs | 7 ++ nix-bindings-expr-sys/Cargo.toml | 17 +++++ nix-bindings-expr-sys/README.md | 5 ++ nix-bindings-expr-sys/build.rs | 42 +++++++++++ nix-bindings-expr-sys/include/nix-c-expr.h | 2 + nix-bindings-expr-sys/src/lib.rs | 10 +++ nix-bindings-expr/Cargo.toml | 11 +-- nix-bindings-expr/src/eval_state.rs | 20 +++--- nix-bindings-expr/src/primop.rs | 7 +- nix-bindings-expr/src/value.rs | 2 +- nix-bindings-expr/src/value/__private.rs | 2 +- nix-bindings-fetchers-sys/Cargo.toml | 16 +++++ nix-bindings-fetchers-sys/README.md | 5 ++ nix-bindings-fetchers-sys/build.rs | 40 +++++++++++ .../include/nix-c-fetchers.h | 1 + nix-bindings-fetchers-sys/src/lib.rs | 9 +++ nix-bindings-fetchers/Cargo.toml | 8 +-- nix-bindings-fetchers/src/lib.rs | 2 +- nix-bindings-flake-sys/Cargo.toml | 20 ++++++ nix-bindings-flake-sys/README.md | 5 ++ nix-bindings-flake-sys/build.rs | 56 +++++++++++++++ nix-bindings-flake-sys/include/nix-c-flake.h | 3 + nix-bindings-flake-sys/src/lib.rs | 11 +++ nix-bindings-flake/Cargo.toml | 12 ++-- nix-bindings-flake/src/lib.rs | 2 +- nix-bindings-store-sys/Cargo.toml | 16 +++++ nix-bindings-store-sys/README.md | 5 ++ nix-bindings-store-sys/build.rs | 40 +++++++++++ nix-bindings-store-sys/include/nix-c-store.h | 1 + nix-bindings-store-sys/src/lib.rs | 9 +++ nix-bindings-store/Cargo.toml | 9 +-- nix-bindings-store/src/derivation.rs | 2 +- nix-bindings-store/src/path.rs | 2 +- nix-bindings-store/src/store.rs | 5 +- nix-bindings-util-sys/Cargo.toml | 4 +- nix-bindings-util-sys/README.md | 11 +-- nix-bindings-util-sys/build.rs | 16 ++--- nix-bindings-util-sys/include/nix-c-raw.h | 7 -- nix-bindings-util-sys/include/nix-c-util.h | 1 + nix-bindings-util/Cargo.toml | 6 +- nix-bindings-util/src/context.rs | 2 +- nix-bindings-util/src/lib.rs | 3 + nix-bindings-util/src/settings.rs | 6 +- 51 files changed, 571 insertions(+), 104 deletions(-) create mode 100644 nix-bindings-bdwgc-sys/Cargo.toml create mode 100644 nix-bindings-bdwgc-sys/README.md create mode 100644 nix-bindings-bdwgc-sys/build.rs create mode 100644 nix-bindings-bdwgc-sys/include/bdwgc.h create mode 100644 nix-bindings-bdwgc-sys/src/lib.rs create mode 100644 nix-bindings-expr-sys/Cargo.toml create mode 100644 nix-bindings-expr-sys/README.md create mode 100644 nix-bindings-expr-sys/build.rs create mode 100644 nix-bindings-expr-sys/include/nix-c-expr.h create mode 100644 nix-bindings-expr-sys/src/lib.rs create mode 100644 nix-bindings-fetchers-sys/Cargo.toml create mode 100644 nix-bindings-fetchers-sys/README.md create mode 100644 nix-bindings-fetchers-sys/build.rs create mode 100644 nix-bindings-fetchers-sys/include/nix-c-fetchers.h create mode 100644 nix-bindings-fetchers-sys/src/lib.rs create mode 100644 nix-bindings-flake-sys/Cargo.toml create mode 100644 nix-bindings-flake-sys/README.md create mode 100644 nix-bindings-flake-sys/build.rs create mode 100644 nix-bindings-flake-sys/include/nix-c-flake.h create mode 100644 nix-bindings-flake-sys/src/lib.rs create mode 100644 nix-bindings-store-sys/Cargo.toml create mode 100644 nix-bindings-store-sys/README.md create mode 100644 nix-bindings-store-sys/build.rs create mode 100644 nix-bindings-store-sys/include/nix-c-store.h create mode 100644 nix-bindings-store-sys/src/lib.rs delete mode 100644 nix-bindings-util-sys/include/nix-c-raw.h create mode 100644 nix-bindings-util-sys/include/nix-c-util.h diff --git a/Cargo.lock b/Cargo.lock index 146ff70..5eb39f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -208,36 +208,66 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "nix-bindings-bdwgc-sys" +version = "0.2.0" +dependencies = [ + "bindgen", + "pkg-config", +] + [[package]] name = "nix-bindings-expr" -version = "0.1.1" +version = "0.2.0" dependencies = [ "anyhow", "cstr", "ctor", "lazy_static", + "nix-bindings-bdwgc-sys", + "nix-bindings-expr-sys", "nix-bindings-store", + "nix-bindings-store-sys", "nix-bindings-util", "nix-bindings-util-sys", "tempfile", ] +[[package]] +name = "nix-bindings-expr-sys" +version = "0.2.0" +dependencies = [ + "bindgen", + "nix-bindings-store-sys", + "nix-bindings-util-sys", + "pkg-config", +] + [[package]] name = "nix-bindings-fetchers" -version = "0.1.1" +version = "0.2.0" dependencies = [ "anyhow", "cstr", "ctor", + "nix-bindings-fetchers-sys", "nix-bindings-store", "nix-bindings-util", - "nix-bindings-util-sys", "tempfile", ] +[[package]] +name = "nix-bindings-fetchers-sys" +version = "0.2.0" +dependencies = [ + "bindgen", + "nix-bindings-util-sys", + "pkg-config", +] + [[package]] name = "nix-bindings-flake" -version = "0.1.1" +version = "0.2.0" dependencies = [ "anyhow", "cstr", @@ -245,28 +275,51 @@ dependencies = [ "lazy_static", "nix-bindings-expr", "nix-bindings-fetchers", + "nix-bindings-flake-sys", "nix-bindings-store", "nix-bindings-util", - "nix-bindings-util-sys", "tempfile", ] +[[package]] +name = "nix-bindings-flake-sys" +version = "0.2.0" +dependencies = [ + "bindgen", + "nix-bindings-bdwgc-sys", + "nix-bindings-expr-sys", + "nix-bindings-fetchers-sys", + "nix-bindings-store-sys", + "nix-bindings-util-sys", + "pkg-config", +] + [[package]] name = "nix-bindings-store" -version = "0.1.1" +version = "0.2.0" dependencies = [ "anyhow", "ctor", "lazy_static", + "nix-bindings-store-sys", "nix-bindings-util", "nix-bindings-util-sys", "pkg-config", "tempfile", ] +[[package]] +name = "nix-bindings-store-sys" +version = "0.2.0" +dependencies = [ + "bindgen", + "nix-bindings-util-sys", + "pkg-config", +] + [[package]] name = "nix-bindings-util" -version = "0.1.1" +version = "0.2.0" dependencies = [ "anyhow", "ctor", @@ -275,7 +328,7 @@ dependencies = [ [[package]] name = "nix-bindings-util-sys" -version = "0.1.1" +version = "0.2.0" dependencies = [ "bindgen", "pkg-config", diff --git a/Cargo.toml b/Cargo.toml index 7cfd424..67da147 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,11 @@ [workspace] members = [ + "nix-bindings-bdwgc-sys", "nix-bindings-util-sys", + "nix-bindings-store-sys", + "nix-bindings-expr-sys", + "nix-bindings-fetchers-sys", + "nix-bindings-flake-sys", "nix-bindings-expr", "nix-bindings-fetchers", "nix-bindings-flake", diff --git a/flake.nix b/flake.nix index f9896ad..ddd12be 100644 --- a/flake.nix +++ b/flake.nix @@ -29,10 +29,7 @@ { _file = ./flake.nix; options.perSystem = flake-parts-lib.mkPerSystemOption ( - { config, pkgs, ... }: - let - cfg = config.nix-bindings-rust; - in + { pkgs, ... }: { options.nix-bindings-rust = { nixPackage = lib.mkOption { @@ -49,10 +46,25 @@ A module to load into your nix-cargo-integration [`perSystem.nci.projects..depsDrvConfig`](https://flake.parts/options/nix-cargo-integration.html#opt-perSystem.nci.projects._name_.depsDrvConfig) or similar such options. + This provides common build configuration (pkg-config, libclang, etc.) but you must + add the specific Nix C libraries your crates need to `buildInputs`: + - `nix-bindings-util-sys` needs `nix-util-c` + - `nix-bindings-store-sys` needs `nix-store-c` + - `nix-bindings-expr-sys` needs `nix-expr-c` + - `nix-bindings-fetchers-sys` needs `nix-fetchers-c` (Nix >= 2.29) + - `nix-bindings-flake-sys` needs `nix-flake-c` + - `nix-bindings-bdwgc-sys` needs `boehmgc` + Example: ```nix - perSystem = perSystem@{ config, ... }: { - nci.projects."my_project".depsDrvConfig = perSystem.config.nix-bindings-rust.nciBuildConfig; + perSystem = perSystem@{ config, pkgs, ... }: { + nci.projects."my_project".depsDrvConfig = { + imports = [ perSystem.config.nix-bindings-rust.nciBuildConfig ]; + mkDerivation.buildInputs = [ + perSystem.config.nix-bindings-rust.nixPackage.libs.nix-store-c + # ... add other libs as needed + ]; + }; } ``` ''; @@ -64,22 +76,7 @@ buildInputs = [ # stdbool.h pkgs.stdenv.cc - ] - ++ ( - if cfg.nixPackage ? libs then - let - l = cfg.nixPackage.libs; - in - [ - l.nix-expr-c - l.nix-store-c - l.nix-util-c - l.nix-fetchers-c or null # Nix >= 2.29 - l.nix-flake-c - ] - else - [ cfg.nixPackage ] - ); + ]; nativeBuildInputs = [ pkgs.pkg-config ]; diff --git a/nci.nix b/nci.nix index cc864b0..9a468b6 100644 --- a/nci.nix +++ b/nci.nix @@ -2,8 +2,24 @@ perSystem = { config, + pkgs, ... }: + let + cfg = config.nix-bindings-rust; + nixLibs = + if cfg.nixPackage ? libs then + cfg.nixPackage.libs + else + # Fallback for older Nix versions without split libs + { + nix-util-c = cfg.nixPackage; + nix-store-c = cfg.nixPackage; + nix-expr-c = cfg.nixPackage; + nix-fetchers-c = cfg.nixPackage; + nix-flake-c = cfg.nixPackage; + }; + in { # https://flake.parts/options/nix-cargo-integration nci.projects.nix-bindings = { @@ -42,12 +58,46 @@ echo "experimental-features = ca-derivations flakes" > "$NIX_CONF_DIR/nix.conf" # Init ahead of time, because concurrent initialization is flaky - ${config.nix-bindings-rust.nixPackage}/bin/nix-store --init + ${cfg.nixPackage}/bin/nix-store --init echo "Store initialized." ''; }; }; }; + + # Per-crate configuration: only provide the specific Nix libs each crate needs + # FIXME should use propagatedBuildInputs + nci.crates.nix-bindings-bdwgc-sys.drvConfig.mkDerivation.buildInputs = [ + pkgs.boehmgc + ]; + nci.crates.nix-bindings-util-sys.drvConfig.mkDerivation.buildInputs = [ + nixLibs.nix-util-c + ]; + nci.crates.nix-bindings-util.drvConfig.mkDerivation.buildInputs = + config.nci.crates.nix-bindings-util-sys.drvConfig.mkDerivation.buildInputs; + nci.crates.nix-bindings-store-sys.drvConfig.mkDerivation.buildInputs = + config.nci.crates.nix-bindings-util-sys.drvConfig.mkDerivation.buildInputs + ++ [ nixLibs.nix-store-c ]; + nci.crates.nix-bindings-store.drvConfig.mkDerivation.buildInputs = + config.nci.crates.nix-bindings-store-sys.drvConfig.mkDerivation.buildInputs; + nci.crates.nix-bindings-expr-sys.drvConfig.mkDerivation.buildInputs = + config.nci.crates.nix-bindings-store-sys.drvConfig.mkDerivation.buildInputs + ++ [ + nixLibs.nix-expr-c + pkgs.boehmgc + ]; + nci.crates.nix-bindings-expr.drvConfig.mkDerivation.buildInputs = + config.nci.crates.nix-bindings-expr-sys.drvConfig.mkDerivation.buildInputs; + nci.crates.nix-bindings-fetchers-sys.drvConfig.mkDerivation.buildInputs = + config.nci.crates.nix-bindings-expr-sys.drvConfig.mkDerivation.buildInputs + ++ [ nixLibs.nix-fetchers-c ]; + nci.crates.nix-bindings-fetchers.drvConfig.mkDerivation.buildInputs = + config.nci.crates.nix-bindings-fetchers-sys.drvConfig.mkDerivation.buildInputs; + nci.crates.nix-bindings-flake-sys.drvConfig.mkDerivation.buildInputs = + config.nci.crates.nix-bindings-fetchers-sys.drvConfig.mkDerivation.buildInputs + ++ [ nixLibs.nix-flake-c ]; + nci.crates.nix-bindings-flake.drvConfig.mkDerivation.buildInputs = + config.nci.crates.nix-bindings-flake-sys.drvConfig.mkDerivation.buildInputs; }; } diff --git a/nix-bindings-bdwgc-sys/Cargo.toml b/nix-bindings-bdwgc-sys/Cargo.toml new file mode 100644 index 0000000..bf75ca7 --- /dev/null +++ b/nix-bindings-bdwgc-sys/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "nix-bindings-bdwgc-sys" +version = "0.2.0" +edition = "2021" +build = "build.rs" +license = "LGPL-2.1" + +[lib] +path = "src/lib.rs" + +[dependencies] + +[build-dependencies] +bindgen = "0.69" +pkg-config = "0.3" diff --git a/nix-bindings-bdwgc-sys/README.md b/nix-bindings-bdwgc-sys/README.md new file mode 100644 index 0000000..3723e90 --- /dev/null +++ b/nix-bindings-bdwgc-sys/README.md @@ -0,0 +1,4 @@ +# nix-bindings-bdwgc-sys + +This crate contains generated bindings for the Boehm-Demers-Weiser garbage collector (`bdw-gc`). +**You should not have to use this crate directly,** and so you should probably not add it to your dependencies. diff --git a/nix-bindings-bdwgc-sys/build.rs b/nix-bindings-bdwgc-sys/build.rs new file mode 100644 index 0000000..aed2a8c --- /dev/null +++ b/nix-bindings-bdwgc-sys/build.rs @@ -0,0 +1,27 @@ +use std::env; +use std::path::PathBuf; + +fn main() { + println!("cargo:rerun-if-changed=include/bdwgc.h"); + + let mut args = Vec::new(); + for path in pkg_config::probe_library("bdw-gc") + .unwrap() + .include_paths + .iter() + { + args.push(format!("-I{}", path.to_str().unwrap())); + } + + let bindings = bindgen::Builder::default() + .header("include/bdwgc.h") + .clang_args(args) + .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) + .generate() + .expect("Unable to generate bindings"); + + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + bindings + .write_to_file(out_path.join("bindings.rs")) + .expect("Couldn't write bindings!"); +} diff --git a/nix-bindings-bdwgc-sys/include/bdwgc.h b/nix-bindings-bdwgc-sys/include/bdwgc.h new file mode 100644 index 0000000..2a70434 --- /dev/null +++ b/nix-bindings-bdwgc-sys/include/bdwgc.h @@ -0,0 +1,2 @@ +#define GC_THREADS +#include diff --git a/nix-bindings-bdwgc-sys/src/lib.rs b/nix-bindings-bdwgc-sys/src/lib.rs new file mode 100644 index 0000000..0cf64d2 --- /dev/null +++ b/nix-bindings-bdwgc-sys/src/lib.rs @@ -0,0 +1,7 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(dead_code)] +#![allow(clippy::all)] + +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); diff --git a/nix-bindings-expr-sys/Cargo.toml b/nix-bindings-expr-sys/Cargo.toml new file mode 100644 index 0000000..39518fc --- /dev/null +++ b/nix-bindings-expr-sys/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "nix-bindings-expr-sys" +version = "0.2.0" +edition = "2021" +build = "build.rs" +license = "LGPL-2.1" + +[lib] +path = "src/lib.rs" + +[dependencies] +nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.2.0" } +nix-bindings-store-sys = { path = "../nix-bindings-store-sys", version = "0.2.0" } + +[build-dependencies] +bindgen = "0.69" +pkg-config = "0.3" diff --git a/nix-bindings-expr-sys/README.md b/nix-bindings-expr-sys/README.md new file mode 100644 index 0000000..ccdcc26 --- /dev/null +++ b/nix-bindings-expr-sys/README.md @@ -0,0 +1,5 @@ +# nix-bindings-expr-sys + +This crate contains generated bindings for the Nix C API (`nix-expr-c`). +**You should not have to use this crate directly,** and so you should probably not add it to your dependencies. +Instead, use the `nix-bindings-expr` crate, which _should_ be sufficient. diff --git a/nix-bindings-expr-sys/build.rs b/nix-bindings-expr-sys/build.rs new file mode 100644 index 0000000..6e0eed2 --- /dev/null +++ b/nix-bindings-expr-sys/build.rs @@ -0,0 +1,42 @@ +use std::path::PathBuf; + +#[derive(Debug)] +struct StripNixPrefix; + +impl bindgen::callbacks::ParseCallbacks for StripNixPrefix { + fn item_name(&self, name: &str) -> Option { + name.strip_prefix("nix_").map(String::from) + } +} + +fn main() { + println!("cargo:rerun-if-changed=include/nix-c-expr.h"); + println!("cargo:rustc-link-lib=nixexprc"); + + let mut args = Vec::new(); + for path in pkg_config::probe_library("nix-expr-c") + .unwrap() + .include_paths + .iter() + { + args.push(format!("-I{}", path.to_str().unwrap())); + } + + let out_path = PathBuf::from(std::env::var("OUT_DIR").unwrap()); + + let bindings = bindgen::Builder::default() + .header("include/nix-c-expr.h") + .clang_args(args) + .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) + .parse_callbacks(Box::new(StripNixPrefix)) + // Blocklist symbols from nix-bindings-util-sys + .blocklist_file(".*nix_api_util\\.h") + // Blocklist symbols from nix-bindings-store-sys + .blocklist_file(".*nix_api_store\\.h") + .generate() + .expect("Unable to generate bindings"); + + bindings + .write_to_file(out_path.join("bindings.rs")) + .expect("Couldn't write bindings!"); +} diff --git a/nix-bindings-expr-sys/include/nix-c-expr.h b/nix-bindings-expr-sys/include/nix-c-expr.h new file mode 100644 index 0000000..c2649a4 --- /dev/null +++ b/nix-bindings-expr-sys/include/nix-c-expr.h @@ -0,0 +1,2 @@ +#include +#include diff --git a/nix-bindings-expr-sys/src/lib.rs b/nix-bindings-expr-sys/src/lib.rs new file mode 100644 index 0000000..afd13d3 --- /dev/null +++ b/nix-bindings-expr-sys/src/lib.rs @@ -0,0 +1,10 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(dead_code)] +#![allow(clippy::all)] + +use nix_bindings_store_sys::*; +use nix_bindings_util_sys::*; + +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); diff --git a/nix-bindings-expr/Cargo.toml b/nix-bindings-expr/Cargo.toml index 4daee1c..a231e3c 100644 --- a/nix-bindings-expr/Cargo.toml +++ b/nix-bindings-expr/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nix-bindings-expr" -version = "0.1.1" +version = "0.2.0" edition = "2021" license = "LGPL-2.1" description = "Rust bindings to Nix expression evaluator" @@ -11,9 +11,12 @@ path = "src/lib.rs" [dependencies] anyhow = "1.0" -nix-bindings-store = { path = "../nix-bindings-store", version = "0.1.1" } -nix-bindings-util = { path = "../nix-bindings-util", version = "0.1.1" } -nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.1.1" } +nix-bindings-store = { path = "../nix-bindings-store", version = "0.2.0" } +nix-bindings-util = { path = "../nix-bindings-util", version = "0.2.0" } +nix-bindings-bdwgc-sys = { path = "../nix-bindings-bdwgc-sys", version = "0.2.0" } +nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.2.0" } +nix-bindings-store-sys = { path = "../nix-bindings-store-sys", version = "0.2.0" } +nix-bindings-expr-sys = { path = "../nix-bindings-expr-sys", version = "0.2.0" } lazy_static = "1.4" ctor = "0.2" tempfile = "3.10" diff --git a/nix-bindings-expr/src/eval_state.rs b/nix-bindings-expr/src/eval_state.rs index 44b6945..b34de83 100644 --- a/nix-bindings-expr/src/eval_state.rs +++ b/nix-bindings-expr/src/eval_state.rs @@ -134,14 +134,16 @@ use anyhow::Context as _; use anyhow::{bail, Result}; use cstr::cstr; use lazy_static::lazy_static; +use nix_bindings_bdwgc_sys as gc; +use nix_bindings_expr_sys as raw; use nix_bindings_store::path::StorePath; use nix_bindings_store::store::{Store, StoreWeak}; +use nix_bindings_store_sys as raw_store; use nix_bindings_util::context::Context; use nix_bindings_util::string_return::{ callback_get_result_string, callback_get_result_string_data, }; use nix_bindings_util::{check_call, check_call_opt_key, result_string_init}; -use nix_bindings_util_sys as raw; use std::ffi::{c_char, CString}; use std::iter::FromIterator; use std::os::raw::c_uint; @@ -151,7 +153,7 @@ use std::sync::{Arc, Weak}; lazy_static! { static ref INIT: Result<()> = { unsafe { - raw::GC_allow_register_threads(); + gc::GC_allow_register_threads(); check_call!(raw::libexpr_init(&mut Context::new()))?; Ok(()) } @@ -887,7 +889,7 @@ impl EvalState { let mut paths = Vec::with_capacity(n as usize); for i in 0..n { let path = raw::realised_string_get_store_path(rs, i); - let path = NonNull::new(path as *mut raw::StorePath).ok_or_else(|| { + let path = NonNull::new(path as *mut raw_store::StorePath).ok_or_else(|| { anyhow::format_err!( "nix_realised_string_get_store_path returned a null pointer" ) @@ -1153,7 +1155,7 @@ impl Drop for ThreadRegistrationGuard { fn drop(&mut self) { if self.must_unregister { unsafe { - raw::GC_unregister_my_thread(); + gc::GC_unregister_my_thread(); } } } @@ -1161,14 +1163,14 @@ impl Drop for ThreadRegistrationGuard { fn gc_register_my_thread_do_it() -> Result<()> { unsafe { - let mut sb: raw::GC_stack_base = raw::GC_stack_base { + let mut sb: gc::GC_stack_base = gc::GC_stack_base { mem_base: null_mut(), }; - let r = raw::GC_get_stack_base(&mut sb); - if r as u32 != raw::GC_SUCCESS { + let r = gc::GC_get_stack_base(&mut sb); + if r as u32 != gc::GC_SUCCESS { Err(anyhow::format_err!("GC_get_stack_base failed: {}", r))?; } - raw::GC_register_my_thread(&sb); + gc::GC_register_my_thread(&sb); Ok(()) } } @@ -1179,7 +1181,7 @@ fn gc_register_my_thread_do_it() -> Result<()> { pub fn gc_register_my_thread() -> Result { init()?; unsafe { - let already_done = raw::GC_thread_is_registered(); + let already_done = gc::GC_thread_is_registered(); if already_done != 0 { return Ok(ThreadRegistrationGuard { must_unregister: false, diff --git a/nix-bindings-expr/src/primop.rs b/nix-bindings-expr/src/primop.rs index 7996605..02410fc 100644 --- a/nix-bindings-expr/src/primop.rs +++ b/nix-bindings-expr/src/primop.rs @@ -1,8 +1,9 @@ use crate::eval_state::{EvalState, EvalStateWeak}; use crate::value::Value; use anyhow::Result; +use nix_bindings_expr_sys as raw; use nix_bindings_util::check_call; -use nix_bindings_util_sys as raw; +use nix_bindings_util_sys as raw_util; use std::ffi::{c_int, c_void, CStr, CString}; use std::mem::ManuallyDrop; use std::ptr::{null, null_mut}; @@ -84,7 +85,7 @@ struct PrimOpContext { unsafe extern "C" fn function_adapter( user_data: *mut ::std::os::raw::c_void, - context_out: *mut raw::c_context, + context_out: *mut raw_util::c_context, _state: *mut raw::EvalState, args: *mut *mut raw::Value, ret: *mut raw::Value, @@ -111,7 +112,7 @@ unsafe extern "C" fn function_adapter( CString::new("") .unwrap() }); - raw::set_err_msg(context_out, raw::err_NIX_ERR_UNKNOWN, cstr.as_ptr()); + raw_util::set_err_msg(context_out, raw_util::err_NIX_ERR_UNKNOWN, cstr.as_ptr()); }, } } diff --git a/nix-bindings-expr/src/value.rs b/nix-bindings-expr/src/value.rs index 1d7f866..e75c4df 100644 --- a/nix-bindings-expr/src/value.rs +++ b/nix-bindings-expr/src/value.rs @@ -1,7 +1,7 @@ pub mod __private; +use nix_bindings_expr_sys as raw; use nix_bindings_util::{check_call, context::Context}; -use nix_bindings_util_sys as raw; use std::ptr::{null_mut, NonNull}; // TODO: test: cloning a thunk does not duplicate the evaluation. diff --git a/nix-bindings-expr/src/value/__private.rs b/nix-bindings-expr/src/value/__private.rs index 4d59514..770a217 100644 --- a/nix-bindings-expr/src/value/__private.rs +++ b/nix-bindings-expr/src/value/__private.rs @@ -1,6 +1,6 @@ //! Functions that are relevant for other bindings modules, but normally not end users. use super::Value; -use nix_bindings_util_sys as raw; +use nix_bindings_expr_sys as raw; /// Take ownership of a new [`Value`]. /// diff --git a/nix-bindings-fetchers-sys/Cargo.toml b/nix-bindings-fetchers-sys/Cargo.toml new file mode 100644 index 0000000..6101928 --- /dev/null +++ b/nix-bindings-fetchers-sys/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "nix-bindings-fetchers-sys" +version = "0.2.0" +edition = "2021" +build = "build.rs" +license = "LGPL-2.1" + +[lib] +path = "src/lib.rs" + +[dependencies] +nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.2.0" } + +[build-dependencies] +bindgen = "0.69" +pkg-config = "0.3" diff --git a/nix-bindings-fetchers-sys/README.md b/nix-bindings-fetchers-sys/README.md new file mode 100644 index 0000000..b403eac --- /dev/null +++ b/nix-bindings-fetchers-sys/README.md @@ -0,0 +1,5 @@ +# nix-bindings-fetchers-sys + +This crate contains generated bindings for the Nix C API (`nix-fetchers-c`). +**You should not have to use this crate directly,** and so you should probably not add it to your dependencies. +Instead, use the `nix-bindings-fetchers` crate, which _should_ be sufficient. diff --git a/nix-bindings-fetchers-sys/build.rs b/nix-bindings-fetchers-sys/build.rs new file mode 100644 index 0000000..34f6640 --- /dev/null +++ b/nix-bindings-fetchers-sys/build.rs @@ -0,0 +1,40 @@ +use std::path::PathBuf; + +#[derive(Debug)] +struct StripNixPrefix; + +impl bindgen::callbacks::ParseCallbacks for StripNixPrefix { + fn item_name(&self, name: &str) -> Option { + name.strip_prefix("nix_").map(String::from) + } +} + +fn main() { + println!("cargo:rerun-if-changed=include/nix-c-fetchers.h"); + println!("cargo:rustc-link-lib=nixfetchersc"); + + let mut args = Vec::new(); + for path in pkg_config::probe_library("nix-fetchers-c") + .unwrap() + .include_paths + .iter() + { + args.push(format!("-I{}", path.to_str().unwrap())); + } + + let out_path = PathBuf::from(std::env::var("OUT_DIR").unwrap()); + + let bindings = bindgen::Builder::default() + .header("include/nix-c-fetchers.h") + .clang_args(args) + .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) + .parse_callbacks(Box::new(StripNixPrefix)) + // Blocklist symbols from nix-bindings-util-sys + .blocklist_file(".*nix_api_util\\.h") + .generate() + .expect("Unable to generate bindings"); + + bindings + .write_to_file(out_path.join("bindings.rs")) + .expect("Couldn't write bindings!"); +} diff --git a/nix-bindings-fetchers-sys/include/nix-c-fetchers.h b/nix-bindings-fetchers-sys/include/nix-c-fetchers.h new file mode 100644 index 0000000..2f4c542 --- /dev/null +++ b/nix-bindings-fetchers-sys/include/nix-c-fetchers.h @@ -0,0 +1 @@ +#include diff --git a/nix-bindings-fetchers-sys/src/lib.rs b/nix-bindings-fetchers-sys/src/lib.rs new file mode 100644 index 0000000..4c5c9fe --- /dev/null +++ b/nix-bindings-fetchers-sys/src/lib.rs @@ -0,0 +1,9 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(dead_code)] +#![allow(clippy::all)] + +use nix_bindings_util_sys::*; + +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); diff --git a/nix-bindings-fetchers/Cargo.toml b/nix-bindings-fetchers/Cargo.toml index e816590..cf216a0 100644 --- a/nix-bindings-fetchers/Cargo.toml +++ b/nix-bindings-fetchers/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nix-bindings-fetchers" -version = "0.1.1" +version = "0.2.0" edition = "2021" license = "LGPL-2.1" description = "Rust bindings to Nix fetchers" @@ -11,9 +11,9 @@ path = "src/lib.rs" [dependencies] anyhow = "1.0" -nix-bindings-store = { path = "../nix-bindings-store", version = "0.1.1" } -nix-bindings-util = { path = "../nix-bindings-util", version = "0.1.1" } -nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.1.1" } +nix-bindings-store = { path = "../nix-bindings-store", version = "0.2.0" } +nix-bindings-util = { path = "../nix-bindings-util", version = "0.2.0" } +nix-bindings-fetchers-sys = { path = "../nix-bindings-fetchers-sys", version = "0.2.0" } ctor = "0.2" tempfile = "3.10" cstr = "0.2" diff --git a/nix-bindings-fetchers/src/lib.rs b/nix-bindings-fetchers/src/lib.rs index 2ea0b84..0b7b6cc 100644 --- a/nix-bindings-fetchers/src/lib.rs +++ b/nix-bindings-fetchers/src/lib.rs @@ -1,6 +1,6 @@ use anyhow::{Context as _, Result}; +use nix_bindings_fetchers_sys as raw; use nix_bindings_util::context::{self, Context}; -use nix_bindings_util_sys as raw; use std::ptr::NonNull; pub struct FetchersSettings { diff --git a/nix-bindings-flake-sys/Cargo.toml b/nix-bindings-flake-sys/Cargo.toml new file mode 100644 index 0000000..e53bbe4 --- /dev/null +++ b/nix-bindings-flake-sys/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "nix-bindings-flake-sys" +version = "0.2.0" +edition = "2021" +build = "build.rs" +license = "LGPL-2.1" + +[lib] +path = "src/lib.rs" + +[dependencies] +nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.2.0" } +nix-bindings-store-sys = { path = "../nix-bindings-store-sys", version = "0.2.0" } +nix-bindings-expr-sys = { path = "../nix-bindings-expr-sys", version = "0.2.0" } +nix-bindings-fetchers-sys = { path = "../nix-bindings-fetchers-sys", version = "0.2.0" } +nix-bindings-bdwgc-sys = { path = "../nix-bindings-bdwgc-sys", version = "0.2.0" } + +[build-dependencies] +bindgen = "0.69" +pkg-config = "0.3" diff --git a/nix-bindings-flake-sys/README.md b/nix-bindings-flake-sys/README.md new file mode 100644 index 0000000..10a9d96 --- /dev/null +++ b/nix-bindings-flake-sys/README.md @@ -0,0 +1,5 @@ +# nix-bindings-flake-sys + +This crate contains generated bindings for the Nix C API (`nix-flake-c`). +**You should not have to use this crate directly,** and so you should probably not add it to your dependencies. +Instead, use the `nix-bindings-flake` crate, which _should_ be sufficient. diff --git a/nix-bindings-flake-sys/build.rs b/nix-bindings-flake-sys/build.rs new file mode 100644 index 0000000..1fb3233 --- /dev/null +++ b/nix-bindings-flake-sys/build.rs @@ -0,0 +1,56 @@ +use std::path::PathBuf; + +#[derive(Debug)] +struct StripNixPrefix; + +impl bindgen::callbacks::ParseCallbacks for StripNixPrefix { + fn item_name(&self, name: &str) -> Option { + name.strip_prefix("nix_").map(String::from) + } +} + +fn main() { + println!("cargo:rerun-if-changed=include/nix-c-flake.h"); + println!("cargo:rustc-link-lib=nixflakec"); + + let mut args = Vec::new(); + for path in pkg_config::probe_library("nix-flake-c") + .unwrap() + .include_paths + .iter() + { + args.push(format!("-I{}", path.to_str().unwrap())); + } + for path in pkg_config::probe_library("bdw-gc") + .unwrap() + .include_paths + .iter() + { + args.push(format!("-I{}", path.to_str().unwrap())); + } + + let out_path = PathBuf::from(std::env::var("OUT_DIR").unwrap()); + + let bindings = bindgen::Builder::default() + .header("include/nix-c-flake.h") + .clang_args(args) + .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) + .parse_callbacks(Box::new(StripNixPrefix)) + // Blocklist symbols from nix-bindings-util-sys + .blocklist_file(".*nix_api_util\\.h") + // Blocklist symbols from nix-bindings-store-sys + .blocklist_file(".*nix_api_store\\.h") + // Blocklist symbols from nix-bindings-expr-sys + .blocklist_file(".*nix_api_expr\\.h") + .blocklist_file(".*nix_api_value\\.h") + // Blocklist symbols from nix-bindings-fetchers-sys + .blocklist_file(".*nix_api_fetchers\\.h") + // Blocklist symbols from nix-bindings-bdwgc-sys + .blocklist_file(".*/gc\\.h") + .generate() + .expect("Unable to generate bindings"); + + bindings + .write_to_file(out_path.join("bindings.rs")) + .expect("Couldn't write bindings!"); +} diff --git a/nix-bindings-flake-sys/include/nix-c-flake.h b/nix-bindings-flake-sys/include/nix-c-flake.h new file mode 100644 index 0000000..89a45f1 --- /dev/null +++ b/nix-bindings-flake-sys/include/nix-c-flake.h @@ -0,0 +1,3 @@ +#define GC_THREADS +#include +#include diff --git a/nix-bindings-flake-sys/src/lib.rs b/nix-bindings-flake-sys/src/lib.rs new file mode 100644 index 0000000..1bcac64 --- /dev/null +++ b/nix-bindings-flake-sys/src/lib.rs @@ -0,0 +1,11 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(dead_code)] +#![allow(clippy::all)] + +use nix_bindings_expr_sys::*; +use nix_bindings_fetchers_sys::*; +use nix_bindings_util_sys::*; + +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); diff --git a/nix-bindings-flake/Cargo.toml b/nix-bindings-flake/Cargo.toml index 7a76906..78e8460 100644 --- a/nix-bindings-flake/Cargo.toml +++ b/nix-bindings-flake/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nix-bindings-flake" -version = "0.1.1" +version = "0.2.0" edition = "2021" license = "LGPL-2.1" description = "Rust bindings to Nix flakes" @@ -11,11 +11,11 @@ path = "src/lib.rs" [dependencies] anyhow = "1.0" -nix-bindings-expr = { path = "../nix-bindings-expr", version = "0.1.1" } -nix-bindings-fetchers = { path = "../nix-bindings-fetchers", version = "0.1.1" } -nix-bindings-store = { path = "../nix-bindings-store", version = "0.1.1" } -nix-bindings-util = { path = "../nix-bindings-util", version = "0.1.1" } -nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.1.1" } +nix-bindings-expr = { path = "../nix-bindings-expr", version = "0.2.0" } +nix-bindings-fetchers = { path = "../nix-bindings-fetchers", version = "0.2.0" } +nix-bindings-store = { path = "../nix-bindings-store", version = "0.2.0" } +nix-bindings-util = { path = "../nix-bindings-util", version = "0.2.0" } +nix-bindings-flake-sys = { path = "../nix-bindings-flake-sys", version = "0.2.0" } lazy_static = "1.4" ctor = "0.2" tempfile = "3.10" diff --git a/nix-bindings-flake/src/lib.rs b/nix-bindings-flake/src/lib.rs index 5da43dc..cb8e850 100644 --- a/nix-bindings-flake/src/lib.rs +++ b/nix-bindings-flake/src/lib.rs @@ -3,12 +3,12 @@ use std::{ffi::CString, os::raw::c_char, ptr::NonNull}; use anyhow::{Context as _, Result}; use nix_bindings_expr::eval_state::EvalState; use nix_bindings_fetchers::FetchersSettings; +use nix_bindings_flake_sys as raw; use nix_bindings_util::{ context::{self, Context}, result_string_init, string_return::{callback_get_result_string, callback_get_result_string_data}, }; -use nix_bindings_util_sys as raw; /// Store settings for the flakes feature. pub struct FlakeSettings { diff --git a/nix-bindings-store-sys/Cargo.toml b/nix-bindings-store-sys/Cargo.toml new file mode 100644 index 0000000..c098860 --- /dev/null +++ b/nix-bindings-store-sys/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "nix-bindings-store-sys" +version = "0.2.0" +edition = "2021" +build = "build.rs" +license = "LGPL-2.1" + +[lib] +path = "src/lib.rs" + +[dependencies] +nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.2.0" } + +[build-dependencies] +bindgen = "0.69" +pkg-config = "0.3" diff --git a/nix-bindings-store-sys/README.md b/nix-bindings-store-sys/README.md new file mode 100644 index 0000000..73138ee --- /dev/null +++ b/nix-bindings-store-sys/README.md @@ -0,0 +1,5 @@ +# nix-bindings-store-sys + +This crate contains generated bindings for the Nix C API (`nix-store-c`). +**You should not have to use this crate directly,** and so you should probably not add it to your dependencies. +Instead, use the `nix-bindings-store` crate, which _should_ be sufficient. diff --git a/nix-bindings-store-sys/build.rs b/nix-bindings-store-sys/build.rs new file mode 100644 index 0000000..bafdfae --- /dev/null +++ b/nix-bindings-store-sys/build.rs @@ -0,0 +1,40 @@ +use std::path::PathBuf; + +#[derive(Debug)] +struct StripNixPrefix; + +impl bindgen::callbacks::ParseCallbacks for StripNixPrefix { + fn item_name(&self, name: &str) -> Option { + name.strip_prefix("nix_").map(String::from) + } +} + +fn main() { + println!("cargo:rerun-if-changed=include/nix-c-store.h"); + println!("cargo:rustc-link-lib=nixstorec"); + + let mut args = Vec::new(); + for path in pkg_config::probe_library("nix-store-c") + .unwrap() + .include_paths + .iter() + { + args.push(format!("-I{}", path.to_str().unwrap())); + } + + let out_path = PathBuf::from(std::env::var("OUT_DIR").unwrap()); + + let bindings = bindgen::Builder::default() + .header("include/nix-c-store.h") + .clang_args(args) + .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) + .parse_callbacks(Box::new(StripNixPrefix)) + // Blocklist symbols from nix-bindings-util-sys + .blocklist_file(".*nix_api_util\\.h") + .generate() + .expect("Unable to generate bindings"); + + bindings + .write_to_file(out_path.join("bindings.rs")) + .expect("Couldn't write bindings!"); +} diff --git a/nix-bindings-store-sys/include/nix-c-store.h b/nix-bindings-store-sys/include/nix-c-store.h new file mode 100644 index 0000000..2c04d1d --- /dev/null +++ b/nix-bindings-store-sys/include/nix-c-store.h @@ -0,0 +1 @@ +#include diff --git a/nix-bindings-store-sys/src/lib.rs b/nix-bindings-store-sys/src/lib.rs new file mode 100644 index 0000000..4c5c9fe --- /dev/null +++ b/nix-bindings-store-sys/src/lib.rs @@ -0,0 +1,9 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(dead_code)] +#![allow(clippy::all)] + +use nix_bindings_util_sys::*; + +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); diff --git a/nix-bindings-store/Cargo.toml b/nix-bindings-store/Cargo.toml index 2754072..e0ed8a0 100644 --- a/nix-bindings-store/Cargo.toml +++ b/nix-bindings-store/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nix-bindings-store" -version = "0.1.1" +version = "0.2.0" edition = "2021" build = "build.rs" license = "LGPL-2.1" @@ -12,8 +12,9 @@ path = "src/lib.rs" [dependencies] anyhow = "1.0" -nix-bindings-util = { path = "../nix-bindings-util", version = "0.1.1" } -nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.1.1" } +nix-bindings-util = { path = "../nix-bindings-util", version = "0.2.0" } +nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.2.0" } +nix-bindings-store-sys = { path = "../nix-bindings-store-sys", version = "0.2.0" } lazy_static = "1.4" [dev-dependencies] @@ -23,7 +24,7 @@ tempfile = "3.10" [build-dependencies] pkg-config = "0.3" # Needed for version parsing in build.rs -nix-bindings-util = { path = "../nix-bindings-util", version = "0.1.1" } +nix-bindings-util = { path = "../nix-bindings-util", version = "0.2.0" } [lints.rust] warnings = "deny" diff --git a/nix-bindings-store/src/derivation.rs b/nix-bindings-store/src/derivation.rs index f036dce..a1ee1c6 100644 --- a/nix-bindings-store/src/derivation.rs +++ b/nix-bindings-store/src/derivation.rs @@ -1,6 +1,6 @@ #![cfg(nix_at_least = "2.33.0pre")] -use nix_bindings_util_sys as raw; +use nix_bindings_store_sys as raw; use std::ptr::NonNull; /// A Nix derivation diff --git a/nix-bindings-store/src/path.rs b/nix-bindings-store/src/path.rs index ca694f2..51469af 100644 --- a/nix-bindings-store/src/path.rs +++ b/nix-bindings-store/src/path.rs @@ -1,11 +1,11 @@ use std::ptr::NonNull; use anyhow::Result; +use nix_bindings_store_sys as raw; use nix_bindings_util::{ result_string_init, string_return::{callback_get_result_string, callback_get_result_string_data}, }; -use nix_bindings_util_sys as raw; pub struct StorePath { raw: NonNull, diff --git a/nix-bindings-store/src/store.rs b/nix-bindings-store/src/store.rs index 61b60b6..d884c69 100644 --- a/nix-bindings-store/src/store.rs +++ b/nix-bindings-store/src/store.rs @@ -1,11 +1,12 @@ use anyhow::{bail, Error, Result}; use lazy_static::lazy_static; +use nix_bindings_store_sys as raw; use nix_bindings_util::context::Context; use nix_bindings_util::string_return::{ callback_get_result_string, callback_get_result_string_data, }; use nix_bindings_util::{check_call, result_string_init}; -use nix_bindings_util_sys as raw; +use nix_bindings_util_sys as raw_util; #[cfg(nix_at_least = "2.33.0pre")] use std::collections::BTreeMap; use std::collections::HashMap; @@ -73,7 +74,7 @@ lazy_static! { #[cfg(nix_at_least = "2.33.0pre")] unsafe extern "C" fn callback_get_result_store_path_set( - _context: *mut raw::c_context, + _context: *mut raw_util::c_context, user_data: *mut std::os::raw::c_void, store_path: *const raw::StorePath, ) { diff --git a/nix-bindings-util-sys/Cargo.toml b/nix-bindings-util-sys/Cargo.toml index f450317..e37bf1a 100644 --- a/nix-bindings-util-sys/Cargo.toml +++ b/nix-bindings-util-sys/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nix-bindings-util-sys" -version = "0.1.1" +version = "0.2.0" edition = "2021" build = "build.rs" license = "LGPL-2.1" @@ -10,6 +10,8 @@ repository = "https://github.com/nixops4/nix-bindings-rust" [lib] path = "src/lib.rs" +[dependencies] + [build-dependencies] bindgen = "0.69" pkg-config = "0.3" diff --git a/nix-bindings-util-sys/README.md b/nix-bindings-util-sys/README.md index b74d9a1..82debbf 100644 --- a/nix-bindings-util-sys/README.md +++ b/nix-bindings-util-sys/README.md @@ -1,12 +1,5 @@ # nix-bindings-util-sys -This crate contains generated bindings for the Nix C API. +This crate contains generated bindings for the Nix C API (`nix-util-c`). **You should not have to use this crate directly,** and so you should probably not add it to your dependencies. -Instead, use the `nix-bindings-util`, `nix-bindings-store` and `nix-bindings-expr` crates, which _should_ be sufficient. - -## Design - -Rust bindgen currently does not allow "layered" libraries to be split into separate crates. -For example, the expr crate would have all-new types that are distinct and incompatible with the store crate. - -Ideally bindgen will support reusing already generated modules, and we could move the code generation into the appropriate crates, so that the system dependencies of each crate become accurate. +Instead, use the `nix-bindings-util` crate, which _should_ be sufficient. diff --git a/nix-bindings-util-sys/build.rs b/nix-bindings-util-sys/build.rs index 5dbdbc7..2851ee2 100644 --- a/nix-bindings-util-sys/build.rs +++ b/nix-bindings-util-sys/build.rs @@ -11,12 +11,12 @@ impl bindgen::callbacks::ParseCallbacks for StripNixPrefix { fn main() { // Tell cargo to invalidate the built crate whenever the wrapper changes - println!("cargo:rerun-if-changed=include/nix-c-raw.h"); - println!("cargo:rustc-link-lib=nixflake"); + println!("cargo:rerun-if-changed=include/nix-c-util.h"); + println!("cargo:rustc-link-lib=nixutil"); // https://rust-lang.github.io/rust-bindgen/library-usage.html let bindings = bindgen::Builder::default() - .header("include/nix-c-raw.h") + .header("include/nix-c-util.h") // Find the includes .clang_args(c_headers()) // Tell cargo to invalidate the built crate whenever any of the @@ -38,15 +38,7 @@ fn main() { fn c_headers() -> Vec { let mut args = Vec::new(); // args.push("-isystem".to_string()); - for path in pkg_config::probe_library("nix-flake-c") - .unwrap() - .include_paths - .iter() - { - args.push(format!("-I{}", path.to_str().unwrap())); - } - - for path in pkg_config::probe_library("bdw-gc") + for path in pkg_config::probe_library("nix-util-c") .unwrap() .include_paths .iter() diff --git a/nix-bindings-util-sys/include/nix-c-raw.h b/nix-bindings-util-sys/include/nix-c-raw.h deleted file mode 100644 index 88e252f..0000000 --- a/nix-bindings-util-sys/include/nix-c-raw.h +++ /dev/null @@ -1,7 +0,0 @@ -#include -#include -#define GC_THREADS -#include -#include -#include -#include diff --git a/nix-bindings-util-sys/include/nix-c-util.h b/nix-bindings-util-sys/include/nix-c-util.h new file mode 100644 index 0000000..7fd0bc6 --- /dev/null +++ b/nix-bindings-util-sys/include/nix-c-util.h @@ -0,0 +1 @@ +#include diff --git a/nix-bindings-util/Cargo.toml b/nix-bindings-util/Cargo.toml index fcf1aba..6c0d9db 100644 --- a/nix-bindings-util/Cargo.toml +++ b/nix-bindings-util/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nix-bindings-util" -version = "0.1.1" +version = "0.2.0" edition = "2021" license = "LGPL-2.1" description = "Rust bindings to Nix utility library" @@ -11,7 +11,9 @@ path = "src/lib.rs" [dependencies] anyhow = "1.0" -nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.1.1" } +nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.2.0" } + +[dev-dependencies] ctor = "0.2" [lints.rust] diff --git a/nix-bindings-util/src/context.rs b/nix-bindings-util/src/context.rs index af82b27..dbb333e 100644 --- a/nix-bindings-util/src/context.rs +++ b/nix-bindings-util/src/context.rs @@ -113,7 +113,7 @@ macro_rules! check_call_opt_key { { let ctx : &mut $crate::context::Context = $ctx; let ret = $($f)::*(ctx.ptr(), $($arg,)*); - if unsafe { raw::err_code(ctx.ptr()) == raw::err_NIX_ERR_KEY } { + if unsafe { $crate::raw_sys::err_code(ctx.ptr()) == $crate::raw_sys::err_NIX_ERR_KEY } { ctx.clear(); return Ok(None); } diff --git a/nix-bindings-util/src/lib.rs b/nix-bindings-util/src/lib.rs index c74208c..f626d96 100644 --- a/nix-bindings-util/src/lib.rs +++ b/nix-bindings-util/src/lib.rs @@ -3,3 +3,6 @@ pub mod settings; #[macro_use] pub mod string_return; pub mod nix_version; + +// Re-export for use in macros +pub use nix_bindings_util_sys as raw_sys; diff --git a/nix-bindings-util/src/settings.rs b/nix-bindings-util/src/settings.rs index 72af247..8c213aa 100644 --- a/nix-bindings-util/src/settings.rs +++ b/nix-bindings-util/src/settings.rs @@ -74,20 +74,20 @@ mod tests { fn setup() { let mut ctx = context::Context::new(); unsafe { - check_call!(raw::libstore_init(&mut ctx)).unwrap(); + check_call!(nix_bindings_util_sys::libutil_init(&mut ctx)).unwrap(); } } #[test] fn set_get() { // Something that shouldn't matter if it's a different value temporarily - let key = "user-agent-suffix"; + let key = "json-log-path"; // Save the old value, in case it's important. Probably not. // If this doesn't work, pick a different setting to test with let old_value = get(key).unwrap(); - let new_value = "just a string that we're storing into some option for testing purposes"; + let new_value = "/just/a/path/that/we/are/storing/into/some/option/for/testing/purposes"; let res_e = (|| { set(key, new_value)?; From 22480afeb513ee457ed89de84349798e9f287e80 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 7 Jan 2026 09:20:01 +0100 Subject: [PATCH 237/306] EvalStateBuilder: Specify Nix version constraint --- Cargo.lock | 94 ++++++++++++++++------------- nix-bindings-expr/Cargo.toml | 5 ++ nix-bindings-expr/build.rs | 6 ++ nix-bindings-expr/src/eval_state.rs | 5 ++ 4 files changed, 68 insertions(+), 42 deletions(-) create mode 100644 nix-bindings-expr/build.rs diff --git a/Cargo.lock b/Cargo.lock index e4a8151..821630f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 4 [[package]] name = "aho-corasick" -version = "1.1.4" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] @@ -42,9 +42,9 @@ dependencies = [ [[package]] name = "bitflags" -version = "2.10.0" +version = "2.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" [[package]] name = "cexpr" @@ -57,9 +57,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.4" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" [[package]] name = "clang-sys" @@ -105,7 +105,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.61.1", ] [[package]] @@ -116,14 +116,14 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "getrandom" -version = "0.3.4" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", "libc", "r-efi", - "wasip2", + "wasi", ] [[package]] @@ -134,11 +134,11 @@ checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "home" -version = "0.5.12" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -164,9 +164,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.180" +version = "0.2.176" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" +checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" [[package]] name = "libloading" @@ -192,9 +192,9 @@ checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "log" -version = "0.4.29" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" [[package]] name = "memchr" @@ -219,6 +219,7 @@ dependencies = [ "nix-bindings-store", "nix-bindings-util", "nix-bindings-util-sys", + "pkg-config", "tempfile", ] @@ -315,18 +316,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.105" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.43" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" +checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" dependencies = [ "proc-macro2", ] @@ -339,9 +340,9 @@ checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "regex" -version = "1.12.2" +version = "1.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +checksum = "8b5288124840bee7b386bc413c487869b360b2b4ec421ea56425128692f2a82c" dependencies = [ "aho-corasick", "memchr", @@ -351,9 +352,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.13" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +checksum = "833eb9ce86d40ef33cb1306d8accf7bc8ec2bfea4355cbdebb3df68b40925cad" dependencies = [ "aho-corasick", "memchr", @@ -362,9 +363,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.8" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" [[package]] name = "rustc-hash" @@ -387,15 +388,15 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.3" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys 0.11.0", - "windows-sys 0.61.2", + "windows-sys 0.61.1", ] [[package]] @@ -406,9 +407,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "syn" -version = "2.0.114" +version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", @@ -417,22 +418,31 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.24.0" +version = "3.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" +checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" dependencies = [ "fastrand", "getrandom", "once_cell", - "rustix 1.1.3", - "windows-sys 0.61.2", + "rustix 1.1.2", + "windows-sys 0.61.1", ] [[package]] name = "unicode-ident" -version = "1.0.22" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" +checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" + +[[package]] +name = "wasi" +version = "0.14.7+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" +dependencies = [ + "wasip2", +] [[package]] name = "wasip2" @@ -457,9 +467,9 @@ dependencies = [ [[package]] name = "windows-link" -version = "0.2.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" [[package]] name = "windows-sys" @@ -472,9 +482,9 @@ dependencies = [ [[package]] name = "windows-sys" -version = "0.61.2" +version = "0.61.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +checksum = "6f109e41dd4a3c848907eb83d5a42ea98b3769495597450cf6d153507b166f0f" dependencies = [ "windows-link", ] diff --git a/nix-bindings-expr/Cargo.toml b/nix-bindings-expr/Cargo.toml index 41b3cc7..e931946 100644 --- a/nix-bindings-expr/Cargo.toml +++ b/nix-bindings-expr/Cargo.toml @@ -2,6 +2,7 @@ name = "nix-bindings-expr" version = "0.1.0" edition = "2021" +build = "build.rs" license = "LGPL-2.1" description = "Rust bindings to Nix expression evaluator" repository = "https://github.com/nixops4/nix-bindings-rust" @@ -19,6 +20,10 @@ ctor = "0.2" tempfile = "3.10" cstr = "0.2" +[build-dependencies] +pkg-config = "0.3" +nix-bindings-util = { path = "../nix-bindings-util" } + [lints.rust] warnings = "deny" dead-code = "allow" diff --git a/nix-bindings-expr/build.rs b/nix-bindings-expr/build.rs new file mode 100644 index 0000000..6a038bd --- /dev/null +++ b/nix-bindings-expr/build.rs @@ -0,0 +1,6 @@ +use nix_bindings_util::nix_version::emit_version_cfg; + +fn main() { + let nix_version = pkg_config::probe_library("nix-expr-c").unwrap().version; + emit_version_cfg(&nix_version, &["2.26"]); +} diff --git a/nix-bindings-expr/src/eval_state.rs b/nix-bindings-expr/src/eval_state.rs index f2e5b29..70d2ae0 100644 --- a/nix-bindings-expr/src/eval_state.rs +++ b/nix-bindings-expr/src/eval_state.rs @@ -224,6 +224,8 @@ impl Drop for EvalStateRef { /// Provides advanced configuration options for evaluation context setup. /// Use [`EvalState::new`] for simple cases or this builder for custom configuration. /// +/// Requires Nix 2.26.0 or later. +/// /// # Examples /// /// ```rust @@ -244,11 +246,13 @@ impl Drop for EvalStateRef { /// # Ok(()) /// # } /// ``` +#[cfg(nix_at_least = "2.26")] pub struct EvalStateBuilder { eval_state_builder: *mut raw::eval_state_builder, lookup_path: Vec, store: Store, } +#[cfg(nix_at_least = "2.26")] impl Drop for EvalStateBuilder { fn drop(&mut self) { unsafe { @@ -256,6 +260,7 @@ impl Drop for EvalStateBuilder { } } } +#[cfg(nix_at_least = "2.26")] impl EvalStateBuilder { /// Creates a new [`EvalStateBuilder`]. pub fn new(store: Store) -> Result { From eff76e99070d1a88aa8855db9d5b5eeb4639825d Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 7 Jan 2026 09:38:28 +0100 Subject: [PATCH 238/306] Test eval_state_builder_load() to prevent regression --- nix-bindings-expr/src/eval_state.rs | 122 ++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) diff --git a/nix-bindings-expr/src/eval_state.rs b/nix-bindings-expr/src/eval_state.rs index 70d2ae0..6ffb1c9 100644 --- a/nix-bindings-expr/src/eval_state.rs +++ b/nix-bindings-expr/src/eval_state.rs @@ -1255,6 +1255,12 @@ mod tests { #[ctor] fn setup() { test_init(); + + // Configure Nix settings for the test suite + // Set max-call-depth to 1000 (lower than default 10000) for the + // eval_state_builder_loads_max_call_depth test case, while + // giving other tests sufficient room for normal evaluation. + std::env::set_var("NIX_CONFIG", "max-call-depth = 1000"); } /// Run a function while making sure that the current thread is registered with the GC. @@ -2657,4 +2663,120 @@ mod tests { }) .unwrap(); } + + /// Test for path coercion fix (commit 8f6ec2e, ). + /// + /// This test verifies that path coercion works correctly with EvalStateBuilder. + /// Path coercion requires readOnlyMode = false, which is loaded from global + /// settings by calling eval_state_builder_load(). + /// + /// # Background + /// + /// Without the eval_state_builder_load() call, settings from global Nix + /// configuration are never loaded, leaving readOnlyMode = true (the default). + /// This prevents Nix from adding paths to the store during evaluation, + /// which could cause errors like: "error: path '/some/local/path' does not exist" + /// + /// # Test Coverage + /// + /// This test exercises store file creation: + /// 1. builtins.toFile successfully creates files in the store + /// 2. Files are actually written to /nix/store + /// 3. Content is written correctly + /// + /// Note: This test may not reliably fail without the fix in all environments. + /// Use eval_state_builder_loads_max_call_depth for a deterministic test. + #[test] + #[cfg(nix_at_least = "2.26" /* real_path, eval_state_builder_load */)] + fn eval_state_builder_path_coercion() { + gc_registering_current_thread(|| { + let mut store = Store::open(None, HashMap::new()).unwrap(); + let mut es = EvalStateBuilder::new(store.clone()) + .unwrap() + .build() + .unwrap(); + + // Use builtins.toFile to create a file in the store. + // This operation requires readOnlyMode = false to succeed. + let expr = r#"builtins.toFile "test-file.txt" "test content""#; + + // Evaluate the expression + let value = es.eval_from_string(expr, "").unwrap(); + + // Realise the string to get the path and associated store paths + let realised = es.realise_string(&value, false).unwrap(); + + // Verify we got exactly one store path + assert_eq!( + realised.paths.len(), + 1, + "Expected 1 store path, got {}", + realised.paths.len() + ); + + // Get the physical filesystem path for the store path + // In a relocated store, this differs from realised.s + let physical_path = store.real_path(&realised.paths[0]).unwrap(); + + // Verify the store path actually exists on disk + assert!( + std::path::Path::new(&physical_path).exists(), + "Store path should exist: {}", + physical_path + ); + + // Verify the content was written correctly + let store_content = std::fs::read_to_string(&physical_path).unwrap(); + assert_eq!(store_content, "test content"); + }) + .unwrap(); + } + + /// Test that eval_state_builder_load() loads settings. + /// + /// Uses max-call-depth as the test setting. The test suite sets + /// max-call-depth = 1000 via NIX_CONFIG in setup() for the purpose of this test case. + /// This test creates a recursive function that calls itself 1100 times. + /// + /// - WITH the fix: Settings are loaded, max-call-depth=1000 is enforced, + /// recursion fails at depth 1000 + /// - WITHOUT the fix: Settings aren't loaded, default max-call-depth=10000 + /// is used, recursion of 1100 succeeds when it should fail + #[test] + #[cfg(nix_at_least = "2.26")] + fn eval_state_builder_loads_max_call_depth() { + gc_registering_current_thread(|| { + let store = Store::open(None, HashMap::new()).unwrap(); + let mut es = EvalStateBuilder::new(store).unwrap().build().unwrap(); + + // Create a recursive function that calls itself 1100 times + // This should fail because max-call-depth is 1000 (set in setup()) + let expr = r#" + let + recurse = n: if n == 0 then "done" else recurse (n - 1); + in + recurse 1100 + "#; + + let result = es.eval_from_string(expr, ""); + + match result { + Err(e) => { + let err_str = e.to_string(); + assert!( + err_str.contains("max-call-depth"), + "Expected max-call-depth error, got: {}", + err_str + ); + } + Ok(_) => { + panic!( + "Expected recursion to fail with max-call-depth=1000, but it succeeded. \ + This indicates eval_state_builder_load() was not called." + ); + } + } + }) + .unwrap(); + } } From f1d15ff416c83a7e683f13ed57516192f774b06a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 7 Jan 2026 09:48:18 +0100 Subject: [PATCH 239/306] EvalStateBuilder: Allow opting out of ambient settings The C API provides nix_eval_state_builder_load as a separate function to allow controlling whether settings are loaded from the environment. Add load_ambient_settings() method to expose this control, with the default being to load them (needed in some situations to allow path coercion to work). --- nix-bindings-expr/src/eval_state.rs | 75 +++++++++++++++++++++++++++-- 1 file changed, 70 insertions(+), 5 deletions(-) diff --git a/nix-bindings-expr/src/eval_state.rs b/nix-bindings-expr/src/eval_state.rs index 6ffb1c9..5a82fc6 100644 --- a/nix-bindings-expr/src/eval_state.rs +++ b/nix-bindings-expr/src/eval_state.rs @@ -250,6 +250,7 @@ impl Drop for EvalStateRef { pub struct EvalStateBuilder { eval_state_builder: *mut raw::eval_state_builder, lookup_path: Vec, + load_ambient_settings: bool, store: Store, } #[cfg(nix_at_least = "2.26")] @@ -271,6 +272,7 @@ impl EvalStateBuilder { store, eval_state_builder, lookup_path: Vec::new(), + load_ambient_settings: true, }) } /// Sets the [lookup path](https://nix.dev/manual/nix/latest/language/constructs/lookup-path.html) for Nix expression evaluation. @@ -286,6 +288,15 @@ impl EvalStateBuilder { self.lookup_path = lookup_path; Ok(self) } + /// Sets whether to load settings from the ambient environment. + /// + /// When enabled (default), calls `nix_eval_state_builder_load` to load settings + /// from NIX_CONFIG and other environment variables. When disabled, only the + /// explicitly configured settings are used. + pub fn load_ambient_settings(mut self, load: bool) -> Self { + self.load_ambient_settings = load; + self + } /// Builds the configured [`EvalState`]. pub fn build(&self) -> Result { // Make sure the library is initialized @@ -295,11 +306,13 @@ impl EvalStateBuilder { // Load settings from global configuration (including readOnlyMode = false). // This is necessary for path coercion to work (adding files to the store). - unsafe { - check_call!(raw::eval_state_builder_load( - &mut context, - self.eval_state_builder - ))?; + if self.load_ambient_settings { + unsafe { + check_call!(raw::eval_state_builder_load( + &mut context, + self.eval_state_builder + ))?; + } } // Note: these raw C string pointers borrow from self.lookup_path @@ -2742,6 +2755,9 @@ mod tests { /// recursion fails at depth 1000 /// - WITHOUT the fix: Settings aren't loaded, default max-call-depth=10000 /// is used, recursion of 1100 succeeds when it should fail + /// + /// Complementary to eval_state_builder_ignores_ambient_when_disabled which verifies + /// that ambient settings are NOT loaded when disabled. #[test] #[cfg(nix_at_least = "2.26")] fn eval_state_builder_loads_max_call_depth() { @@ -2779,4 +2795,53 @@ mod tests { }) .unwrap(); } + + /// Test that load_ambient_settings(false) ignores the ambient environment. + /// + /// The test suite sets max-call-depth = 1000 via NIX_CONFIG in setup(). + /// When we disable loading ambient settings, this should be ignored and + /// the default max-call-depth = 10000 should be used instead. + /// + /// Complementary to eval_state_builder_loads_max_call_depth which verifies + /// that ambient settings ARE loaded when enabled. + #[test] + #[cfg(nix_at_least = "2.26")] + fn eval_state_builder_ignores_ambient_when_disabled() { + gc_registering_current_thread(|| { + let store = Store::open(None, HashMap::new()).unwrap(); + let mut es = EvalStateBuilder::new(store) + .unwrap() + .load_ambient_settings(false) + .build() + .unwrap(); + + // Create a recursive function that calls itself 1100 times + // With ambient settings disabled, default max-call-depth=10000 is used, + // so this should succeed (unlike eval_state_builder_loads_max_call_depth) + let expr = r#" + let + recurse = n: if n == 0 then "done" else recurse (n - 1); + in + recurse 1100 + "#; + + let result = es.eval_from_string(expr, ""); + + match result { + Ok(value) => { + // Success expected - ambient NIX_CONFIG was ignored + let result_str = es.require_string(&value).unwrap(); + assert_eq!(result_str, "done"); + } + Err(e) => { + panic!( + "Expected recursion to succeed with default max-call-depth=10000, \ + but it failed: {}. This indicates ambient settings were not ignored.", + e + ); + } + } + }) + .unwrap(); + } } From 6110414520617d2ea279ee5b44b726aba4699a04 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 13 Jan 2026 15:25:57 +0100 Subject: [PATCH 240/306] Add automatic C library input propagation workaround Automatically adds Nix C library build inputs based on which nix-bindings crates are dependencies, working around missing native input propagation in nix-cargo-integration. The workaround inspects the dreamLock to detect: - If the crate being built is a nix-bindings crate (adds its own inputs) - Direct dependencies on nix-bindings crates (adds their inputs) The mapping is recursive via lazyAttrsOf, so depending on nix-bindings-flake automatically brings in transitive C library dependencies. Downstream consumers can extend the mapping for their own multi-crate workspaces where crate A depends on crate B which depends on nix-bindings. --- flake.nix | 25 +++--- input-propagation-workaround.nix | 143 +++++++++++++++++++++++++++++++ nci.nix | 54 +----------- 3 files changed, 156 insertions(+), 66 deletions(-) create mode 100644 input-propagation-workaround.nix diff --git a/flake.nix b/flake.nix index ddd12be..a8295dd 100644 --- a/flake.nix +++ b/flake.nix @@ -28,6 +28,7 @@ }: { _file = ./flake.nix; + imports = [ ./input-propagation-workaround.nix ]; options.perSystem = flake-parts-lib.mkPerSystemOption ( { pkgs, ... }: { @@ -46,24 +47,20 @@ A module to load into your nix-cargo-integration [`perSystem.nci.projects..depsDrvConfig`](https://flake.parts/options/nix-cargo-integration.html#opt-perSystem.nci.projects._name_.depsDrvConfig) or similar such options. - This provides common build configuration (pkg-config, libclang, etc.) but you must - add the specific Nix C libraries your crates need to `buildInputs`: - - `nix-bindings-util-sys` needs `nix-util-c` - - `nix-bindings-store-sys` needs `nix-store-c` - - `nix-bindings-expr-sys` needs `nix-expr-c` - - `nix-bindings-fetchers-sys` needs `nix-fetchers-c` (Nix >= 2.29) - - `nix-bindings-flake-sys` needs `nix-flake-c` - - `nix-bindings-bdwgc-sys` needs `boehmgc` + This provides common build configuration (pkg-config, libclang, etc.) and + automatically adds Nix C library build inputs based on which nix-bindings + crates are *direct* dependencies of your crate. + + To disable automatic build input detection: + ```nix + nix-bindings-rust.inputPropagationWorkaround.enable = false; + ``` Example: ```nix - perSystem = perSystem@{ config, pkgs, ... }: { - nci.projects."my_project".depsDrvConfig = { + perSystem = perSystem@{ config, ... }: { + nci.projects."my_project".drvConfig = { imports = [ perSystem.config.nix-bindings-rust.nciBuildConfig ]; - mkDerivation.buildInputs = [ - perSystem.config.nix-bindings-rust.nixPackage.libs.nix-store-c - # ... add other libs as needed - ]; }; } ``` diff --git a/input-propagation-workaround.nix b/input-propagation-workaround.nix new file mode 100644 index 0000000..c3ac92f --- /dev/null +++ b/input-propagation-workaround.nix @@ -0,0 +1,143 @@ +# Workaround for missing native input propagation in nix-cargo-integration +# +# Automatically adds Nix C library build inputs based on which nix-bindings +# crates are direct dependencies of the crate being built. The mapping is +# recursive, so depending on nix-bindings-flake will also bring in the +# transitive C library dependencies (nix-fetchers-c, nix-expr-c, etc.). +# +# Note: For multi-crate workspaces, if your crate A depends on your crate B +# which depends on nix-bindings, you'll need to add an A -> B mapping to +# `crateInputMapping` so that A also gets B's nix-bindings inputs. +{ + perSystem = + { + lib, + config, + pkgs, + ... + }: + let + cfg = config.nix-bindings-rust.inputPropagationWorkaround; + nixPackage = config.nix-bindings-rust.nixPackage; + + nixLibs = + if nixPackage ? libs then + nixPackage.libs + else + # Fallback for older Nix versions without split libs + { + nix-util-c = nixPackage; + nix-store-c = nixPackage; + nix-expr-c = nixPackage; + nix-fetchers-c = nixPackage; + nix-flake-c = nixPackage; + }; + + # A module for nciBuildConfig that sets buildInputs based on nix-bindings dependencies. + # Uses options inspection to detect drvConfig vs depsDrvConfig context. + workaroundModule = + { + lib, + config, + options, + ... + }: + let + # rust-cargo-lock exists in drvConfig but not depsDrvConfig + isDrvConfig = options ? rust-cargo-lock; + + dreamLock = config.rust-cargo-lock.dreamLock; + depsList = dreamLock.dependencies.${config.name}.${config.version} or [ ]; + + # Convert list of deps to attrset keyed by name for efficient lookup + deps = builtins.listToAttrs ( + map (dep: { + name = dep.name; + value = dep; + }) depsList + ); + + # Inputs for the crate itself if it's in the mapping + selfInputs = cfg.crateInputMapping.${config.name} or [ ]; + + # Inputs for direct dependencies that have mappings + depInputs = lib.concatLists (lib.attrValues (lib.intersectAttrs deps cfg.crateInputMapping)); + + allInputs = selfInputs ++ depInputs; + in + { + config = lib.optionalAttrs isDrvConfig { + mkDerivation.buildInputs = allInputs; + rust-crane.depsDrv.mkDerivation.buildInputs = allInputs; + }; + }; + in + { + options.nix-bindings-rust.inputPropagationWorkaround = { + enable = lib.mkOption { + type = lib.types.bool; + default = true; + description = '' + Whether to automatically add Nix C library build inputs based on + which nix-bindings crates are direct dependencies. + + Set to `false` to disable automatic detection and specify buildInputs manually. + ''; + }; + + crateInputMapping = lib.mkOption { + type = lib.types.lazyAttrsOf (lib.types.listOf lib.types.package); + description = '' + Mapping from crate names to build inputs. Entries can reference + other entries for transitive dependencies. + + The input propagation workaround can see direct dependencies, so + if you have `my-crate -> nix-bindings`, that works out of the box. + If you have `my-other-crate -> my-crate -> nix-bindings`, then you + need to specify `my-other-crate -> my-crate` as follows: + + ```nix + nix-bindings-rust.inputPropagationWorkaround.crateInputMapping."my-other-crate" = + config.nix-bindings-rust.inputPropagationWorkaround.crateInputMapping."my-crate"; + ``` + ''; + default = { }; + }; + }; + + config = lib.mkIf cfg.enable { + nix-bindings-rust.inputPropagationWorkaround.crateInputMapping = { + # -sys crates with their transitive dependencies + "nix-bindings-bdwgc-sys" = [ pkgs.boehmgc ]; + "nix-bindings-util-sys" = [ nixLibs.nix-util-c.dev ]; + "nix-bindings-store-sys" = [ + nixLibs.nix-store-c.dev + ] + ++ cfg.crateInputMapping."nix-bindings-util-sys"; + "nix-bindings-expr-sys" = [ + nixLibs.nix-expr-c.dev + ] + ++ cfg.crateInputMapping."nix-bindings-store-sys" + ++ cfg.crateInputMapping."nix-bindings-bdwgc-sys"; + "nix-bindings-fetchers-sys" = [ + nixLibs.nix-fetchers-c.dev + ] + ++ cfg.crateInputMapping."nix-bindings-expr-sys"; + "nix-bindings-flake-sys" = [ + nixLibs.nix-flake-c.dev + ] + ++ cfg.crateInputMapping."nix-bindings-fetchers-sys" + ++ cfg.crateInputMapping."nix-bindings-bdwgc-sys"; + # High-level crates reference their -sys counterparts + "nix-bindings-bdwgc" = cfg.crateInputMapping."nix-bindings-bdwgc-sys"; + "nix-bindings-util" = cfg.crateInputMapping."nix-bindings-util-sys"; + "nix-bindings-store" = cfg.crateInputMapping."nix-bindings-store-sys"; + "nix-bindings-expr" = cfg.crateInputMapping."nix-bindings-expr-sys"; + "nix-bindings-fetchers" = cfg.crateInputMapping."nix-bindings-fetchers-sys"; + "nix-bindings-flake" = cfg.crateInputMapping."nix-bindings-flake-sys"; + }; + + nix-bindings-rust.nciBuildConfig.imports = [ workaroundModule ]; + }; + }; +} diff --git a/nci.nix b/nci.nix index 9a468b6..62c523e 100644 --- a/nci.nix +++ b/nci.nix @@ -1,24 +1,8 @@ { perSystem = - { - config, - pkgs, - ... - }: + { config, ... }: let cfg = config.nix-bindings-rust; - nixLibs = - if cfg.nixPackage ? libs then - cfg.nixPackage.libs - else - # Fallback for older Nix versions without split libs - { - nix-util-c = cfg.nixPackage; - nix-store-c = cfg.nixPackage; - nix-expr-c = cfg.nixPackage; - nix-fetchers-c = cfg.nixPackage; - nix-flake-c = cfg.nixPackage; - }; in { # https://flake.parts/options/nix-cargo-integration @@ -31,7 +15,7 @@ drvConfig = { imports = [ # Downstream projects import this into depsDrvConfig instead - config.nix-bindings-rust.nciBuildConfig + cfg.nciBuildConfig ]; # Extra settings for running the tests mkDerivation = { @@ -65,39 +49,5 @@ }; }; }; - - # Per-crate configuration: only provide the specific Nix libs each crate needs - # FIXME should use propagatedBuildInputs - nci.crates.nix-bindings-bdwgc-sys.drvConfig.mkDerivation.buildInputs = [ - pkgs.boehmgc - ]; - nci.crates.nix-bindings-util-sys.drvConfig.mkDerivation.buildInputs = [ - nixLibs.nix-util-c - ]; - nci.crates.nix-bindings-util.drvConfig.mkDerivation.buildInputs = - config.nci.crates.nix-bindings-util-sys.drvConfig.mkDerivation.buildInputs; - nci.crates.nix-bindings-store-sys.drvConfig.mkDerivation.buildInputs = - config.nci.crates.nix-bindings-util-sys.drvConfig.mkDerivation.buildInputs - ++ [ nixLibs.nix-store-c ]; - nci.crates.nix-bindings-store.drvConfig.mkDerivation.buildInputs = - config.nci.crates.nix-bindings-store-sys.drvConfig.mkDerivation.buildInputs; - nci.crates.nix-bindings-expr-sys.drvConfig.mkDerivation.buildInputs = - config.nci.crates.nix-bindings-store-sys.drvConfig.mkDerivation.buildInputs - ++ [ - nixLibs.nix-expr-c - pkgs.boehmgc - ]; - nci.crates.nix-bindings-expr.drvConfig.mkDerivation.buildInputs = - config.nci.crates.nix-bindings-expr-sys.drvConfig.mkDerivation.buildInputs; - nci.crates.nix-bindings-fetchers-sys.drvConfig.mkDerivation.buildInputs = - config.nci.crates.nix-bindings-expr-sys.drvConfig.mkDerivation.buildInputs - ++ [ nixLibs.nix-fetchers-c ]; - nci.crates.nix-bindings-fetchers.drvConfig.mkDerivation.buildInputs = - config.nci.crates.nix-bindings-fetchers-sys.drvConfig.mkDerivation.buildInputs; - nci.crates.nix-bindings-flake-sys.drvConfig.mkDerivation.buildInputs = - config.nci.crates.nix-bindings-fetchers-sys.drvConfig.mkDerivation.buildInputs - ++ [ nixLibs.nix-flake-c ]; - nci.crates.nix-bindings-flake.drvConfig.mkDerivation.buildInputs = - config.nci.crates.nix-bindings-flake-sys.drvConfig.mkDerivation.buildInputs; }; } From 0266e22379c822e9ffa6e7bd7b071914ebe497bd Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 13 Jan 2026 16:49:52 +0100 Subject: [PATCH 241/306] Add CHANGELOG and crate READMEs - Add CHANGELOG.md documenting 0.1.0 and 0.2.0 releases with contributor credits and PR links - Add README.md to each crate linking to the changelog - Link READMEs in Cargo.toml files - Add repository field to -sys crates that were missing it - Update top-level README to reference changelog and reflect -sys crate split - Document changelog update step in release process --- CHANGELOG.md | 83 ++++++++++++++++++++++++++++ README.md | 3 +- doc/maintainers/release.md | 11 +++- nix-bindings-bdwgc-sys/Cargo.toml | 2 + nix-bindings-bdwgc-sys/README.md | 4 ++ nix-bindings-expr-sys/Cargo.toml | 2 + nix-bindings-expr-sys/README.md | 4 ++ nix-bindings-expr/Cargo.toml | 1 + nix-bindings-expr/README.md | 7 +++ nix-bindings-fetchers-sys/Cargo.toml | 2 + nix-bindings-fetchers-sys/README.md | 4 ++ nix-bindings-fetchers/Cargo.toml | 1 + nix-bindings-fetchers/README.md | 7 +++ nix-bindings-flake-sys/Cargo.toml | 2 + nix-bindings-flake-sys/README.md | 4 ++ nix-bindings-flake/Cargo.toml | 1 + nix-bindings-flake/README.md | 7 +++ nix-bindings-store-sys/Cargo.toml | 2 + nix-bindings-store-sys/README.md | 4 ++ nix-bindings-store/Cargo.toml | 1 + nix-bindings-store/README.md | 7 +++ nix-bindings-util-sys/Cargo.toml | 1 + nix-bindings-util-sys/README.md | 4 ++ nix-bindings-util/Cargo.toml | 1 + nix-bindings-util/README.md | 7 +++ 25 files changed, 170 insertions(+), 2 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 nix-bindings-expr/README.md create mode 100644 nix-bindings-fetchers/README.md create mode 100644 nix-bindings-flake/README.md create mode 100644 nix-bindings-store/README.md create mode 100644 nix-bindings-util/README.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..7c8ea3a --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,83 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [0.2.0] - 2026-01-13 + +### Added + +- Workaround for automatic C library input propagation in downstream Nix builds. ([#27] by [@roberth]) +- `EvalStateBuilder::load_ambient_settings()` to control whether global Nix settings are loaded. ([#36] by [@roberth]) + +### Fixed + +- Path coercion failing with "path does not exist" errors due to missing `eval_state_builder_load()` call. ([#36] by [@aanderse]) + +### Changed + +- Split `nix-bindings-util-sys` (which contained all low-level FFI bindings) into separate per-library `*-sys` crates. ([#27] by [@Ericson2314]) + This allows downstream crates to depend on just the low-level bindings they need without pulling in higher-level crates. + +## [0.1.0] - 2026-01-12 + +Initial release, extracted from the [nixops4 repository](https://github.com/nixops4/nixops4). + +### Added + +- `nix-bindings-store`: Rust bindings for Nix store operations + - Store opening (auto, from URI, from environment) + - Store path parsing and manipulation + - `Store::get_fs_closure` ([#12] by [@RossComputerGuy], [@roberth]) + - `Clone` for `Derivation` ([#25] by [@Ericson2314]) + - Store deduplication workaround for [nix#11979] + - aarch64 ABI support ([#26] by [@RossComputerGuy]) +- `nix-bindings-expr`: Rust bindings for Nix expression evaluation + - `EvalState` for evaluating Nix expressions + - Value creation (int, string, attrs, thunks, primops, etc.) + - Value inspection/extraction (`require_*` functions) + - Attribute selection and manipulation + - Thread registration for GC safety +- `nix-bindings-fetchers`: Rust bindings for Nix fetchers +- `nix-bindings-flake`: Rust bindings for Nix flake operations + - Flake locking + - Flake overriding +- `nix-bindings-util`: Shared utilities + - Context management for Nix C API error handling + - Settings access +- `nix-bindings-util-sys`: Low-level FFI bindings for all Nix C libraries + +### Contributors + +Thanks to everyone who contributed to the initial development, some of whom may not be listed with individual changes above: + +- [@aanderse] +- [@Ericson2314] +- [@ErinvanderVeen] +- [@numinit] +- [@prednaz] +- [@Radvendii] +- [@roberth] +- [@RossComputerGuy] + + + +[@aanderse]: https://github.com/aanderse +[@Ericson2314]: https://github.com/Ericson2314 +[@ErinvanderVeen]: https://github.com/ErinvanderVeen +[@numinit]: https://github.com/numinit +[@prednaz]: https://github.com/prednaz +[@Radvendii]: https://github.com/Radvendii +[@roberth]: https://github.com/roberth +[@RossComputerGuy]: https://github.com/RossComputerGuy + +[#12]: https://github.com/nixops4/nix-bindings-rust/pull/12 +[#25]: https://github.com/nixops4/nix-bindings-rust/pull/25 +[#26]: https://github.com/nixops4/nix-bindings-rust/pull/26 +[#27]: https://github.com/nixops4/nix-bindings-rust/pull/27 +[#36]: https://github.com/nixops4/nix-bindings-rust/pull/36 +[0.2.0]: https://github.com/nixops4/nix-bindings-rust/compare/0.1.0...0.2.0 +[0.1.0]: https://github.com/nixops4/nix-bindings-rust/releases/tag/0.1.0 +[nix#11979]: https://github.com/NixOS/nix/issues/11979 diff --git a/README.md b/README.md index 44c0975..ad6f2ac 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ This workspace provides multiple crates that wrap different layers of the Nix C - **`nix-bindings-flake`** - Flake operations - **`nix-bindings-fetchers`** - Fetcher functionality (requires Nix ≥ 2.29) -The `nix-bindings-util-sys` crate contains the generated FFI bindings and is not intended for direct use. +The `*-sys` crates contain generated FFI bindings and are not intended for direct use. ## Features @@ -211,6 +211,7 @@ For VSCode, load the dev shell via Nix Env Selector extension or direnv. ## Documentation +- [Changelog](CHANGELOG.md) - [Nix C API Reference][C API] - [nix-cargo-integration][nix-cargo-integration] - [Hacking Guide](doc/hacking/test-ffi.md) diff --git a/doc/maintainers/release.md b/doc/maintainers/release.md index 59056dd..3b0f03b 100644 --- a/doc/maintainers/release.md +++ b/doc/maintainers/release.md @@ -1,9 +1,18 @@ - # Release process This project uses simple tags, that trigger a release of all crates using Hercules CI. See [HCI Effects cargo publish workflow]. +## Before tagging + +- Update `CHANGELOG.md`: + - Make sure the Unreleased section is up to date + - Change it to the new version and release date + +## After tagging + +- Add a new Unreleased section to `CHANGELOG.md` + Dissatisfied with the coarse grained release process? Complain to @roberth and he'll get it done for you. [HCI Effects cargo publish workflow]: https://docs.hercules-ci.com/hercules-ci-effects/reference/flake-parts/cargo-publish/#_releasing_a_version diff --git a/nix-bindings-bdwgc-sys/Cargo.toml b/nix-bindings-bdwgc-sys/Cargo.toml index bf75ca7..ff4bc1e 100644 --- a/nix-bindings-bdwgc-sys/Cargo.toml +++ b/nix-bindings-bdwgc-sys/Cargo.toml @@ -4,6 +4,8 @@ version = "0.2.0" edition = "2021" build = "build.rs" license = "LGPL-2.1" +repository = "https://github.com/nixops4/nix-bindings-rust" +readme = "README.md" [lib] path = "src/lib.rs" diff --git a/nix-bindings-bdwgc-sys/README.md b/nix-bindings-bdwgc-sys/README.md index 3723e90..80988a5 100644 --- a/nix-bindings-bdwgc-sys/README.md +++ b/nix-bindings-bdwgc-sys/README.md @@ -2,3 +2,7 @@ This crate contains generated bindings for the Boehm-Demers-Weiser garbage collector (`bdw-gc`). **You should not have to use this crate directly,** and so you should probably not add it to your dependencies. + +## Changelog + +See the [nix-bindings-rust changelog](https://github.com/nixops4/nix-bindings-rust/blob/main/CHANGELOG.md). diff --git a/nix-bindings-expr-sys/Cargo.toml b/nix-bindings-expr-sys/Cargo.toml index 39518fc..1512326 100644 --- a/nix-bindings-expr-sys/Cargo.toml +++ b/nix-bindings-expr-sys/Cargo.toml @@ -4,6 +4,8 @@ version = "0.2.0" edition = "2021" build = "build.rs" license = "LGPL-2.1" +repository = "https://github.com/nixops4/nix-bindings-rust" +readme = "README.md" [lib] path = "src/lib.rs" diff --git a/nix-bindings-expr-sys/README.md b/nix-bindings-expr-sys/README.md index ccdcc26..906f366 100644 --- a/nix-bindings-expr-sys/README.md +++ b/nix-bindings-expr-sys/README.md @@ -3,3 +3,7 @@ This crate contains generated bindings for the Nix C API (`nix-expr-c`). **You should not have to use this crate directly,** and so you should probably not add it to your dependencies. Instead, use the `nix-bindings-expr` crate, which _should_ be sufficient. + +## Changelog + +See the [nix-bindings-rust changelog](https://github.com/nixops4/nix-bindings-rust/blob/main/CHANGELOG.md). diff --git a/nix-bindings-expr/Cargo.toml b/nix-bindings-expr/Cargo.toml index 4e64c63..f72ea6e 100644 --- a/nix-bindings-expr/Cargo.toml +++ b/nix-bindings-expr/Cargo.toml @@ -6,6 +6,7 @@ build = "build.rs" license = "LGPL-2.1" description = "Rust bindings to Nix expression evaluator" repository = "https://github.com/nixops4/nix-bindings-rust" +readme = "README.md" [lib] path = "src/lib.rs" diff --git a/nix-bindings-expr/README.md b/nix-bindings-expr/README.md new file mode 100644 index 0000000..ebd889a --- /dev/null +++ b/nix-bindings-expr/README.md @@ -0,0 +1,7 @@ +# nix-bindings-expr + +Rust bindings to the Nix expression evaluator. + +## Changelog + +See the [nix-bindings-rust changelog](https://github.com/nixops4/nix-bindings-rust/blob/main/CHANGELOG.md). diff --git a/nix-bindings-fetchers-sys/Cargo.toml b/nix-bindings-fetchers-sys/Cargo.toml index 6101928..a4742c9 100644 --- a/nix-bindings-fetchers-sys/Cargo.toml +++ b/nix-bindings-fetchers-sys/Cargo.toml @@ -4,6 +4,8 @@ version = "0.2.0" edition = "2021" build = "build.rs" license = "LGPL-2.1" +repository = "https://github.com/nixops4/nix-bindings-rust" +readme = "README.md" [lib] path = "src/lib.rs" diff --git a/nix-bindings-fetchers-sys/README.md b/nix-bindings-fetchers-sys/README.md index b403eac..87113b6 100644 --- a/nix-bindings-fetchers-sys/README.md +++ b/nix-bindings-fetchers-sys/README.md @@ -3,3 +3,7 @@ This crate contains generated bindings for the Nix C API (`nix-fetchers-c`). **You should not have to use this crate directly,** and so you should probably not add it to your dependencies. Instead, use the `nix-bindings-fetchers` crate, which _should_ be sufficient. + +## Changelog + +See the [nix-bindings-rust changelog](https://github.com/nixops4/nix-bindings-rust/blob/main/CHANGELOG.md). diff --git a/nix-bindings-fetchers/Cargo.toml b/nix-bindings-fetchers/Cargo.toml index cf216a0..fde4ce5 100644 --- a/nix-bindings-fetchers/Cargo.toml +++ b/nix-bindings-fetchers/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" license = "LGPL-2.1" description = "Rust bindings to Nix fetchers" repository = "https://github.com/nixops4/nix-bindings-rust" +readme = "README.md" [lib] path = "src/lib.rs" diff --git a/nix-bindings-fetchers/README.md b/nix-bindings-fetchers/README.md new file mode 100644 index 0000000..6d23b13 --- /dev/null +++ b/nix-bindings-fetchers/README.md @@ -0,0 +1,7 @@ +# nix-bindings-fetchers + +Rust bindings to the nix-fetchers library. + +## Changelog + +See the [nix-bindings-rust changelog](https://github.com/nixops4/nix-bindings-rust/blob/main/CHANGELOG.md). diff --git a/nix-bindings-flake-sys/Cargo.toml b/nix-bindings-flake-sys/Cargo.toml index e53bbe4..ecc3e99 100644 --- a/nix-bindings-flake-sys/Cargo.toml +++ b/nix-bindings-flake-sys/Cargo.toml @@ -4,6 +4,8 @@ version = "0.2.0" edition = "2021" build = "build.rs" license = "LGPL-2.1" +repository = "https://github.com/nixops4/nix-bindings-rust" +readme = "README.md" [lib] path = "src/lib.rs" diff --git a/nix-bindings-flake-sys/README.md b/nix-bindings-flake-sys/README.md index 10a9d96..84114d7 100644 --- a/nix-bindings-flake-sys/README.md +++ b/nix-bindings-flake-sys/README.md @@ -3,3 +3,7 @@ This crate contains generated bindings for the Nix C API (`nix-flake-c`). **You should not have to use this crate directly,** and so you should probably not add it to your dependencies. Instead, use the `nix-bindings-flake` crate, which _should_ be sufficient. + +## Changelog + +See the [nix-bindings-rust changelog](https://github.com/nixops4/nix-bindings-rust/blob/main/CHANGELOG.md). diff --git a/nix-bindings-flake/Cargo.toml b/nix-bindings-flake/Cargo.toml index 78e8460..30a426d 100644 --- a/nix-bindings-flake/Cargo.toml +++ b/nix-bindings-flake/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" license = "LGPL-2.1" description = "Rust bindings to Nix flakes" repository = "https://github.com/nixops4/nix-bindings-rust" +readme = "README.md" [lib] path = "src/lib.rs" diff --git a/nix-bindings-flake/README.md b/nix-bindings-flake/README.md new file mode 100644 index 0000000..6fda25d --- /dev/null +++ b/nix-bindings-flake/README.md @@ -0,0 +1,7 @@ +# nix-bindings-flake + +Rust bindings to Nix flakes. + +## Changelog + +See the [nix-bindings-rust changelog](https://github.com/nixops4/nix-bindings-rust/blob/main/CHANGELOG.md). diff --git a/nix-bindings-store-sys/Cargo.toml b/nix-bindings-store-sys/Cargo.toml index c098860..c1e03f2 100644 --- a/nix-bindings-store-sys/Cargo.toml +++ b/nix-bindings-store-sys/Cargo.toml @@ -4,6 +4,8 @@ version = "0.2.0" edition = "2021" build = "build.rs" license = "LGPL-2.1" +repository = "https://github.com/nixops4/nix-bindings-rust" +readme = "README.md" [lib] path = "src/lib.rs" diff --git a/nix-bindings-store-sys/README.md b/nix-bindings-store-sys/README.md index 73138ee..31a2d7e 100644 --- a/nix-bindings-store-sys/README.md +++ b/nix-bindings-store-sys/README.md @@ -3,3 +3,7 @@ This crate contains generated bindings for the Nix C API (`nix-store-c`). **You should not have to use this crate directly,** and so you should probably not add it to your dependencies. Instead, use the `nix-bindings-store` crate, which _should_ be sufficient. + +## Changelog + +See the [nix-bindings-rust changelog](https://github.com/nixops4/nix-bindings-rust/blob/main/CHANGELOG.md). diff --git a/nix-bindings-store/Cargo.toml b/nix-bindings-store/Cargo.toml index e0ed8a0..8a018dd 100644 --- a/nix-bindings-store/Cargo.toml +++ b/nix-bindings-store/Cargo.toml @@ -6,6 +6,7 @@ build = "build.rs" license = "LGPL-2.1" description = "Rust bindings to Nix store library" repository = "https://github.com/nixops4/nix-bindings-rust" +readme = "README.md" [lib] path = "src/lib.rs" diff --git a/nix-bindings-store/README.md b/nix-bindings-store/README.md new file mode 100644 index 0000000..1df35eb --- /dev/null +++ b/nix-bindings-store/README.md @@ -0,0 +1,7 @@ +# nix-bindings-store + +Rust bindings to the Nix store library. + +## Changelog + +See the [nix-bindings-rust changelog](https://github.com/nixops4/nix-bindings-rust/blob/main/CHANGELOG.md). diff --git a/nix-bindings-util-sys/Cargo.toml b/nix-bindings-util-sys/Cargo.toml index e37bf1a..6c38fab 100644 --- a/nix-bindings-util-sys/Cargo.toml +++ b/nix-bindings-util-sys/Cargo.toml @@ -6,6 +6,7 @@ build = "build.rs" license = "LGPL-2.1" description = "Low-level FFI bindings to Nix utility library" repository = "https://github.com/nixops4/nix-bindings-rust" +readme = "README.md" [lib] path = "src/lib.rs" diff --git a/nix-bindings-util-sys/README.md b/nix-bindings-util-sys/README.md index 82debbf..7e762d5 100644 --- a/nix-bindings-util-sys/README.md +++ b/nix-bindings-util-sys/README.md @@ -3,3 +3,7 @@ This crate contains generated bindings for the Nix C API (`nix-util-c`). **You should not have to use this crate directly,** and so you should probably not add it to your dependencies. Instead, use the `nix-bindings-util` crate, which _should_ be sufficient. + +## Changelog + +See the [nix-bindings-rust changelog](https://github.com/nixops4/nix-bindings-rust/blob/main/CHANGELOG.md). diff --git a/nix-bindings-util/Cargo.toml b/nix-bindings-util/Cargo.toml index 6c0d9db..5c68a56 100644 --- a/nix-bindings-util/Cargo.toml +++ b/nix-bindings-util/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" license = "LGPL-2.1" description = "Rust bindings to Nix utility library" repository = "https://github.com/nixops4/nix-bindings-rust" +readme = "README.md" [lib] path = "src/lib.rs" diff --git a/nix-bindings-util/README.md b/nix-bindings-util/README.md new file mode 100644 index 0000000..895b7cf --- /dev/null +++ b/nix-bindings-util/README.md @@ -0,0 +1,7 @@ +# nix-bindings-util + +Rust bindings to the Nix utility library. + +## Changelog + +See the [nix-bindings-rust changelog](https://github.com/nixops4/nix-bindings-rust/blob/main/CHANGELOG.md). From 0b0bd7013e010cb8eb6a3b45ab4f892873b147b0 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 13 Jan 2026 16:53:56 +0100 Subject: [PATCH 242/306] nix-bindings-expr: Add version to build-dep for crates.io cargo publish requires all dependencies to have version requirements. The build-dependency on nix-bindings-util was missing one. --- nix-bindings-expr/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nix-bindings-expr/Cargo.toml b/nix-bindings-expr/Cargo.toml index f72ea6e..c9280e4 100644 --- a/nix-bindings-expr/Cargo.toml +++ b/nix-bindings-expr/Cargo.toml @@ -26,7 +26,7 @@ cstr = "0.2" [build-dependencies] pkg-config = "0.3" -nix-bindings-util = { path = "../nix-bindings-util" } +nix-bindings-util = { path = "../nix-bindings-util", version = "0.2.0" } [lints.rust] warnings = "deny" From db5637a44867822dadf3f3fabe49499c0dc1d2ce Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 13 Jan 2026 17:07:20 +0100 Subject: [PATCH 243/306] Add Unreleased section to CHANGELOG --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c8ea3a..da22679 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + ## [0.2.0] - 2026-01-13 ### Added @@ -78,6 +80,7 @@ Thanks to everyone who contributed to the initial development, some of whom may [#26]: https://github.com/nixops4/nix-bindings-rust/pull/26 [#27]: https://github.com/nixops4/nix-bindings-rust/pull/27 [#36]: https://github.com/nixops4/nix-bindings-rust/pull/36 +[Unreleased]: https://github.com/nixops4/nix-bindings-rust/compare/0.2.0...HEAD [0.2.0]: https://github.com/nixops4/nix-bindings-rust/compare/0.1.0...0.2.0 [0.1.0]: https://github.com/nixops4/nix-bindings-rust/releases/tag/0.1.0 [nix#11979]: https://github.com/NixOS/nix/issues/11979 From 9b2959d57d46b430e1610e22692bc94878d2cd72 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 13 Jan 2026 17:10:54 +0100 Subject: [PATCH 244/306] Add missing description fields to -sys crates --- nix-bindings-bdwgc-sys/Cargo.toml | 1 + nix-bindings-expr-sys/Cargo.toml | 1 + nix-bindings-fetchers-sys/Cargo.toml | 1 + nix-bindings-flake-sys/Cargo.toml | 1 + nix-bindings-store-sys/Cargo.toml | 1 + 5 files changed, 5 insertions(+) diff --git a/nix-bindings-bdwgc-sys/Cargo.toml b/nix-bindings-bdwgc-sys/Cargo.toml index ff4bc1e..4516766 100644 --- a/nix-bindings-bdwgc-sys/Cargo.toml +++ b/nix-bindings-bdwgc-sys/Cargo.toml @@ -4,6 +4,7 @@ version = "0.2.0" edition = "2021" build = "build.rs" license = "LGPL-2.1" +description = "Low-level FFI bindings to the Boehm-Demers-Weiser garbage collector" repository = "https://github.com/nixops4/nix-bindings-rust" readme = "README.md" diff --git a/nix-bindings-expr-sys/Cargo.toml b/nix-bindings-expr-sys/Cargo.toml index 1512326..af12a4e 100644 --- a/nix-bindings-expr-sys/Cargo.toml +++ b/nix-bindings-expr-sys/Cargo.toml @@ -4,6 +4,7 @@ version = "0.2.0" edition = "2021" build = "build.rs" license = "LGPL-2.1" +description = "Low-level FFI bindings to the Nix expression evaluator" repository = "https://github.com/nixops4/nix-bindings-rust" readme = "README.md" diff --git a/nix-bindings-fetchers-sys/Cargo.toml b/nix-bindings-fetchers-sys/Cargo.toml index a4742c9..6986909 100644 --- a/nix-bindings-fetchers-sys/Cargo.toml +++ b/nix-bindings-fetchers-sys/Cargo.toml @@ -4,6 +4,7 @@ version = "0.2.0" edition = "2021" build = "build.rs" license = "LGPL-2.1" +description = "Low-level FFI bindings to the nix-fetchers library" repository = "https://github.com/nixops4/nix-bindings-rust" readme = "README.md" diff --git a/nix-bindings-flake-sys/Cargo.toml b/nix-bindings-flake-sys/Cargo.toml index ecc3e99..a04257c 100644 --- a/nix-bindings-flake-sys/Cargo.toml +++ b/nix-bindings-flake-sys/Cargo.toml @@ -4,6 +4,7 @@ version = "0.2.0" edition = "2021" build = "build.rs" license = "LGPL-2.1" +description = "Low-level FFI bindings to Nix flakes" repository = "https://github.com/nixops4/nix-bindings-rust" readme = "README.md" diff --git a/nix-bindings-store-sys/Cargo.toml b/nix-bindings-store-sys/Cargo.toml index c1e03f2..2cbf9e2 100644 --- a/nix-bindings-store-sys/Cargo.toml +++ b/nix-bindings-store-sys/Cargo.toml @@ -4,6 +4,7 @@ version = "0.2.0" edition = "2021" build = "build.rs" license = "LGPL-2.1" +description = "Low-level FFI bindings to the Nix store library" repository = "https://github.com/nixops4/nix-bindings-rust" readme = "README.md" From 5aff417cacb2612d4dce1c23f1cb69d9cf89b7b9 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 13 Jan 2026 17:39:15 +0100 Subject: [PATCH 245/306] Document release branch workflow --- doc/maintainers/release.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/maintainers/release.md b/doc/maintainers/release.md index 3b0f03b..1555ac0 100644 --- a/doc/maintainers/release.md +++ b/doc/maintainers/release.md @@ -3,6 +3,10 @@ This project uses simple tags, that trigger a release of all crates using Hercules CI. See [HCI Effects cargo publish workflow]. +## Release branch + +Create a `release` branch and PR for release preparation. This allows CI to validate the release before tagging. + ## Before tagging - Update `CHANGELOG.md`: @@ -12,6 +16,9 @@ See [HCI Effects cargo publish workflow]. ## After tagging - Add a new Unreleased section to `CHANGELOG.md` +- Merge the release PR + +--- Dissatisfied with the coarse grained release process? Complain to @roberth and he'll get it done for you. From 9f2c7a2013a208d8eeb7cfad6b763e9b929dc666 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 13 Jan 2026 17:53:44 +0100 Subject: [PATCH 246/306] Bump version to 0.2.1 for development --- Cargo.lock | 22 +++++++++++----------- nix-bindings-bdwgc-sys/Cargo.toml | 2 +- nix-bindings-expr-sys/Cargo.toml | 6 +++--- nix-bindings-expr/Cargo.toml | 16 ++++++++-------- nix-bindings-fetchers-sys/Cargo.toml | 4 ++-- nix-bindings-fetchers/Cargo.toml | 8 ++++---- nix-bindings-flake-sys/Cargo.toml | 12 ++++++------ nix-bindings-flake/Cargo.toml | 12 ++++++------ nix-bindings-store-sys/Cargo.toml | 4 ++-- nix-bindings-store/Cargo.toml | 10 +++++----- nix-bindings-util-sys/Cargo.toml | 2 +- nix-bindings-util/Cargo.toml | 4 ++-- 12 files changed, 51 insertions(+), 51 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d7f2049..01fe2eb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -210,7 +210,7 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "nix-bindings-bdwgc-sys" -version = "0.2.0" +version = "0.2.1" dependencies = [ "bindgen", "pkg-config", @@ -218,7 +218,7 @@ dependencies = [ [[package]] name = "nix-bindings-expr" -version = "0.2.0" +version = "0.2.1" dependencies = [ "anyhow", "cstr", @@ -236,7 +236,7 @@ dependencies = [ [[package]] name = "nix-bindings-expr-sys" -version = "0.2.0" +version = "0.2.1" dependencies = [ "bindgen", "nix-bindings-store-sys", @@ -246,7 +246,7 @@ dependencies = [ [[package]] name = "nix-bindings-fetchers" -version = "0.2.0" +version = "0.2.1" dependencies = [ "anyhow", "cstr", @@ -259,7 +259,7 @@ dependencies = [ [[package]] name = "nix-bindings-fetchers-sys" -version = "0.2.0" +version = "0.2.1" dependencies = [ "bindgen", "nix-bindings-util-sys", @@ -268,7 +268,7 @@ dependencies = [ [[package]] name = "nix-bindings-flake" -version = "0.2.0" +version = "0.2.1" dependencies = [ "anyhow", "cstr", @@ -284,7 +284,7 @@ dependencies = [ [[package]] name = "nix-bindings-flake-sys" -version = "0.2.0" +version = "0.2.1" dependencies = [ "bindgen", "nix-bindings-bdwgc-sys", @@ -297,7 +297,7 @@ dependencies = [ [[package]] name = "nix-bindings-store" -version = "0.2.0" +version = "0.2.1" dependencies = [ "anyhow", "ctor", @@ -311,7 +311,7 @@ dependencies = [ [[package]] name = "nix-bindings-store-sys" -version = "0.2.0" +version = "0.2.1" dependencies = [ "bindgen", "nix-bindings-util-sys", @@ -320,7 +320,7 @@ dependencies = [ [[package]] name = "nix-bindings-util" -version = "0.2.0" +version = "0.2.1" dependencies = [ "anyhow", "ctor", @@ -329,7 +329,7 @@ dependencies = [ [[package]] name = "nix-bindings-util-sys" -version = "0.2.0" +version = "0.2.1" dependencies = [ "bindgen", "pkg-config", diff --git a/nix-bindings-bdwgc-sys/Cargo.toml b/nix-bindings-bdwgc-sys/Cargo.toml index 4516766..734663e 100644 --- a/nix-bindings-bdwgc-sys/Cargo.toml +++ b/nix-bindings-bdwgc-sys/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nix-bindings-bdwgc-sys" -version = "0.2.0" +version = "0.2.1" edition = "2021" build = "build.rs" license = "LGPL-2.1" diff --git a/nix-bindings-expr-sys/Cargo.toml b/nix-bindings-expr-sys/Cargo.toml index af12a4e..4f8e373 100644 --- a/nix-bindings-expr-sys/Cargo.toml +++ b/nix-bindings-expr-sys/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nix-bindings-expr-sys" -version = "0.2.0" +version = "0.2.1" edition = "2021" build = "build.rs" license = "LGPL-2.1" @@ -12,8 +12,8 @@ readme = "README.md" path = "src/lib.rs" [dependencies] -nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.2.0" } -nix-bindings-store-sys = { path = "../nix-bindings-store-sys", version = "0.2.0" } +nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.2.1" } +nix-bindings-store-sys = { path = "../nix-bindings-store-sys", version = "0.2.1" } [build-dependencies] bindgen = "0.69" diff --git a/nix-bindings-expr/Cargo.toml b/nix-bindings-expr/Cargo.toml index c9280e4..616bd96 100644 --- a/nix-bindings-expr/Cargo.toml +++ b/nix-bindings-expr/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nix-bindings-expr" -version = "0.2.0" +version = "0.2.1" edition = "2021" build = "build.rs" license = "LGPL-2.1" @@ -13,12 +13,12 @@ path = "src/lib.rs" [dependencies] anyhow = "1.0" -nix-bindings-store = { path = "../nix-bindings-store", version = "0.2.0" } -nix-bindings-util = { path = "../nix-bindings-util", version = "0.2.0" } -nix-bindings-bdwgc-sys = { path = "../nix-bindings-bdwgc-sys", version = "0.2.0" } -nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.2.0" } -nix-bindings-store-sys = { path = "../nix-bindings-store-sys", version = "0.2.0" } -nix-bindings-expr-sys = { path = "../nix-bindings-expr-sys", version = "0.2.0" } +nix-bindings-store = { path = "../nix-bindings-store", version = "0.2.1" } +nix-bindings-util = { path = "../nix-bindings-util", version = "0.2.1" } +nix-bindings-bdwgc-sys = { path = "../nix-bindings-bdwgc-sys", version = "0.2.1" } +nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.2.1" } +nix-bindings-store-sys = { path = "../nix-bindings-store-sys", version = "0.2.1" } +nix-bindings-expr-sys = { path = "../nix-bindings-expr-sys", version = "0.2.1" } lazy_static = "1.4" ctor = "0.2" tempfile = "3.10" @@ -26,7 +26,7 @@ cstr = "0.2" [build-dependencies] pkg-config = "0.3" -nix-bindings-util = { path = "../nix-bindings-util", version = "0.2.0" } +nix-bindings-util = { path = "../nix-bindings-util", version = "0.2.1" } [lints.rust] warnings = "deny" diff --git a/nix-bindings-fetchers-sys/Cargo.toml b/nix-bindings-fetchers-sys/Cargo.toml index 6986909..99ff741 100644 --- a/nix-bindings-fetchers-sys/Cargo.toml +++ b/nix-bindings-fetchers-sys/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nix-bindings-fetchers-sys" -version = "0.2.0" +version = "0.2.1" edition = "2021" build = "build.rs" license = "LGPL-2.1" @@ -12,7 +12,7 @@ readme = "README.md" path = "src/lib.rs" [dependencies] -nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.2.0" } +nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.2.1" } [build-dependencies] bindgen = "0.69" diff --git a/nix-bindings-fetchers/Cargo.toml b/nix-bindings-fetchers/Cargo.toml index fde4ce5..e0d06cb 100644 --- a/nix-bindings-fetchers/Cargo.toml +++ b/nix-bindings-fetchers/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nix-bindings-fetchers" -version = "0.2.0" +version = "0.2.1" edition = "2021" license = "LGPL-2.1" description = "Rust bindings to Nix fetchers" @@ -12,9 +12,9 @@ path = "src/lib.rs" [dependencies] anyhow = "1.0" -nix-bindings-store = { path = "../nix-bindings-store", version = "0.2.0" } -nix-bindings-util = { path = "../nix-bindings-util", version = "0.2.0" } -nix-bindings-fetchers-sys = { path = "../nix-bindings-fetchers-sys", version = "0.2.0" } +nix-bindings-store = { path = "../nix-bindings-store", version = "0.2.1" } +nix-bindings-util = { path = "../nix-bindings-util", version = "0.2.1" } +nix-bindings-fetchers-sys = { path = "../nix-bindings-fetchers-sys", version = "0.2.1" } ctor = "0.2" tempfile = "3.10" cstr = "0.2" diff --git a/nix-bindings-flake-sys/Cargo.toml b/nix-bindings-flake-sys/Cargo.toml index a04257c..fe83419 100644 --- a/nix-bindings-flake-sys/Cargo.toml +++ b/nix-bindings-flake-sys/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nix-bindings-flake-sys" -version = "0.2.0" +version = "0.2.1" edition = "2021" build = "build.rs" license = "LGPL-2.1" @@ -12,11 +12,11 @@ readme = "README.md" path = "src/lib.rs" [dependencies] -nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.2.0" } -nix-bindings-store-sys = { path = "../nix-bindings-store-sys", version = "0.2.0" } -nix-bindings-expr-sys = { path = "../nix-bindings-expr-sys", version = "0.2.0" } -nix-bindings-fetchers-sys = { path = "../nix-bindings-fetchers-sys", version = "0.2.0" } -nix-bindings-bdwgc-sys = { path = "../nix-bindings-bdwgc-sys", version = "0.2.0" } +nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.2.1" } +nix-bindings-store-sys = { path = "../nix-bindings-store-sys", version = "0.2.1" } +nix-bindings-expr-sys = { path = "../nix-bindings-expr-sys", version = "0.2.1" } +nix-bindings-fetchers-sys = { path = "../nix-bindings-fetchers-sys", version = "0.2.1" } +nix-bindings-bdwgc-sys = { path = "../nix-bindings-bdwgc-sys", version = "0.2.1" } [build-dependencies] bindgen = "0.69" diff --git a/nix-bindings-flake/Cargo.toml b/nix-bindings-flake/Cargo.toml index 30a426d..5222133 100644 --- a/nix-bindings-flake/Cargo.toml +++ b/nix-bindings-flake/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nix-bindings-flake" -version = "0.2.0" +version = "0.2.1" edition = "2021" license = "LGPL-2.1" description = "Rust bindings to Nix flakes" @@ -12,11 +12,11 @@ path = "src/lib.rs" [dependencies] anyhow = "1.0" -nix-bindings-expr = { path = "../nix-bindings-expr", version = "0.2.0" } -nix-bindings-fetchers = { path = "../nix-bindings-fetchers", version = "0.2.0" } -nix-bindings-store = { path = "../nix-bindings-store", version = "0.2.0" } -nix-bindings-util = { path = "../nix-bindings-util", version = "0.2.0" } -nix-bindings-flake-sys = { path = "../nix-bindings-flake-sys", version = "0.2.0" } +nix-bindings-expr = { path = "../nix-bindings-expr", version = "0.2.1" } +nix-bindings-fetchers = { path = "../nix-bindings-fetchers", version = "0.2.1" } +nix-bindings-store = { path = "../nix-bindings-store", version = "0.2.1" } +nix-bindings-util = { path = "../nix-bindings-util", version = "0.2.1" } +nix-bindings-flake-sys = { path = "../nix-bindings-flake-sys", version = "0.2.1" } lazy_static = "1.4" ctor = "0.2" tempfile = "3.10" diff --git a/nix-bindings-store-sys/Cargo.toml b/nix-bindings-store-sys/Cargo.toml index 2cbf9e2..3bc2bb5 100644 --- a/nix-bindings-store-sys/Cargo.toml +++ b/nix-bindings-store-sys/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nix-bindings-store-sys" -version = "0.2.0" +version = "0.2.1" edition = "2021" build = "build.rs" license = "LGPL-2.1" @@ -12,7 +12,7 @@ readme = "README.md" path = "src/lib.rs" [dependencies] -nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.2.0" } +nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.2.1" } [build-dependencies] bindgen = "0.69" diff --git a/nix-bindings-store/Cargo.toml b/nix-bindings-store/Cargo.toml index 8a018dd..c665a0e 100644 --- a/nix-bindings-store/Cargo.toml +++ b/nix-bindings-store/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nix-bindings-store" -version = "0.2.0" +version = "0.2.1" edition = "2021" build = "build.rs" license = "LGPL-2.1" @@ -13,9 +13,9 @@ path = "src/lib.rs" [dependencies] anyhow = "1.0" -nix-bindings-util = { path = "../nix-bindings-util", version = "0.2.0" } -nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.2.0" } -nix-bindings-store-sys = { path = "../nix-bindings-store-sys", version = "0.2.0" } +nix-bindings-util = { path = "../nix-bindings-util", version = "0.2.1" } +nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.2.1" } +nix-bindings-store-sys = { path = "../nix-bindings-store-sys", version = "0.2.1" } lazy_static = "1.4" [dev-dependencies] @@ -25,7 +25,7 @@ tempfile = "3.10" [build-dependencies] pkg-config = "0.3" # Needed for version parsing in build.rs -nix-bindings-util = { path = "../nix-bindings-util", version = "0.2.0" } +nix-bindings-util = { path = "../nix-bindings-util", version = "0.2.1" } [lints.rust] warnings = "deny" diff --git a/nix-bindings-util-sys/Cargo.toml b/nix-bindings-util-sys/Cargo.toml index 6c38fab..7a0d01c 100644 --- a/nix-bindings-util-sys/Cargo.toml +++ b/nix-bindings-util-sys/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nix-bindings-util-sys" -version = "0.2.0" +version = "0.2.1" edition = "2021" build = "build.rs" license = "LGPL-2.1" diff --git a/nix-bindings-util/Cargo.toml b/nix-bindings-util/Cargo.toml index 5c68a56..6a32fb1 100644 --- a/nix-bindings-util/Cargo.toml +++ b/nix-bindings-util/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nix-bindings-util" -version = "0.2.0" +version = "0.2.1" edition = "2021" license = "LGPL-2.1" description = "Rust bindings to Nix utility library" @@ -12,7 +12,7 @@ path = "src/lib.rs" [dependencies] anyhow = "1.0" -nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.2.0" } +nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.2.1" } [dev-dependencies] ctor = "0.2" From b8c741dfe954575f8d8ba8b1397083c96677bc98 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 13 Jan 2026 17:59:56 +0100 Subject: [PATCH 247/306] Document release process in full Adopted the complete workflow from the HCI Effects cargo publish documentation rather than referencing it, making the steps explicit and self-contained. --- doc/maintainers/release.md | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/doc/maintainers/release.md b/doc/maintainers/release.md index 1555ac0..2d04d96 100644 --- a/doc/maintainers/release.md +++ b/doc/maintainers/release.md @@ -1,22 +1,20 @@ # Release process This project uses simple tags, that trigger a release of all crates using Hercules CI. -See [HCI Effects cargo publish workflow]. +Based on the [HCI Effects cargo publish workflow]. -## Release branch +## Steps -Create a `release` branch and PR for release preparation. This allows CI to validate the release before tagging. - -## Before tagging - -- Update `CHANGELOG.md`: - - Make sure the Unreleased section is up to date - - Change it to the new version and release date - -## After tagging - -- Add a new Unreleased section to `CHANGELOG.md` -- Merge the release PR +1. Create a `release` branch +2. Decide the version bump (patch for fixes, minor for features, major for breaking changes) +3. Update `CHANGELOG.md`: make sure the Unreleased section is up to date, then change it to the new version and release date +4. Open a draft release PR and wait for CI to pass +5. Create and push a tag matching the version +6. Add a new Unreleased section to `CHANGELOG.md` +7. Bump version in all `Cargo.toml` files to the next patch version (e.g., `0.2.0` → `0.2.1`) + and run `cargo update --workspace` to update `Cargo.lock`, + so that `cargo publish --dry-run` passes on subsequent commits +8. Merge the release PR --- From ab64f871609b0b456c7130b98704502734ff287c Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 14 Jan 2026 15:19:47 -0500 Subject: [PATCH 248/306] Fix `cargo doc` --- nix-bindings-bdwgc-sys/src/lib.rs | 27 +++++++++++++++++++++++++-- nix-bindings-expr-sys/src/lib.rs | 28 ++++++++++++++++++++++++++-- nix-bindings-fetchers-sys/src/lib.rs | 28 ++++++++++++++++++++++++++-- nix-bindings-flake-sys/src/lib.rs | 28 ++++++++++++++++++++++++++-- nix-bindings-store-sys/src/lib.rs | 28 ++++++++++++++++++++++++++-- nix-bindings-store/src/store.rs | 2 +- nix-bindings-util-sys/src/lib.rs | 2 +- 7 files changed, 131 insertions(+), 12 deletions(-) diff --git a/nix-bindings-bdwgc-sys/src/lib.rs b/nix-bindings-bdwgc-sys/src/lib.rs index 0cf64d2..e722c20 100644 --- a/nix-bindings-bdwgc-sys/src/lib.rs +++ b/nix-bindings-bdwgc-sys/src/lib.rs @@ -1,7 +1,30 @@ +//! Raw bindings to Nix C API +//! +//! This crate contains automatically generated bindings from the Nix C headers. +//! The bindings are generated by bindgen and include C-style naming conventions +//! and documentation comments that don't always conform to Rust standards. +//! +//! Normally you don't have to use this crate directly. + +// This file must only contain generated code, so that the module-level +// #![allow(...)] attributes don't suppress warnings in hand-written code. +// If you need to add hand-written code, use a submodule to isolate the +// generated code. See: +// https://github.com/nixops4/nixops4/pull/138/commits/330c3881be3d3cf3e59adebbe0ab1c0f15f6d2c9 + +// Standard bindgen suppressions for C naming conventions #![allow(non_upper_case_globals)] #![allow(non_camel_case_types)] #![allow(non_snake_case)] -#![allow(dead_code)] -#![allow(clippy::all)] +// Clippy suppressions for generated C bindings +// bindgen doesn't generate safety docs +#![allow(clippy::missing_safety_doc)] +// Rustdoc suppressions for generated C documentation +// The C headers contain Doxygen-style documentation that doesn't translate +// well to Rust's rustdoc format, causing various warnings: +#![allow(rustdoc::broken_intra_doc_links)] // @param[in]/[out] references don't resolve +#![allow(rustdoc::bare_urls)] // C docs may contain unescaped URLs +#![allow(rustdoc::invalid_html_tags)] // Doxygen HTML tags like +#![allow(rustdoc::invalid_codeblock_attributes)] // C code examples may use unsupported attributes include!(concat!(env!("OUT_DIR"), "/bindings.rs")); diff --git a/nix-bindings-expr-sys/src/lib.rs b/nix-bindings-expr-sys/src/lib.rs index afd13d3..739f706 100644 --- a/nix-bindings-expr-sys/src/lib.rs +++ b/nix-bindings-expr-sys/src/lib.rs @@ -1,8 +1,32 @@ +//! Raw bindings to Nix C API +//! +//! This crate contains automatically generated bindings from the Nix C headers. +//! The bindings are generated by bindgen and include C-style naming conventions +//! and documentation comments that don't always conform to Rust standards. +//! +//! Normally you don't have to use this crate directly. +//! Instead use `nix-expr`. + +// This file must only contain generated code, so that the module-level +// #![allow(...)] attributes don't suppress warnings in hand-written code. +// If you need to add hand-written code, use a submodule to isolate the +// generated code. See: +// https://github.com/nixops4/nixops4/pull/138/commits/330c3881be3d3cf3e59adebbe0ab1c0f15f6d2c9 + +// Standard bindgen suppressions for C naming conventions #![allow(non_upper_case_globals)] #![allow(non_camel_case_types)] #![allow(non_snake_case)] -#![allow(dead_code)] -#![allow(clippy::all)] +// Clippy suppressions for generated C bindings +// bindgen doesn't generate safety docs +#![allow(clippy::missing_safety_doc)] +// Rustdoc suppressions for generated C documentation +// The C headers contain Doxygen-style documentation that doesn't translate +// well to Rust's rustdoc format, causing various warnings: +#![allow(rustdoc::broken_intra_doc_links)] // @param[in]/[out] references don't resolve +#![allow(rustdoc::bare_urls)] // C docs may contain unescaped URLs +#![allow(rustdoc::invalid_html_tags)] // Doxygen HTML tags like +#![allow(rustdoc::invalid_codeblock_attributes)] // C code examples may use unsupported attributes use nix_bindings_store_sys::*; use nix_bindings_util_sys::*; diff --git a/nix-bindings-fetchers-sys/src/lib.rs b/nix-bindings-fetchers-sys/src/lib.rs index 4c5c9fe..97a30ed 100644 --- a/nix-bindings-fetchers-sys/src/lib.rs +++ b/nix-bindings-fetchers-sys/src/lib.rs @@ -1,8 +1,32 @@ +//! Raw bindings to Nix C API +//! +//! This crate contains automatically generated bindings from the Nix C headers. +//! The bindings are generated by bindgen and include C-style naming conventions +//! and documentation comments that don't always conform to Rust standards. +//! +//! Normally you don't have to use this crate directly. +//! Instead use `nix-fetchers`. + +// This file must only contain generated code, so that the module-level +// #![allow(...)] attributes don't suppress warnings in hand-written code. +// If you need to add hand-written code, use a submodule to isolate the +// generated code. See: +// https://github.com/nixops4/nixops4/pull/138/commits/330c3881be3d3cf3e59adebbe0ab1c0f15f6d2c9 + +// Standard bindgen suppressions for C naming conventions #![allow(non_upper_case_globals)] #![allow(non_camel_case_types)] #![allow(non_snake_case)] -#![allow(dead_code)] -#![allow(clippy::all)] +// Clippy suppressions for generated C bindings +// bindgen doesn't generate safety docs +#![allow(clippy::missing_safety_doc)] +// Rustdoc suppressions for generated C documentation +// The C headers contain Doxygen-style documentation that doesn't translate +// well to Rust's rustdoc format, causing various warnings: +#![allow(rustdoc::broken_intra_doc_links)] // @param[in]/[out] references don't resolve +#![allow(rustdoc::bare_urls)] // C docs may contain unescaped URLs +#![allow(rustdoc::invalid_html_tags)] // Doxygen HTML tags like +#![allow(rustdoc::invalid_codeblock_attributes)] // C code examples may use unsupported attributes use nix_bindings_util_sys::*; diff --git a/nix-bindings-flake-sys/src/lib.rs b/nix-bindings-flake-sys/src/lib.rs index 1bcac64..2e9209b 100644 --- a/nix-bindings-flake-sys/src/lib.rs +++ b/nix-bindings-flake-sys/src/lib.rs @@ -1,8 +1,32 @@ +//! Raw bindings to Nix C API +//! +//! This crate contains automatically generated bindings from the Nix C headers. +//! The bindings are generated by bindgen and include C-style naming conventions +//! and documentation comments that don't always conform to Rust standards. +//! +//! Normally you don't have to use this crate directly. +//! Instead use `nix-flake`. + +// This file must only contain generated code, so that the module-level +// #![allow(...)] attributes don't suppress warnings in hand-written code. +// If you need to add hand-written code, use a submodule to isolate the +// generated code. See: +// https://github.com/nixops4/nixops4/pull/138/commits/330c3881be3d3cf3e59adebbe0ab1c0f15f6d2c9 + +// Standard bindgen suppressions for C naming conventions #![allow(non_upper_case_globals)] #![allow(non_camel_case_types)] #![allow(non_snake_case)] -#![allow(dead_code)] -#![allow(clippy::all)] +// Clippy suppressions for generated C bindings +// bindgen doesn't generate safety docs +#![allow(clippy::missing_safety_doc)] +// Rustdoc suppressions for generated C documentation +// The C headers contain Doxygen-style documentation that doesn't translate +// well to Rust's rustdoc format, causing various warnings: +#![allow(rustdoc::broken_intra_doc_links)] // @param[in]/[out] references don't resolve +#![allow(rustdoc::bare_urls)] // C docs may contain unescaped URLs +#![allow(rustdoc::invalid_html_tags)] // Doxygen HTML tags like +#![allow(rustdoc::invalid_codeblock_attributes)] // C code examples may use unsupported attributes use nix_bindings_expr_sys::*; use nix_bindings_fetchers_sys::*; diff --git a/nix-bindings-store-sys/src/lib.rs b/nix-bindings-store-sys/src/lib.rs index 4c5c9fe..eb41931 100644 --- a/nix-bindings-store-sys/src/lib.rs +++ b/nix-bindings-store-sys/src/lib.rs @@ -1,8 +1,32 @@ +//! Raw bindings to Nix C API +//! +//! This crate contains automatically generated bindings from the Nix C headers. +//! The bindings are generated by bindgen and include C-style naming conventions +//! and documentation comments that don't always conform to Rust standards. +//! +//! Normally you don't have to use this crate directly. +//! Instead use `nix-store`. + +// This file must only contain generated code, so that the module-level +// #![allow(...)] attributes don't suppress warnings in hand-written code. +// If you need to add hand-written code, use a submodule to isolate the +// generated code. See: +// https://github.com/nixops4/nixops4/pull/138/commits/330c3881be3d3cf3e59adebbe0ab1c0f15f6d2c9 + +// Standard bindgen suppressions for C naming conventions #![allow(non_upper_case_globals)] #![allow(non_camel_case_types)] #![allow(non_snake_case)] -#![allow(dead_code)] -#![allow(clippy::all)] +// Clippy suppressions for generated C bindings +// bindgen doesn't generate safety docs +#![allow(clippy::missing_safety_doc)] +// Rustdoc suppressions for generated C documentation +// The C headers contain Doxygen-style documentation that doesn't translate +// well to Rust's rustdoc format, causing various warnings: +#![allow(rustdoc::broken_intra_doc_links)] // @param[in]/[out] references don't resolve +#![allow(rustdoc::bare_urls)] // C docs may contain unescaped URLs +#![allow(rustdoc::invalid_html_tags)] // Doxygen HTML tags like +#![allow(rustdoc::invalid_codeblock_attributes)] // C code examples may use unsupported attributes use nix_bindings_util_sys::*; diff --git a/nix-bindings-store/src/store.rs b/nix-bindings-store/src/store.rs index d884c69..11ea0e1 100644 --- a/nix-bindings-store/src/store.rs +++ b/nix-bindings-store/src/store.rs @@ -102,7 +102,7 @@ pub struct Store { impl Store { /// Open a store. /// - /// See [`nix_bindings_util_sys::store_open`] for more information. + /// See [`nix_bindings_store_sys::store_open`] for more information. #[doc(alias = "nix_store_open")] pub fn open<'a, 'b>( url: Option<&str>, diff --git a/nix-bindings-util-sys/src/lib.rs b/nix-bindings-util-sys/src/lib.rs index 63f3849..b7c06dc 100644 --- a/nix-bindings-util-sys/src/lib.rs +++ b/nix-bindings-util-sys/src/lib.rs @@ -5,7 +5,7 @@ //! and documentation comments that don't always conform to Rust standards. //! //! Normally you don't have to use this crate directly. -//! Instead use `nix-store` and `nix-expr`. +//! Instead use `nix-util`. // This file must only contain generated code, so that the module-level // #![allow(...)] attributes don't suppress warnings in hand-written code. From 0eb73977efa184711647849791ebdb4da2cc65a8 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 26 Nov 2025 22:11:43 -0500 Subject: [PATCH 249/306] Add zerocopy instance to raw bindings struct This will come in handy later, when we integrate harmonia. It is a very stable thing to do. --- Cargo.lock | 21 +++++++++++++++++++++ nix-bindings-store-sys/Cargo.toml | 1 + nix-bindings-store-sys/build.rs | 17 +++++++++++++++++ 3 files changed, 39 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 01fe2eb..f3d9634 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -316,6 +316,7 @@ dependencies = [ "bindgen", "nix-bindings-util-sys", "pkg-config", + "zerocopy", ] [[package]] @@ -611,3 +612,23 @@ name = "wit-bindgen" version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + +[[package]] +name = "zerocopy" +version = "0.8.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea879c944afe8a2b25fef16bb4ba234f47c694565e97383b36f3a878219065c" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf955aa904d6040f70dc8e9384444cb1030aed272ba3cb09bbc4ab9e7c1f34f5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/nix-bindings-store-sys/Cargo.toml b/nix-bindings-store-sys/Cargo.toml index 3bc2bb5..f9f5ee2 100644 --- a/nix-bindings-store-sys/Cargo.toml +++ b/nix-bindings-store-sys/Cargo.toml @@ -13,6 +13,7 @@ path = "src/lib.rs" [dependencies] nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.2.1" } +zerocopy = { version = "0.8", features = ["derive"] } [build-dependencies] bindgen = "0.69" diff --git a/nix-bindings-store-sys/build.rs b/nix-bindings-store-sys/build.rs index bafdfae..8702756 100644 --- a/nix-bindings-store-sys/build.rs +++ b/nix-bindings-store-sys/build.rs @@ -9,6 +9,22 @@ impl bindgen::callbacks::ParseCallbacks for StripNixPrefix { } } +#[derive(Debug)] +struct AddZerocopyDerives {} +impl bindgen::callbacks::ParseCallbacks for AddZerocopyDerives { + fn add_derives(&self, info: &bindgen::callbacks::DeriveInfo<'_>) -> Vec { + if info.name == "store_path_hash_part" { + vec![ + "zerocopy::FromBytes".to_string(), + "zerocopy::IntoBytes".to_string(), + "zerocopy::Immutable".to_string(), + ] + } else { + vec![] + } + } +} + fn main() { println!("cargo:rerun-if-changed=include/nix-c-store.h"); println!("cargo:rustc-link-lib=nixstorec"); @@ -29,6 +45,7 @@ fn main() { .clang_args(args) .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) .parse_callbacks(Box::new(StripNixPrefix)) + .parse_callbacks(Box::new(AddZerocopyDerives {})) // Blocklist symbols from nix-bindings-util-sys .blocklist_file(".*nix_api_util\\.h") .generate() From 65abededcadec096e356ce3bf8c66bed9e7b1e5c Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 15 Dec 2025 20:54:28 -0500 Subject: [PATCH 250/306] Add new `StorePath` bindings --- Cargo.lock | 8 +++++ nix-bindings-store/Cargo.toml | 2 ++ nix-bindings-store/src/path.rs | 62 +++++++++++++++++++++++++++++++++- 3 files changed, 71 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index f3d9634..4577bc4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -132,6 +132,12 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + [[package]] name = "home" version = "0.5.11" @@ -301,12 +307,14 @@ version = "0.2.1" dependencies = [ "anyhow", "ctor", + "hex-literal", "lazy_static", "nix-bindings-store-sys", "nix-bindings-util", "nix-bindings-util-sys", "pkg-config", "tempfile", + "zerocopy", ] [[package]] diff --git a/nix-bindings-store/Cargo.toml b/nix-bindings-store/Cargo.toml index c665a0e..c37d62d 100644 --- a/nix-bindings-store/Cargo.toml +++ b/nix-bindings-store/Cargo.toml @@ -17,9 +17,11 @@ nix-bindings-util = { path = "../nix-bindings-util", version = "0.2.1" } nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.2.1" } nix-bindings-store-sys = { path = "../nix-bindings-store-sys", version = "0.2.1" } lazy_static = "1.4" +zerocopy = "0.8" [dev-dependencies] ctor = "0.2" +hex-literal = "0.4" tempfile = "3.10" [build-dependencies] diff --git a/nix-bindings-store/src/path.rs b/nix-bindings-store/src/path.rs index 51469af..e3be645 100644 --- a/nix-bindings-store/src/path.rs +++ b/nix-bindings-store/src/path.rs @@ -1,15 +1,21 @@ use std::ptr::NonNull; -use anyhow::Result; +use anyhow::{Context as _, Result}; use nix_bindings_store_sys as raw; +#[cfg(nix_at_least = "2.33")] +use nix_bindings_util::{check_call, context::Context}; use nix_bindings_util::{ result_string_init, string_return::{callback_get_result_string, callback_get_result_string_data}, }; +/// The size of a store path hash in bytes (20 bytes, decoded from nix32). +pub const STORE_PATH_HASH_SIZE: usize = 20; + pub struct StorePath { raw: NonNull, } + impl StorePath { /// Get the name of the store path. /// @@ -26,6 +32,43 @@ impl StorePath { } } + /// Get the hash part of the store path. + /// + /// This returns the decoded hash (not the nix32-encoded string). + #[cfg(nix_at_least = "2.33")] + pub fn hash(&self) -> Result<[u8; STORE_PATH_HASH_SIZE]> { + let mut result = [0u8; STORE_PATH_HASH_SIZE]; + let hash_part: &mut raw::store_path_hash_part = zerocopy::transmute_mut!(&mut result); + + let mut ctx = Context::new(); + + unsafe { + check_call!(raw::store_path_hash(&mut ctx, self.as_ptr(), hash_part))?; + } + Ok(result) + } + + /// Create a StorePath from hash and name components. + #[cfg(nix_at_least = "2.33")] + pub fn from_parts(hash: &[u8; STORE_PATH_HASH_SIZE], name: &str) -> Result { + let hash_part: &raw::store_path_hash_part = zerocopy::transmute_ref!(hash); + + let mut ctx = Context::new(); + + let out_path = unsafe { + check_call!(raw::store_create_from_parts( + &mut ctx, + hash_part, + name.as_ptr() as *const i8, + name.len() + ))? + }; + + NonNull::new(out_path) + .map(|ptr| unsafe { Self::new_raw(ptr) }) + .context("store_create_from_parts returned null") + } + /// This is a low level function that you shouldn't have to call unless you are developing the Nix bindings. /// /// Construct a new `StorePath` by first cloning the C store path. @@ -81,6 +124,9 @@ impl Drop for StorePath { #[cfg(test)] mod tests { + use super::*; + use hex_literal::hex; + #[test] #[cfg(nix_at_least = "2.26" /* get_storedir */)] fn store_path_name() { @@ -91,4 +137,18 @@ mod tests { let store_path = store.parse_store_path(store_path_string.as_str()).unwrap(); assert_eq!(store_path.name().unwrap(), "bash-interactive-5.2p26"); } + + #[test] + #[cfg(nix_at_least = "2.33")] + fn store_path_round_trip() { + let original_hash: [u8; STORE_PATH_HASH_SIZE] = + hex!("0123456789abcdef0011223344556677deadbeef"); + let original_name = "foo.drv"; + + let store_path = StorePath::from_parts(&original_hash, original_name).unwrap(); + + // Round trip gets back what we started with + assert_eq!(store_path.hash().unwrap(), original_hash); + assert_eq!(store_path.name().unwrap(), original_name); + } } From 6619b881b38842ea0d7dee7f9620321b6108d215 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 14 Jan 2026 21:47:02 +0100 Subject: [PATCH 251/306] Assert STORE_PATH_HASH_SIZE matches C struct `zerocopy` is safe enough, but it's good to be explicit about this. --- nix-bindings-store/src/path.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nix-bindings-store/src/path.rs b/nix-bindings-store/src/path.rs index e3be645..41f7f5b 100644 --- a/nix-bindings-store/src/path.rs +++ b/nix-bindings-store/src/path.rs @@ -12,6 +12,9 @@ use nix_bindings_util::{ /// The size of a store path hash in bytes (20 bytes, decoded from nix32). pub const STORE_PATH_HASH_SIZE: usize = 20; +#[cfg(nix_at_least = "2.33")] +const _: () = assert!(std::mem::size_of::() == STORE_PATH_HASH_SIZE); + pub struct StorePath { raw: NonNull, } From c43a67478dc0d3ced5280d4e6c1ee56726bb087f Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 15 Jan 2026 18:52:58 +0100 Subject: [PATCH 252/306] doc: Shared Value state --- nix-bindings-expr/src/eval_state.rs | 4 ++++ nix-bindings-expr/src/value.rs | 15 +++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/nix-bindings-expr/src/eval_state.rs b/nix-bindings-expr/src/eval_state.rs index 138b3be..c05ae9b 100644 --- a/nix-bindings-expr/src/eval_state.rs +++ b/nix-bindings-expr/src/eval_state.rs @@ -442,6 +442,8 @@ impl EvalState { /// Converts [thunks](https://nix.dev/manual/nix/latest/language/evaluation.html#laziness) to their evaluated form. Does not modify already-evaluated values. /// /// Does not perform deep evaluation of nested structures. + /// + /// See also: [Shared Evaluation State](Value#shared-evaluation-state) #[doc(alias = "evaluate")] #[doc(alias = "strict")] pub fn force(&mut self, v: &Value) -> Result<()> { @@ -460,6 +462,8 @@ impl EvalState { /// Returns [`None`] if the value is an unevaluated [thunk](https://nix.dev/manual/nix/latest/language/evaluation.html#laziness). /// /// Returns [`Some`] if the value is already evaluated. + /// + /// See also: [Shared Evaluation State](Value#shared-evaluation-state) #[doc(alias = "type_of")] #[doc(alias = "value_type_lazy")] #[doc(alias = "nix_get_type")] diff --git a/nix-bindings-expr/src/value.rs b/nix-bindings-expr/src/value.rs index e75c4df..68bb444 100644 --- a/nix-bindings-expr/src/value.rs +++ b/nix-bindings-expr/src/value.rs @@ -65,6 +65,21 @@ impl ValueType { } /// A pointer to a [value](https://nix.dev/manual/nix/latest/language/types.html) or [thunk](https://nix.dev/manual/nix/2.31/language/evaluation.html?highlight=thunk#laziness), to be used with [`EvalState`][`crate::eval_state::EvalState`] methods. +/// +/// # Shared Evaluation State +/// +/// Multiple `Value` instances can reference the same underlying Nix value. +/// This occurs when a `Value` is [cloned](Clone), or when multiple Nix +/// expressions reference the same binding. +/// +/// When any reference to a thunk is evaluated—whether through +/// [`force`](crate::eval_state::EvalState::force), other `EvalState` methods, +/// or indirectly as a consequence of evaluating something else—all references +/// observe the evaluated result. This means +/// [`value_type_unforced`](crate::eval_state::EvalState::value_type_unforced) +/// can return `None` (thunk) initially but a specific type later, even without +/// directly operating on that `Value`. The state will not regress back to a +/// less determined state. pub struct Value { inner: NonNull, } From c62dabf9596f4f0530b1fa06d8b43f8210faf73c Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 17 Jan 2026 16:27:02 +0100 Subject: [PATCH 253/306] dev: pre-commit.{installationScript -> shellHook} Installs the file and pre-commit command on PATH in one go. --- dev/flake-module.nix | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dev/flake-module.nix b/dev/flake-module.nix index 0cbaa8f..da4f1d9 100644 --- a/dev/flake-module.nix +++ b/dev/flake-module.nix @@ -81,7 +81,6 @@ config.packages.nix ]; nativeBuildInputs = [ - pkgs.pre-commit config.treefmt.build.wrapper pkgs.rust-analyzer @@ -98,7 +97,7 @@ # pkgs.cargo-valgrind ]; shellHook = '' - ${config.pre-commit.installationScript} + ${config.pre-commit.shellHook} echo 1>&2 "Welcome to the development shell!" ''; # rust-analyzer needs a NIX_PATH for some reason From 43252576c2c35445837fc800a75eac2a3205e732 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 23 Jan 2026 21:38:29 -0500 Subject: [PATCH 254/306] `Derivation::to_json_string` and test Use `serde_json` at test time. --- Cargo.lock | 55 +++++ nix-bindings-store/Cargo.toml | 1 + nix-bindings-store/src/derivation.rs | 29 +++ nix-bindings-store/src/store.rs | 290 ++++++++++++++------------- 4 files changed, 234 insertions(+), 141 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4577bc4..58e41a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -156,6 +156,12 @@ dependencies = [ "either", ] +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + [[package]] name = "lazy_static" version = "1.5.0" @@ -313,6 +319,7 @@ dependencies = [ "nix-bindings-util", "nix-bindings-util-sys", "pkg-config", + "serde_json", "tempfile", "zerocopy", ] @@ -461,6 +468,48 @@ dependencies = [ "windows-sys 0.61.1", ] +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + [[package]] name = "shlex" version = "1.3.0" @@ -640,3 +689,9 @@ dependencies = [ "quote", "syn", ] + +[[package]] +name = "zmij" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfcd145825aace48cff44a8844de64bf75feec3080e0aa5cdbde72961ae51a65" diff --git a/nix-bindings-store/Cargo.toml b/nix-bindings-store/Cargo.toml index c37d62d..2e83ab4 100644 --- a/nix-bindings-store/Cargo.toml +++ b/nix-bindings-store/Cargo.toml @@ -23,6 +23,7 @@ zerocopy = "0.8" ctor = "0.2" hex-literal = "0.4" tempfile = "3.10" +serde_json = "1.0" [build-dependencies] pkg-config = "0.3" diff --git a/nix-bindings-store/src/derivation.rs b/nix-bindings-store/src/derivation.rs index a1ee1c6..ac4c265 100644 --- a/nix-bindings-store/src/derivation.rs +++ b/nix-bindings-store/src/derivation.rs @@ -1,6 +1,13 @@ #![cfg(nix_at_least = "2.33.0pre")] use nix_bindings_store_sys as raw; +#[cfg(nix_at_least = "2.33")] +use nix_bindings_util::{ + check_call, + context::Context, + result_string_init, + string_return::{callback_get_result_string, callback_get_result_string_data}, +}; use std::ptr::NonNull; /// A Nix derivation @@ -15,6 +22,28 @@ impl Derivation { Derivation { inner } } + /// Convert the derivation to JSON (which is encoded to a string). + /// + /// **Requires Nix 2.33 or later.** + /// + /// The JSON format follows the [Nix derivation JSON schema](https://nix.dev/manual/nix/latest/protocols/json/derivation.html). + /// Note that this format is experimental as of writing. + #[cfg(nix_at_least = "2.33")] + pub fn to_json_string(&self) -> anyhow::Result { + let mut ctx = Context::new(); + + unsafe { + let mut r = result_string_init!(); + check_call!(raw::derivation_to_json( + &mut ctx, + self.inner.as_ptr(), + Some(callback_get_result_string), + callback_get_result_string_data(&mut r) + ))?; + r + } + } + /// This is a low level function that you shouldn't have to call unless you are developing the Nix bindings. /// /// Construct a new `Derivation` by first cloning the C derivation. diff --git a/nix-bindings-store/src/store.rs b/nix-bindings-store/src/store.rs index 11ea0e1..7f231f0 100644 --- a/nix-bindings-store/src/store.rs +++ b/nix-bindings-store/src/store.rs @@ -572,37 +572,34 @@ mod tests { } #[cfg(nix_at_least = "2.33")] - fn create_test_derivation_json() -> String { + fn create_test_derivation_json() -> serde_json::Value { let system = current_system().unwrap_or_else(|_| { // Fallback to Rust's platform detection format!("{}-{}", std::env::consts::ARCH, std::env::consts::OS) }); - format!( - r#"{{ - "args": ["-c", "echo $name foo > $out"], + serde_json::json!({ + "args": ["-c", "echo $name foo > $out"], + "builder": "/bin/sh", + "env": { "builder": "/bin/sh", - "env": {{ - "builder": "/bin/sh", - "name": "myname", - "out": "/1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9", - "system": "{}" - }}, - "inputs": {{ - "drvs": {{}}, - "srcs": [] - }}, "name": "myname", - "outputs": {{ - "out": {{ + "out": "/1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9", + "system": system + }, + "inputs": { + "drvs": {}, + "srcs": [] + }, + "name": "myname", + "outputs": { + "out": { "hashAlgo": "sha256", "method": "nar" - }} - }}, - "system": "{}", - "version": 4 - }}"#, - system, system - ) + } + }, + "system": system, + "version": 4 + }) } #[test] @@ -610,7 +607,7 @@ mod tests { fn derivation_from_json() { let (mut store, temp_dir) = create_temp_store(); let drv_json = create_test_derivation_json(); - let drv = store.derivation_from_json(&drv_json).unwrap(); + let drv = store.derivation_from_json(&drv_json.to_string()).unwrap(); // If we got here, parsing succeeded drop(drv); drop(store); @@ -627,12 +624,35 @@ mod tests { drop(temp_dir); } + #[test] + #[cfg(nix_at_least = "2.33")] + fn derivation_to_json_round_trip() { + let (mut store, _temp_dir) = create_temp_store(); + let original_value = create_test_derivation_json(); + + // Parse JSON to Derivation + let drv = store + .derivation_from_json(&original_value.to_string()) + .unwrap(); + + // Convert back to JSON + let round_trip_json = drv.to_json_string().unwrap(); + let round_trip_value: serde_json::Value = serde_json::from_str(&round_trip_json).unwrap(); + + // Verify the round-trip JSON matches the original + assert_eq!( + original_value, round_trip_value, + "Round-trip JSON should match original.\nOriginal: {}\nRound-trip: {}", + original_value, round_trip_value + ); + } + #[test] #[cfg(nix_at_least = "2.33")] fn add_derivation() { let (mut store, temp_dir) = create_temp_store(); let drv_json = create_test_derivation_json(); - let drv = store.derivation_from_json(&drv_json).unwrap(); + let drv = store.derivation_from_json(&drv_json.to_string()).unwrap(); let drv_path = store.add_derivation(&drv).unwrap(); // Verify we got a .drv path @@ -648,7 +668,7 @@ mod tests { fn realise() { let (mut store, temp_dir) = create_temp_store(); let drv_json = create_test_derivation_json(); - let drv = store.derivation_from_json(&drv_json).unwrap(); + let drv = store.derivation_from_json(&drv_json.to_string()).unwrap(); let drv_path = store.add_derivation(&drv).unwrap(); // Build the derivation @@ -665,51 +685,48 @@ mod tests { } #[cfg(nix_at_least = "2.33")] - fn create_multi_output_derivation_json() -> String { + fn create_multi_output_derivation_json() -> serde_json::Value { let system = current_system() .unwrap_or_else(|_| format!("{}-{}", std::env::consts::ARCH, std::env::consts::OS)); - format!( - r#"{{ - "version": 4, - "name": "multi-output-test", - "system": "{}", + serde_json::json!({ + "version": 4, + "name": "multi-output-test", + "system": system, + "builder": "/bin/sh", + "args": ["-c", "echo a > $outa; echo b > $outb; echo c > $outc; echo d > $outd; echo e > $oute; echo f > $outf; echo g > $outg; echo h > $outh; echo i > $outi; echo j > $outj"], + "env": { "builder": "/bin/sh", - "args": ["-c", "echo a > $outa; echo b > $outb; echo c > $outc; echo d > $outd; echo e > $oute; echo f > $outf; echo g > $outg; echo h > $outh; echo i > $outi; echo j > $outj"], - "env": {{ - "builder": "/bin/sh", - "name": "multi-output-test", - "system": "{}", - "outf": "/1vkfzqpwk313b51x0xjyh5s7w1lx141mr8da3dr9wqz5aqjyr2fh", - "outd": "/1ypxifgmbzp5sd0pzsp2f19aq68x5215260z3lcrmy5fch567lpm", - "outi": "/1wmasjnqi12j1mkjbxazdd0qd0ky6dh1qry12fk8qyp5kdamhbdx", - "oute": "/1f9r2k1s168js509qlw8a9di1qd14g5lqdj5fcz8z7wbqg11qp1f", - "outh": "/1rkx1hmszslk5nq9g04iyvh1h7bg8p92zw0hi4155hkjm8bpdn95", - "outc": "/1rj4nsf9pjjqq9jsq58a2qkwa7wgvgr09kgmk7mdyli6h1plas4w", - "outb": "/1p7i1dxifh86xq97m5kgb44d7566gj7rfjbw7fk9iij6ca4akx61", - "outg": "/14f8qi0r804vd6a6v40ckylkk1i6yl6fm243qp6asywy0km535lc", - "outj": "/0gkw1366qklqfqb2lw1pikgdqh3cmi3nw6f1z04an44ia863nxaz", - "outa": "/039akv9zfpihrkrv4pl54f3x231x362bll9afblsgfqgvx96h198" - }}, - "inputs": {{ - "drvs": {{}}, - "srcs": [] - }}, - "outputs": {{ - "outd": {{ "hashAlgo": "sha256", "method": "nar" }}, - "outf": {{ "hashAlgo": "sha256", "method": "nar" }}, - "outg": {{ "hashAlgo": "sha256", "method": "nar" }}, - "outb": {{ "hashAlgo": "sha256", "method": "nar" }}, - "outc": {{ "hashAlgo": "sha256", "method": "nar" }}, - "outi": {{ "hashAlgo": "sha256", "method": "nar" }}, - "outj": {{ "hashAlgo": "sha256", "method": "nar" }}, - "outh": {{ "hashAlgo": "sha256", "method": "nar" }}, - "outa": {{ "hashAlgo": "sha256", "method": "nar" }}, - "oute": {{ "hashAlgo": "sha256", "method": "nar" }} - }} - }}"#, - system, system - ) + "name": "multi-output-test", + "system": system, + "outf": "/1vkfzqpwk313b51x0xjyh5s7w1lx141mr8da3dr9wqz5aqjyr2fh", + "outd": "/1ypxifgmbzp5sd0pzsp2f19aq68x5215260z3lcrmy5fch567lpm", + "outi": "/1wmasjnqi12j1mkjbxazdd0qd0ky6dh1qry12fk8qyp5kdamhbdx", + "oute": "/1f9r2k1s168js509qlw8a9di1qd14g5lqdj5fcz8z7wbqg11qp1f", + "outh": "/1rkx1hmszslk5nq9g04iyvh1h7bg8p92zw0hi4155hkjm8bpdn95", + "outc": "/1rj4nsf9pjjqq9jsq58a2qkwa7wgvgr09kgmk7mdyli6h1plas4w", + "outb": "/1p7i1dxifh86xq97m5kgb44d7566gj7rfjbw7fk9iij6ca4akx61", + "outg": "/14f8qi0r804vd6a6v40ckylkk1i6yl6fm243qp6asywy0km535lc", + "outj": "/0gkw1366qklqfqb2lw1pikgdqh3cmi3nw6f1z04an44ia863nxaz", + "outa": "/039akv9zfpihrkrv4pl54f3x231x362bll9afblsgfqgvx96h198" + }, + "inputs": { + "drvs": {}, + "srcs": [] + }, + "outputs": { + "outd": { "hashAlgo": "sha256", "method": "nar" }, + "outf": { "hashAlgo": "sha256", "method": "nar" }, + "outg": { "hashAlgo": "sha256", "method": "nar" }, + "outb": { "hashAlgo": "sha256", "method": "nar" }, + "outc": { "hashAlgo": "sha256", "method": "nar" }, + "outi": { "hashAlgo": "sha256", "method": "nar" }, + "outj": { "hashAlgo": "sha256", "method": "nar" }, + "outh": { "hashAlgo": "sha256", "method": "nar" }, + "outa": { "hashAlgo": "sha256", "method": "nar" }, + "oute": { "hashAlgo": "sha256", "method": "nar" } + } + }) } #[test] @@ -717,7 +734,7 @@ mod tests { fn realise_multi_output_ordering() { let (mut store, temp_dir) = create_temp_store(); let drv_json = create_multi_output_derivation_json(); - let drv = store.derivation_from_json(&drv_json).unwrap(); + let drv = store.derivation_from_json(&drv_json.to_string()).unwrap(); let drv_path = store.add_derivation(&drv).unwrap(); // Build the derivation @@ -741,34 +758,31 @@ mod tests { // Create a derivation with an invalid system let system = "bogus65-bogusos"; - let drv_json = format!( - r#"{{ - "args": ["-c", "echo $name foo > $out"], + let drv_json = serde_json::json!({ + "args": ["-c", "echo $name foo > $out"], + "builder": "/bin/sh", + "env": { "builder": "/bin/sh", - "env": {{ - "builder": "/bin/sh", - "name": "myname", - "out": "/1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9", - "system": "{}" - }}, - "inputs": {{ - "drvs": {{}}, - "srcs": [] - }}, "name": "myname", - "outputs": {{ - "out": {{ + "out": "/1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9", + "system": system + }, + "inputs": { + "drvs": {}, + "srcs": [] + }, + "name": "myname", + "outputs": { + "out": { "hashAlgo": "sha256", "method": "nar" - }} - }}, - "system": "{}", - "version": 4 - }}"#, - system, system - ); + } + }, + "system": system, + "version": 4 + }); - let drv = store.derivation_from_json(&drv_json).unwrap(); + let drv = store.derivation_from_json(&drv_json.to_string()).unwrap(); let drv_path = store.add_derivation(&drv).unwrap(); // Try to build - should fail @@ -796,34 +810,31 @@ mod tests { .unwrap_or_else(|_| format!("{}-{}", std::env::consts::ARCH, std::env::consts::OS)); // Create a derivation where the builder exits with error - let drv_json = format!( - r#"{{ - "args": ["-c", "exit 1"], + let drv_json = serde_json::json!({ + "args": ["-c", "exit 1"], + "builder": "/bin/sh", + "env": { "builder": "/bin/sh", - "env": {{ - "builder": "/bin/sh", - "name": "failing", - "out": "/1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9", - "system": "{}" - }}, - "inputs": {{ - "drvs": {{}}, - "srcs": [] - }}, "name": "failing", - "outputs": {{ - "out": {{ + "out": "/1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9", + "system": system + }, + "inputs": { + "drvs": {}, + "srcs": [] + }, + "name": "failing", + "outputs": { + "out": { "hashAlgo": "sha256", "method": "nar" - }} - }}, - "system": "{}", - "version": 4 - }}"#, - system, system - ); + } + }, + "system": system, + "version": 4 + }); - let drv = store.derivation_from_json(&drv_json).unwrap(); + let drv = store.derivation_from_json(&drv_json.to_string()).unwrap(); let drv_path = store.add_derivation(&drv).unwrap(); // Try to build - should fail @@ -851,34 +862,31 @@ mod tests { .unwrap_or_else(|_| format!("{}-{}", std::env::consts::ARCH, std::env::consts::OS)); // Create a derivation where the builder succeeds but produces no output - let drv_json = format!( - r#"{{ - "args": ["-c", "true"], + let drv_json = serde_json::json!({ + "args": ["-c", "true"], + "builder": "/bin/sh", + "env": { "builder": "/bin/sh", - "env": {{ - "builder": "/bin/sh", - "name": "no-output", - "out": "/1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9", - "system": "{}" - }}, - "inputs": {{ - "drvs": {{}}, - "srcs": [] - }}, "name": "no-output", - "outputs": {{ - "out": {{ + "out": "/1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9", + "system": system + }, + "inputs": { + "drvs": {}, + "srcs": [] + }, + "name": "no-output", + "outputs": { + "out": { "hashAlgo": "sha256", "method": "nar" - }} - }}, - "system": "{}", - "version": 4 - }}"#, - system, system - ); + } + }, + "system": system, + "version": 4 + }); - let drv = store.derivation_from_json(&drv_json).unwrap(); + let drv = store.derivation_from_json(&drv_json.to_string()).unwrap(); let drv_path = store.add_derivation(&drv).unwrap(); // Try to build - should fail @@ -902,7 +910,7 @@ mod tests { fn get_fs_closure_with_outputs() { let (mut store, temp_dir) = create_temp_store(); let drv_json = create_test_derivation_json(); - let drv = store.derivation_from_json(&drv_json).unwrap(); + let drv = store.derivation_from_json(&drv_json.to_string()).unwrap(); let drv_path = store.add_derivation(&drv).unwrap(); // Build the derivation to get the output path @@ -935,7 +943,7 @@ mod tests { fn get_fs_closure_without_outputs() { let (mut store, temp_dir) = create_temp_store(); let drv_json = create_test_derivation_json(); - let drv = store.derivation_from_json(&drv_json).unwrap(); + let drv = store.derivation_from_json(&drv_json.to_string()).unwrap(); let drv_path = store.add_derivation(&drv).unwrap(); // Build the derivation to get the output path @@ -964,7 +972,7 @@ mod tests { fn get_fs_closure_flip_direction() { let (mut store, temp_dir) = create_temp_store(); let drv_json = create_test_derivation_json(); - let drv = store.derivation_from_json(&drv_json).unwrap(); + let drv = store.derivation_from_json(&drv_json.to_string()).unwrap(); let drv_path = store.add_derivation(&drv).unwrap(); // Build the derivation to get the output path @@ -991,7 +999,7 @@ mod tests { fn get_fs_closure_include_derivers() { let (mut store, temp_dir) = create_temp_store(); let drv_json = create_test_derivation_json(); - let drv = store.derivation_from_json(&drv_json).unwrap(); + let drv = store.derivation_from_json(&drv_json.to_string()).unwrap(); let drv_path = store.add_derivation(&drv).unwrap(); let drv_path_name = drv_path.name().unwrap(); From 57f3fe2ff6732a3bccca33d37c9cc84f75dbe534 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 24 Jan 2026 19:54:06 -0500 Subject: [PATCH 255/306] `cargo update` --- Cargo.lock | 109 ++++++++++++++++++++++++----------------------------- 1 file changed, 50 insertions(+), 59 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 58e41a9..dcaf5b5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 4 [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] @@ -42,9 +42,9 @@ dependencies = [ [[package]] name = "bitflags" -version = "2.9.4" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] name = "cexpr" @@ -57,9 +57,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "clang-sys" @@ -105,7 +105,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] @@ -116,14 +116,14 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "getrandom" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "libc", "r-efi", - "wasi", + "wasip2", ] [[package]] @@ -140,11 +140,11 @@ checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" [[package]] name = "home" -version = "0.5.11" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -176,9 +176,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.176" +version = "0.2.180" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" [[package]] name = "libloading" @@ -204,9 +204,9 @@ checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "log" -version = "0.4.28" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "memchr" @@ -385,18 +385,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.101" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.41" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" dependencies = [ "proc-macro2", ] @@ -409,9 +409,9 @@ checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "regex" -version = "1.11.3" +version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b5288124840bee7b386bc413c487869b360b2b4ec421ea56425128692f2a82c" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" dependencies = [ "aho-corasick", "memchr", @@ -421,9 +421,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.11" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "833eb9ce86d40ef33cb1306d8accf7bc8ec2bfea4355cbdebb3df68b40925cad" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" dependencies = [ "aho-corasick", "memchr", @@ -432,9 +432,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "rustc-hash" @@ -457,15 +457,15 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys 0.11.0", - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] @@ -518,9 +518,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "syn" -version = "2.0.106" +version = "2.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" dependencies = [ "proc-macro2", "quote", @@ -529,37 +529,28 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.23.0" +version = "3.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" +checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" dependencies = [ "fastrand", "getrandom", "once_cell", - "rustix 1.1.2", - "windows-sys 0.61.1", + "rustix 1.1.3", + "windows-sys 0.61.2", ] [[package]] name = "unicode-ident" -version = "1.0.19" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" - -[[package]] -name = "wasi" -version = "0.14.7+wasi-0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" -dependencies = [ - "wasip2", -] +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "wasip2" -version = "1.0.1+wasi-0.2.4" +version = "1.0.2+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" dependencies = [ "wit-bindgen", ] @@ -578,9 +569,9 @@ dependencies = [ [[package]] name = "windows-link" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-sys" @@ -593,9 +584,9 @@ dependencies = [ [[package]] name = "windows-sys" -version = "0.61.1" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f109e41dd4a3c848907eb83d5a42ea98b3769495597450cf6d153507b166f0f" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ "windows-link", ] @@ -666,24 +657,24 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "wit-bindgen" -version = "0.46.0" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" [[package]] name = "zerocopy" -version = "0.8.30" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ea879c944afe8a2b25fef16bb4ba234f47c694565e97383b36f3a878219065c" +checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.30" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf955aa904d6040f70dc8e9384444cb1030aed272ba3cb09bbc4ab9e7c1f34f5" +checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" dependencies = [ "proc-macro2", "quote", From aae065cb3e9c8b2fcf0dd63bf451e3449a58752b Mon Sep 17 00:00:00 2001 From: Artemis Tosini Date: Wed, 14 Jan 2026 14:44:33 -0500 Subject: [PATCH 256/306] Add conversions for `StorePath` to harmonia Only enabled with new feature "harmonia" due to harmonia's state of flux. --- Cargo.lock | 898 +++++++++++++++++- nix-bindings-store/Cargo.toml | 4 + nix-bindings-store/src/path/harmonia.rs | 65 ++ .../src/{path.rs => path/mod.rs} | 3 + 4 files changed, 960 insertions(+), 10 deletions(-) create mode 100644 nix-bindings-store/src/path/harmonia.rs rename nix-bindings-store/src/{path.rs => path/mod.rs} (98%) diff --git a/Cargo.lock b/Cargo.lock index dcaf5b5..b96fa37 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,12 +11,33 @@ dependencies = [ "memchr", ] +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anyhow" version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "bindgen" version = "0.69.5" @@ -46,6 +67,31 @@ version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +[[package]] +name = "bumpalo" +version = "3.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" + +[[package]] +name = "bytes" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" +dependencies = [ + "serde", +] + +[[package]] +name = "cc" +version = "1.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6354c81bbfd62d9cfa9cb3c773c2b7b2a3a482d569de977fd0e961f6e7c00583" +dependencies = [ + "find-msvc-tools", + "shlex", +] + [[package]] name = "cexpr" version = "0.6.0" @@ -61,6 +107,18 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "chrono" +version = "0.4.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" +dependencies = [ + "iana-time-zone", + "num-traits", + "serde", + "windows-link", +] + [[package]] name = "clang-sys" version = "1.8.1" @@ -72,6 +130,21 @@ dependencies = [ "libloading", ] +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + [[package]] name = "cstr" version = "0.2.12" @@ -92,12 +165,98 @@ dependencies = [ "syn", ] +[[package]] +name = "darling" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "data-encoding" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" + +[[package]] +name = "deranged" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +dependencies = [ + "powerfmt", + "serde_core", +] + +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn", + "unicode-xid", +] + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + [[package]] name = "either" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + [[package]] name = "errno" version = "0.3.14" @@ -114,6 +273,29 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "find-msvc-tools" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "getrandom" version = "0.3.4" @@ -132,6 +314,73 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +[[package]] +name = "harmonia-store-core" +version = "0.0.0-alpha.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "983e1ba97729aded802a93f0fb5b61e2eae09efee77c9fa3d62c76e60b621bf0" +dependencies = [ + "bytes", + "data-encoding", + "derive_more", + "harmonia-utils-base-encoding", + "harmonia-utils-hash", + "num_enum", + "ring", + "serde", + "serde_json", + "serde_with", + "thiserror", + "tokio", + "tracing", + "zerocopy", +] + +[[package]] +name = "harmonia-utils-base-encoding" +version = "0.0.0-alpha.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1e8a868920d6ab01dfa2c5abce20ecd2f6d7e1f3c6a9e1941426883ce15a52c" +dependencies = [ + "data-encoding", + "derive_more", + "serde", +] + +[[package]] +name = "harmonia-utils-hash" +version = "0.0.0-alpha.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301feeb6f34b380ab13df6bb264c461704dbbb7f81b45b46c406c0d8c2d8d7f4" +dependencies = [ + "data-encoding", + "derive_more", + "harmonia-utils-base-encoding", + "md5", + "ring", + "serde", + "thiserror", + "tokio", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "hex-literal" version = "0.4.1" @@ -147,6 +396,59 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "iana-time-zone" +version = "0.1.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", + "serde", + "serde_core", +] + [[package]] name = "itertools" version = "0.12.1" @@ -162,6 +464,16 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +[[package]] +name = "js-sys" +version = "0.3.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -208,6 +520,12 @@ version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +[[package]] +name = "md5" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae960838283323069879657ca3de837e9f7bbb4c7bf6ea7f1b290d5e9476d2e0" + [[package]] name = "memchr" version = "2.7.6" @@ -220,6 +538,17 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "mio" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + [[package]] name = "nix-bindings-bdwgc-sys" version = "0.2.1" @@ -313,6 +642,7 @@ version = "0.2.1" dependencies = [ "anyhow", "ctor", + "harmonia-store-core", "hex-literal", "lazy_static", "nix-bindings-store-sys", @@ -361,18 +691,67 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "num-conv" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_enum" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "once_cell" version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + [[package]] name = "pkg-config" version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "prettyplease" version = "0.2.37" @@ -383,6 +762,15 @@ dependencies = [ "syn", ] +[[package]] +name = "proc-macro-crate" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" +dependencies = [ + "toml_edit", +] + [[package]] name = "proc-macro2" version = "1.0.106" @@ -407,6 +795,26 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "regex" version = "1.12.2" @@ -436,12 +844,35 @@ version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "rustc-hash" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "0.38.44" @@ -468,6 +899,42 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54e910108742c57a770f492731f99be216a52fadd361b06c8fb59d74ccc267d2" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + [[package]] name = "serde" version = "1.0.228" @@ -475,6 +942,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ "serde_core", + "serde_derive", ] [[package]] @@ -510,12 +978,69 @@ dependencies = [ "zmij", ] +[[package]] +name = "serde_with" +version = "3.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" +dependencies = [ + "base64", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.13.0", + "schemars 0.9.0", + "schemars 1.2.0", + "serde_core", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "socket2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "syn" version = "2.0.114" @@ -534,18 +1059,181 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" dependencies = [ "fastrand", - "getrandom", + "getrandom 0.3.4", "once_cell", "rustix 1.1.3", "windows-sys 0.61.2", ] +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9da98b7d9b7dad93488a84b8248efc35352b0b2657397d4167e7ad67e5d535e5" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" + +[[package]] +name = "time-macros" +version = "0.2.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78cc610bac2dcee56805c99642447d4c5dbde4d01f752ffea0199aee1f601dc4" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tokio" +version = "1.49.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "toml_datetime" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.23.10+spec-1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" +dependencies = [ + "indexmap 2.13.0", + "toml_datetime", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.0.6+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" +dependencies = [ + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + [[package]] name = "unicode-ident" version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + [[package]] name = "wasip2" version = "1.0.2+wasi-0.2.9" @@ -555,6 +1243,51 @@ dependencies = [ "wit-bindgen", ] +[[package]] +name = "wasm-bindgen" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" +dependencies = [ + "unicode-ident", +] + [[package]] name = "which" version = "4.4.2" @@ -567,19 +1300,90 @@ dependencies = [ "rustix 0.38.44", ] +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", ] [[package]] @@ -597,14 +1401,31 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] [[package]] @@ -613,48 +1434,105 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "winnow" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" +dependencies = [ + "memchr", +] + [[package]] name = "wit-bindgen" version = "0.51.0" diff --git a/nix-bindings-store/Cargo.toml b/nix-bindings-store/Cargo.toml index 2e83ab4..af8b1bd 100644 --- a/nix-bindings-store/Cargo.toml +++ b/nix-bindings-store/Cargo.toml @@ -18,6 +18,7 @@ nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.2.1" } nix-bindings-store-sys = { path = "../nix-bindings-store-sys", version = "0.2.1" } lazy_static = "1.4" zerocopy = "0.8" +harmonia-store-core = { version = "0.0.0-alpha.0", optional = true } [dev-dependencies] ctor = "0.2" @@ -30,6 +31,9 @@ pkg-config = "0.3" # Needed for version parsing in build.rs nix-bindings-util = { path = "../nix-bindings-util", version = "0.2.1" } +[features] +harmonia = [ "dep:harmonia-store-core" ] + [lints.rust] warnings = "deny" dead-code = "allow" diff --git a/nix-bindings-store/src/path/harmonia.rs b/nix-bindings-store/src/path/harmonia.rs new file mode 100644 index 0000000..660fa1f --- /dev/null +++ b/nix-bindings-store/src/path/harmonia.rs @@ -0,0 +1,65 @@ +use anyhow::{Context as _, Result}; + +use super::{StorePath, STORE_PATH_HASH_SIZE}; + +impl TryFrom<&harmonia_store_core::store_path::StorePath> for StorePath { + type Error = anyhow::Error; + + fn try_from(harmonia_path: &harmonia_store_core::store_path::StorePath) -> Result { + let hash: &[u8; STORE_PATH_HASH_SIZE] = harmonia_path.hash().as_ref(); + StorePath::from_parts(hash, harmonia_path.name().as_ref()) + } +} + +impl TryFrom<&StorePath> for harmonia_store_core::store_path::StorePath { + type Error = anyhow::Error; + + fn try_from(nix_path: &StorePath) -> Result { + let hash = nix_path + .hash() + .context("Failed to get hash from nix StorePath")?; + let harmonia_hash = harmonia_store_core::store_path::StorePathHash::new(hash); + + let name = nix_path + .name() + .context("Failed to get name from nix StorePath")?; + + let harmonia_name: harmonia_store_core::store_path::StorePathName = name + .parse() + .context("Failed to parse name as StorePathName")?; + + Ok(harmonia_store_core::store_path::StorePath::from(( + harmonia_hash, + harmonia_name, + ))) + } +} + +#[cfg(test)] +mod tests { + + #[test] + fn store_path_round_trip_harmonia() { + let harmonia_path: harmonia_store_core::store_path::StorePath = + "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv".parse().unwrap(); + + let nix_path: crate::path::StorePath = (&harmonia_path).try_into().unwrap(); + + let harmonia_round_trip: harmonia_store_core::store_path::StorePath = + (&nix_path).try_into().unwrap(); + + assert_eq!(harmonia_path, harmonia_round_trip); + } + + #[test] + fn store_path_harmonia_clone() { + let harmonia_path: harmonia_store_core::store_path::StorePath = + "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv".parse().unwrap(); + + let nix_path: crate::path::StorePath = (&harmonia_path).try_into().unwrap(); + let cloned_path = nix_path.clone(); + + assert_eq!(nix_path.name().unwrap(), cloned_path.name().unwrap()); + assert_eq!(nix_path.hash().unwrap(), cloned_path.hash().unwrap()); + } +} diff --git a/nix-bindings-store/src/path.rs b/nix-bindings-store/src/path/mod.rs similarity index 98% rename from nix-bindings-store/src/path.rs rename to nix-bindings-store/src/path/mod.rs index 41f7f5b..c3478fa 100644 --- a/nix-bindings-store/src/path.rs +++ b/nix-bindings-store/src/path/mod.rs @@ -125,6 +125,9 @@ impl Drop for StorePath { } } +#[cfg(all(feature = "harmonia", nix_at_least = "2.33"))] +mod harmonia; + #[cfg(test)] mod tests { use super::*; From 714dcd95c07977acaf50e7188ee3b931dbc182ca Mon Sep 17 00:00:00 2001 From: Artemis Tosini Date: Thu, 15 Jan 2026 09:26:14 -0500 Subject: [PATCH 257/306] Add support for converting between libnix and harmonia derivations --- nix-bindings-store/Cargo.toml | 3 +- nix-bindings-store/src/derivation/harmonia.rs | 100 ++++++++++++++++++ .../src/{derivation.rs => derivation/mod.rs} | 6 ++ 3 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 nix-bindings-store/src/derivation/harmonia.rs rename nix-bindings-store/src/{derivation.rs => derivation/mod.rs} (97%) diff --git a/nix-bindings-store/Cargo.toml b/nix-bindings-store/Cargo.toml index af8b1bd..232688b 100644 --- a/nix-bindings-store/Cargo.toml +++ b/nix-bindings-store/Cargo.toml @@ -19,6 +19,7 @@ nix-bindings-store-sys = { path = "../nix-bindings-store-sys", version = "0.2.1" lazy_static = "1.4" zerocopy = "0.8" harmonia-store-core = { version = "0.0.0-alpha.0", optional = true } +serde_json = { version = "1.0", optional = true } [dev-dependencies] ctor = "0.2" @@ -32,7 +33,7 @@ pkg-config = "0.3" nix-bindings-util = { path = "../nix-bindings-util", version = "0.2.1" } [features] -harmonia = [ "dep:harmonia-store-core" ] +harmonia = [ "dep:harmonia-store-core", "dep:serde_json" ] [lints.rust] warnings = "deny" diff --git a/nix-bindings-store/src/derivation/harmonia.rs b/nix-bindings-store/src/derivation/harmonia.rs new file mode 100644 index 0000000..44b34a2 --- /dev/null +++ b/nix-bindings-store/src/derivation/harmonia.rs @@ -0,0 +1,100 @@ +use anyhow::Context as _; + +use super::Derivation; + +impl Derivation { + /// Convert harmonia Derivation to nix-bindings Derivation. + /// + /// This requires a Store instance because the Nix C API needs it for internal validation. + pub fn from_harmonia( + store: &mut crate::store::Store, + harmonia_drv: &harmonia_store_core::derivation::Derivation, + ) -> anyhow::Result { + let json = serde_json::to_string(harmonia_drv) + .context("Failed to serialize harmonia Derivation to JSON")?; + + store.derivation_from_json(&json) + } +} + +impl TryFrom<&Derivation> for harmonia_store_core::derivation::Derivation { + type Error = anyhow::Error; + + fn try_from(nix_drv: &Derivation) -> anyhow::Result { + let json = nix_drv + .to_json_string() + .context("Failed to convert nix Derivation to JSON")?; + + serde_json::from_str(&json).context("Failed to parse JSON as harmonia Derivation") + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn create_harmonia_derivation() -> harmonia_store_core::derivation::Derivation { + use harmonia_store_core::derivation::{Derivation, DerivationOutput}; + use harmonia_store_core::derived_path::OutputName; + use harmonia_store_core::store_path::StorePath; + use std::collections::{BTreeMap, BTreeSet}; + use std::str::FromStr; + + let system = format!("{}-{}", std::env::consts::ARCH, std::env::consts::OS); + let out_path = "8bs8sd27bzzy6w94fznjd2j8ldmdg7x6-myname"; + + let env = BTreeMap::from([ + ("builder".into(), "/bin/sh".into()), + ("name".into(), "myname".into()), + ("out".into(), format!("/{out_path}").into()), + ("system".into(), system.clone().into()), + ]); + let mut outputs = BTreeMap::new(); + outputs.insert( + OutputName::from_str("out").unwrap(), + DerivationOutput::InputAddressed(StorePath::from_base_path(out_path).unwrap()), + ); + + Derivation { + args: vec!["-c".into(), "echo $name foo > $out".into()], + builder: "/bin/sh".into(), + env, + inputs: BTreeSet::new(), + name: b"myname".as_slice().try_into().unwrap(), + outputs, + platform: system.into(), + structured_attrs: None, + } + } + + #[test] + fn derivation_round_trip_harmonia() { + let mut store = crate::store::Store::open(Some("dummy://"), []).unwrap(); + let harmonia_drv = create_harmonia_derivation(); + + // Convert to nix-bindings Derivation + let nix_drv = Derivation::from_harmonia(&mut store, &harmonia_drv).unwrap(); + + // Convert back to harmonia Derivation + let harmonia_round_trip: harmonia_store_core::derivation::Derivation = + (&nix_drv).try_into().unwrap(); + + assert_eq!(harmonia_drv, harmonia_round_trip); + } + + #[test] + fn derivation_clone() { + let mut store = crate::store::Store::open(Some("dummy://"), []).unwrap(); + let harmonia_drv = create_harmonia_derivation(); + + let derivation = Derivation::from_harmonia(&mut store, &harmonia_drv).unwrap(); + let cloned_derivation = derivation.clone(); + + let original_harmonia: harmonia_store_core::derivation::Derivation = + (&derivation).try_into().unwrap(); + let cloned_harmonia: harmonia_store_core::derivation::Derivation = + (&cloned_derivation).try_into().unwrap(); + + assert_eq!(original_harmonia, cloned_harmonia); + } +} diff --git a/nix-bindings-store/src/derivation.rs b/nix-bindings-store/src/derivation/mod.rs similarity index 97% rename from nix-bindings-store/src/derivation.rs rename to nix-bindings-store/src/derivation/mod.rs index ac4c265..557994e 100644 --- a/nix-bindings-store/src/derivation.rs +++ b/nix-bindings-store/src/derivation/mod.rs @@ -84,3 +84,9 @@ impl Drop for Derivation { } } } + +#[cfg(feature = "harmonia")] +mod harmonia; + +#[cfg(test)] +mod tests {} From ad412bdd8c31c9e3dd15aa732dc4c470da5fc181 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 24 Jan 2026 21:01:38 -0500 Subject: [PATCH 258/306] CI with and without harmonia --- nci.nix | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/nci.nix b/nci.nix index 62c523e..351ac77 100644 --- a/nci.nix +++ b/nci.nix @@ -49,5 +49,24 @@ }; }; }; + nci.crates.nix-bindings-store = + let + addHarmoniaProfile = '' + cat >> Cargo.toml <<'EOF' + + [profile.harmonia] + inherits = "release" + EOF + ''; + in + { + profiles.harmonia = { + features = [ "harmonia" ]; + runTests = true; + # Add harmonia profile to Cargo.toml for both deps and main builds + depsDrvConfig.mkDerivation.postPatch = addHarmoniaProfile; + drvConfig.mkDerivation.postPatch = addHarmoniaProfile; + }; + }; }; } From 1a679ba8736fe9589331d93afb340fd8186826f4 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 26 Jan 2026 12:53:23 +0100 Subject: [PATCH 259/306] chore(devShell): Resolve nixfmt-rfc-style warning --- dev/flake-module.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/flake-module.nix b/dev/flake-module.nix index da4f1d9..384a2d8 100644 --- a/dev/flake-module.nix +++ b/dev/flake-module.nix @@ -84,7 +84,7 @@ config.treefmt.build.wrapper pkgs.rust-analyzer - pkgs.nixfmt-rfc-style + pkgs.nixfmt pkgs.rustfmt pkgs.pkg-config pkgs.clang-tools # clangd From 4551c2b378f099d3dd85192663d55cc1ecf012cc Mon Sep 17 00:00:00 2001 From: Tristan Ross Date: Tue, 27 Jan 2026 12:02:42 -0800 Subject: [PATCH 260/306] Replace lazy_static with LazyLock --- Cargo.lock | 3 --- nix-bindings-expr/Cargo.toml | 1 - nix-bindings-expr/src/eval_state.rs | 17 ++++++----------- nix-bindings-flake/Cargo.toml | 1 - nix-bindings-store/Cargo.toml | 1 - nix-bindings-store/src/store.rs | 18 +++++++----------- 6 files changed, 13 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b96fa37..7e1d1f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -564,7 +564,6 @@ dependencies = [ "anyhow", "cstr", "ctor", - "lazy_static", "nix-bindings-bdwgc-sys", "nix-bindings-expr-sys", "nix-bindings-store", @@ -614,7 +613,6 @@ dependencies = [ "anyhow", "cstr", "ctor", - "lazy_static", "nix-bindings-expr", "nix-bindings-fetchers", "nix-bindings-flake-sys", @@ -644,7 +642,6 @@ dependencies = [ "ctor", "harmonia-store-core", "hex-literal", - "lazy_static", "nix-bindings-store-sys", "nix-bindings-util", "nix-bindings-util-sys", diff --git a/nix-bindings-expr/Cargo.toml b/nix-bindings-expr/Cargo.toml index 616bd96..4b1c777 100644 --- a/nix-bindings-expr/Cargo.toml +++ b/nix-bindings-expr/Cargo.toml @@ -19,7 +19,6 @@ nix-bindings-bdwgc-sys = { path = "../nix-bindings-bdwgc-sys", version = "0.2.1" nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.2.1" } nix-bindings-store-sys = { path = "../nix-bindings-store-sys", version = "0.2.1" } nix-bindings-expr-sys = { path = "../nix-bindings-expr-sys", version = "0.2.1" } -lazy_static = "1.4" ctor = "0.2" tempfile = "3.10" cstr = "0.2" diff --git a/nix-bindings-expr/src/eval_state.rs b/nix-bindings-expr/src/eval_state.rs index c05ae9b..b8e27e6 100644 --- a/nix-bindings-expr/src/eval_state.rs +++ b/nix-bindings-expr/src/eval_state.rs @@ -133,7 +133,6 @@ use crate::value::{Int, Value, ValueType}; use anyhow::Context as _; use anyhow::{bail, Result}; use cstr::cstr; -use lazy_static::lazy_static; use nix_bindings_bdwgc_sys as gc; use nix_bindings_expr_sys as raw; use nix_bindings_store::path::StorePath; @@ -148,17 +147,13 @@ use std::ffi::{c_char, CString}; use std::iter::FromIterator; use std::os::raw::c_uint; use std::ptr::{null, null_mut, NonNull}; -use std::sync::{Arc, Weak}; +use std::sync::{Arc, LazyLock, Weak}; -lazy_static! { - static ref INIT: Result<()> = { - unsafe { - gc::GC_allow_register_threads(); - check_call!(raw::libexpr_init(&mut Context::new()))?; - Ok(()) - } - }; -} +static INIT: LazyLock> = LazyLock::new(|| unsafe { + gc::GC_allow_register_threads(); + check_call!(raw::libexpr_init(&mut Context::new()))?; + Ok(()) +}); pub fn init() -> Result<()> { let x = INIT.as_ref(); diff --git a/nix-bindings-flake/Cargo.toml b/nix-bindings-flake/Cargo.toml index 5222133..336b1f8 100644 --- a/nix-bindings-flake/Cargo.toml +++ b/nix-bindings-flake/Cargo.toml @@ -17,7 +17,6 @@ nix-bindings-fetchers = { path = "../nix-bindings-fetchers", version = "0.2.1" } nix-bindings-store = { path = "../nix-bindings-store", version = "0.2.1" } nix-bindings-util = { path = "../nix-bindings-util", version = "0.2.1" } nix-bindings-flake-sys = { path = "../nix-bindings-flake-sys", version = "0.2.1" } -lazy_static = "1.4" ctor = "0.2" tempfile = "3.10" cstr = "0.2" diff --git a/nix-bindings-store/Cargo.toml b/nix-bindings-store/Cargo.toml index 232688b..83aab99 100644 --- a/nix-bindings-store/Cargo.toml +++ b/nix-bindings-store/Cargo.toml @@ -16,7 +16,6 @@ anyhow = "1.0" nix-bindings-util = { path = "../nix-bindings-util", version = "0.2.1" } nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.2.1" } nix-bindings-store-sys = { path = "../nix-bindings-store-sys", version = "0.2.1" } -lazy_static = "1.4" zerocopy = "0.8" harmonia-store-core = { version = "0.0.0-alpha.0", optional = true } serde_json = { version = "1.0", optional = true } diff --git a/nix-bindings-store/src/store.rs b/nix-bindings-store/src/store.rs index 7f231f0..655a088 100644 --- a/nix-bindings-store/src/store.rs +++ b/nix-bindings-store/src/store.rs @@ -1,5 +1,4 @@ use anyhow::{bail, Error, Result}; -use lazy_static::lazy_static; use nix_bindings_store_sys as raw; use nix_bindings_util::context::Context; use nix_bindings_util::string_return::{ @@ -13,19 +12,17 @@ use std::collections::HashMap; use std::ffi::{c_char, CString}; use std::ptr::null_mut; use std::ptr::NonNull; -use std::sync::{Arc, Mutex, Weak}; +use std::sync::{Arc, LazyLock, Mutex, Weak}; #[cfg(nix_at_least = "2.33.0pre")] use crate::derivation::Derivation; use crate::path::StorePath; /* TODO make Nix itself thread safe */ -lazy_static! { - static ref INIT: Result<()> = unsafe { - check_call!(raw::libstore_init(&mut Context::new()))?; - Ok(()) - }; -} +static INIT: LazyLock> = LazyLock::new(|| unsafe { + check_call!(raw::libstore_init(&mut Context::new()))?; + Ok(()) +}); struct StoreRef { inner: NonNull, @@ -68,9 +65,8 @@ impl StoreWeak { /// Protects against https://github.com/NixOS/nix/issues/11979 (unless different parameters are passed, in which case it's up to luck, but you do get your own parameters as you asked for). type StoreCacheMap = HashMap<(Option, Vec<(String, String)>), StoreWeak>; -lazy_static! { - static ref STORE_CACHE: Arc> = Arc::new(Mutex::new(HashMap::new())); -} +static STORE_CACHE: LazyLock>> = + LazyLock::new(|| Arc::new(Mutex::new(HashMap::new()))); #[cfg(nix_at_least = "2.33.0pre")] unsafe extern "C" fn callback_get_result_store_path_set( From da517ed9018e421f781d3e505660ce818d561504 Mon Sep 17 00:00:00 2001 From: Hercules CI Effects Date: Sun, 1 Feb 2026 02:32:57 +0000 Subject: [PATCH 261/306] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'flake-parts': 'github:hercules-ci/flake-parts/a34fae9c08a15ad73f295041fec82323541400a9?narHash=sha256-XswHlK/Qtjasvhd1nOa1e8MgZ8GS//jBoTqWtrS1Giw%3D' (2025-12-15) → 'github:hercules-ci/flake-parts/80daad04eddbbf5a4d883996a73f3f542fa437ac?narHash=sha256-PVvu7OqHBGWN16zSi6tEmPwwHQ4rLPU9Plvs8/1TUBY%3D' (2026-01-11) • Updated input 'nix': 'github:NixOS/nix/843395a2c87c9b0f0eb9b39bd8567c77cdabcc4e?narHash=sha256-%2BrJcHQE8yjZTR/PZpHr3eya6/Dt1DoGe/9cRoPXTI3k%3D' (2025-12-31) → 'github:NixOS/nix/77b6b01b727f0cd1324e431a32a8854768b957ef?narHash=sha256-vpI7XEfX5zeCVRANUzhMNsZfrMWuN0rwNenQ3z0rJNo%3D' (2026-01-30) • Updated input 'nix/flake-compat': 'github:edolstra/flake-compat/ff81ac966bb2cae68946d5ed5fc4994f96d0ffec?narHash=sha256-NeCCThCEP3eCl2l/%2B27kNNK7QrwZB1IJCrXfrbv5oqU%3D' (2024-12-04) → 'github:NixOS/flake-compat/5edf11c44bc78a0d334f6334cdaf7d60d732daab?narHash=sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns%3D' (2025-12-29) • Updated input 'nix-cargo-integration': 'github:90-008/nix-cargo-integration/525ccaf24a3e3b5d28a4dbec8a9c4e1ca759db8e?narHash=sha256-5hrqsS9pnPl1ptnJVtEUkUcbr9feYH8OG14pKRnbG2U%3D' (2025-12-31) → 'github:90-008/nix-cargo-integration/6d583e2098fa3df490c2597df06386e3efcc39b6?narHash=sha256-bjtDp0NHjLjDOjklQVHCDCVM5q39zDzuwenNri0p4Ys%3D' (2026-01-31) • Updated input 'nix-cargo-integration/parts': 'github:hercules-ci/flake-parts/a34fae9c08a15ad73f295041fec82323541400a9?narHash=sha256-XswHlK/Qtjasvhd1nOa1e8MgZ8GS//jBoTqWtrS1Giw%3D' (2025-12-15) → 'github:hercules-ci/flake-parts/80daad04eddbbf5a4d883996a73f3f542fa437ac?narHash=sha256-PVvu7OqHBGWN16zSi6tEmPwwHQ4rLPU9Plvs8/1TUBY%3D' (2026-01-11) • Updated input 'nix-cargo-integration/rust-overlay': 'github:oxalica/rust-overlay/6d14586a5917a1ec7f045ac97e6d00c68ea5d9f3?narHash=sha256-TjfAb58Ybz/93e2jP0qD846dj%2BVqiY7wk%2BEqsxcZ708%3D' (2025-12-31) → 'github:oxalica/rust-overlay/a1d32c90c8a4ea43e9586b7e5894c179d5747425?narHash=sha256-zmnvRUm15QrlKH0V1BZoiT3U%2BQ%2Btr%2BP5Osi8qgtL9fY%3D' (2026-01-31) • Updated input 'nix-cargo-integration/treefmt': 'github:numtide/treefmt-nix/dec15f37015ac2e774c84d0952d57fcdf169b54d?narHash=sha256-yOt/FTB7oSEKQH9EZMFMeuldK1HGpQs2eAzdS9hNS/o%3D' (2025-12-30) → 'github:numtide/treefmt-nix/28b19c5844cc6e2257801d43f2772a4b4c050a1b?narHash=sha256-8aAYwyVzSSwIhP2glDhw/G0i5%2BwOrren3v6WmxkVonM%3D' (2026-01-29) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/c0b0e0fddf73fd517c3471e546c0df87a42d53f4?narHash=sha256-coBu0ONtFzlwwVBzmjacUQwj3G%2BlybcZ1oeNSQkgC0M%3D' (2025-12-28) → 'github:NixOS/nixpkgs/bfc1b8a4574108ceef22f02bafcf6611380c100d?narHash=sha256-msG8SU5WsBUfVVa/9RPLaymvi5bI8edTavbIq3vRlhI%3D' (2026-01-26) --- flake.lock | 52 ++++++++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/flake.lock b/flake.lock index c9d2ea9..168537c 100644 --- a/flake.lock +++ b/flake.lock @@ -43,15 +43,15 @@ "flake-compat": { "flake": false, "locked": { - "lastModified": 1733328505, - "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=", - "owner": "edolstra", + "lastModified": 1767039857, + "narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=", + "owner": "NixOS", "repo": "flake-compat", - "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", + "rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab", "type": "github" }, "original": { - "owner": "edolstra", + "owner": "NixOS", "repo": "flake-compat", "type": "github" } @@ -77,11 +77,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1765835352, - "narHash": "sha256-XswHlK/Qtjasvhd1nOa1e8MgZ8GS//jBoTqWtrS1Giw=", + "lastModified": 1768135262, + "narHash": "sha256-PVvu7OqHBGWN16zSi6tEmPwwHQ4rLPU9Plvs8/1TUBY=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "a34fae9c08a15ad73f295041fec82323541400a9", + "rev": "80daad04eddbbf5a4d883996a73f3f542fa437ac", "type": "github" }, "original": { @@ -170,11 +170,11 @@ "nixpkgs-regression": "nixpkgs-regression" }, "locked": { - "lastModified": 1767212231, - "narHash": "sha256-+rJcHQE8yjZTR/PZpHr3eya6/Dt1DoGe/9cRoPXTI3k=", + "lastModified": 1769798267, + "narHash": "sha256-vpI7XEfX5zeCVRANUzhMNsZfrMWuN0rwNenQ3z0rJNo=", "owner": "NixOS", "repo": "nix", - "rev": "843395a2c87c9b0f0eb9b39bd8567c77cdabcc4e", + "rev": "77b6b01b727f0cd1324e431a32a8854768b957ef", "type": "github" }, "original": { @@ -196,11 +196,11 @@ "treefmt": "treefmt" }, "locked": { - "lastModified": 1767162115, - "narHash": "sha256-5hrqsS9pnPl1ptnJVtEUkUcbr9feYH8OG14pKRnbG2U=", + "lastModified": 1769840916, + "narHash": "sha256-bjtDp0NHjLjDOjklQVHCDCVM5q39zDzuwenNri0p4Ys=", "owner": "90-008", "repo": "nix-cargo-integration", - "rev": "525ccaf24a3e3b5d28a4dbec8a9c4e1ca759db8e", + "rev": "6d583e2098fa3df490c2597df06386e3efcc39b6", "type": "github" }, "original": { @@ -211,11 +211,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1766902085, - "narHash": "sha256-coBu0ONtFzlwwVBzmjacUQwj3G+lybcZ1oeNSQkgC0M=", + "lastModified": 1769461804, + "narHash": "sha256-msG8SU5WsBUfVVa/9RPLaymvi5bI8edTavbIq3vRlhI=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "c0b0e0fddf73fd517c3471e546c0df87a42d53f4", + "rev": "bfc1b8a4574108ceef22f02bafcf6611380c100d", "type": "github" }, "original": { @@ -280,11 +280,11 @@ ] }, "locked": { - "lastModified": 1765835352, - "narHash": "sha256-XswHlK/Qtjasvhd1nOa1e8MgZ8GS//jBoTqWtrS1Giw=", + "lastModified": 1768135262, + "narHash": "sha256-PVvu7OqHBGWN16zSi6tEmPwwHQ4rLPU9Plvs8/1TUBY=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "a34fae9c08a15ad73f295041fec82323541400a9", + "rev": "80daad04eddbbf5a4d883996a73f3f542fa437ac", "type": "github" }, "original": { @@ -355,11 +355,11 @@ ] }, "locked": { - "lastModified": 1767149068, - "narHash": "sha256-TjfAb58Ybz/93e2jP0qD846dj+VqiY7wk+EqsxcZ708=", + "lastModified": 1769828398, + "narHash": "sha256-zmnvRUm15QrlKH0V1BZoiT3U+Q+tr+P5Osi8qgtL9fY=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "6d14586a5917a1ec7f045ac97e6d00c68ea5d9f3", + "rev": "a1d32c90c8a4ea43e9586b7e5894c179d5747425", "type": "github" }, "original": { @@ -399,11 +399,11 @@ ] }, "locked": { - "lastModified": 1767122417, - "narHash": "sha256-yOt/FTB7oSEKQH9EZMFMeuldK1HGpQs2eAzdS9hNS/o=", + "lastModified": 1769691507, + "narHash": "sha256-8aAYwyVzSSwIhP2glDhw/G0i5+wOrren3v6WmxkVonM=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "dec15f37015ac2e774c84d0952d57fcdf169b54d", + "rev": "28b19c5844cc6e2257801d43f2772a4b4c050a1b", "type": "github" }, "original": { From e9f0f6f7fb26bfe25d638208dc579fb6636a280e Mon Sep 17 00:00:00 2001 From: Hercules CI Effects Date: Sun, 1 Feb 2026 02:33:00 +0000 Subject: [PATCH 262/306] dev/flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'hercules-ci-effects': 'github:hercules-ci/hercules-ci-effects/796ba31ee88bcec5c3cbc80ee34c5e157705aab5?narHash=sha256-0bWm54W2kkhrLdvVboT2KVxBliEkc2sNf%2BINaDhvEDU%3D' (2026-01-13) → 'github:hercules-ci/hercules-ci-effects/c19e263e6e22ec7379d972f19e6a322f943c73fb?narHash=sha256-V0YOJRum50gtKgwavsAfwXc9%2BXAsJCC7386YZx1sWGQ%3D' (2026-01-15) • Updated input 'hercules-ci-effects/flake-parts': 'github:hercules-ci/flake-parts/5635c32d666a59ec9a55cab87e898889869f7b71?narHash=sha256-MhA7wmo/7uogLxiewwRRmIax70g6q1U/YemqTGoFHlM%3D' (2025-12-11) → 'github:hercules-ci/flake-parts/80daad04eddbbf5a4d883996a73f3f542fa437ac?narHash=sha256-PVvu7OqHBGWN16zSi6tEmPwwHQ4rLPU9Plvs8/1TUBY%3D' (2026-01-11) • Updated input 'hercules-ci-effects/nixpkgs': 'github:NixOS/nixpkgs/2fbfb1d73d239d2402a8fe03963e37aab15abe8b?narHash=sha256-9VvC20PJPsleGMewwcWYKGzDIyjckEz8uWmT0vCDYK0%3D' (2025-12-11) → 'github:NixOS/nixpkgs/1412caf7bf9e660f2f962917c14b1ea1c3bc695e?narHash=sha256-AIdl6WAn9aymeaH/NvBj0H9qM%2BXuAuYbGMZaP0zcXAQ%3D' (2026-01-13) • Updated input 'pre-commit-hooks-nix': 'github:cachix/pre-commit-hooks.nix/b68b780b69702a090c8bb1b973bab13756cc7a27?narHash=sha256-t3T/xm8zstHRLx%2BpIHxVpQTiySbKqcQbK%2Br%2B01XVKc0%3D' (2025-12-16) → 'github:cachix/pre-commit-hooks.nix/a1ef738813b15cf8ec759bdff5761b027e3e1d23?narHash=sha256-Efs3VUPelRduf3PpfPP2ovEB4CXT7vHf8W%2Bxc49RL/U%3D' (2026-01-22) • Updated input 'pre-commit-hooks-nix/flake-compat': 'github:edolstra/flake-compat/f387cd2afec9419c8ee37694406ca490c3f34ee5?narHash=sha256-XKUZz9zewJNUj46b4AJdiRZJAvSZ0Dqj2BNfXvFlJC4%3D' (2025-10-27) → 'github:NixOS/flake-compat/5edf11c44bc78a0d334f6334cdaf7d60d732daab?narHash=sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns%3D' (2025-12-29) • Updated input 'treefmt-nix': 'github:numtide/treefmt-nix/dec15f37015ac2e774c84d0952d57fcdf169b54d?narHash=sha256-yOt/FTB7oSEKQH9EZMFMeuldK1HGpQs2eAzdS9hNS/o%3D' (2025-12-30) → 'github:numtide/treefmt-nix/28b19c5844cc6e2257801d43f2772a4b4c050a1b?narHash=sha256-8aAYwyVzSSwIhP2glDhw/G0i5%2BwOrren3v6WmxkVonM%3D' (2026-01-29) --- dev/flake.lock | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/dev/flake.lock b/dev/flake.lock index 4020cf4..44ec905 100644 --- a/dev/flake.lock +++ b/dev/flake.lock @@ -3,15 +3,15 @@ "flake-compat": { "flake": false, "locked": { - "lastModified": 1761588595, - "narHash": "sha256-XKUZz9zewJNUj46b4AJdiRZJAvSZ0Dqj2BNfXvFlJC4=", - "owner": "edolstra", + "lastModified": 1767039857, + "narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=", + "owner": "NixOS", "repo": "flake-compat", - "rev": "f387cd2afec9419c8ee37694406ca490c3f34ee5", + "rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab", "type": "github" }, "original": { - "owner": "edolstra", + "owner": "NixOS", "repo": "flake-compat", "type": "github" } @@ -24,11 +24,11 @@ ] }, "locked": { - "lastModified": 1765495779, - "narHash": "sha256-MhA7wmo/7uogLxiewwRRmIax70g6q1U/YemqTGoFHlM=", + "lastModified": 1768135262, + "narHash": "sha256-PVvu7OqHBGWN16zSi6tEmPwwHQ4rLPU9Plvs8/1TUBY=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "5635c32d666a59ec9a55cab87e898889869f7b71", + "rev": "80daad04eddbbf5a4d883996a73f3f542fa437ac", "type": "github" }, "original": { @@ -63,11 +63,11 @@ "nixpkgs": "nixpkgs" }, "locked": { - "lastModified": 1768263332, - "narHash": "sha256-0bWm54W2kkhrLdvVboT2KVxBliEkc2sNf+INaDhvEDU=", + "lastModified": 1768476106, + "narHash": "sha256-V0YOJRum50gtKgwavsAfwXc9+XAsJCC7386YZx1sWGQ=", "owner": "hercules-ci", "repo": "hercules-ci-effects", - "rev": "796ba31ee88bcec5c3cbc80ee34c5e157705aab5", + "rev": "c19e263e6e22ec7379d972f19e6a322f943c73fb", "type": "github" }, "original": { @@ -78,11 +78,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1765472234, - "narHash": "sha256-9VvC20PJPsleGMewwcWYKGzDIyjckEz8uWmT0vCDYK0=", + "lastModified": 1768305791, + "narHash": "sha256-AIdl6WAn9aymeaH/NvBj0H9qM+XuAuYbGMZaP0zcXAQ=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "2fbfb1d73d239d2402a8fe03963e37aab15abe8b", + "rev": "1412caf7bf9e660f2f962917c14b1ea1c3bc695e", "type": "github" }, "original": { @@ -99,11 +99,11 @@ "nixpkgs": [] }, "locked": { - "lastModified": 1765911976, - "narHash": "sha256-t3T/xm8zstHRLx+pIHxVpQTiySbKqcQbK+r+01XVKc0=", + "lastModified": 1769069492, + "narHash": "sha256-Efs3VUPelRduf3PpfPP2ovEB4CXT7vHf8W+xc49RL/U=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "b68b780b69702a090c8bb1b973bab13756cc7a27", + "rev": "a1ef738813b15cf8ec759bdff5761b027e3e1d23", "type": "github" }, "original": { @@ -124,11 +124,11 @@ "nixpkgs": [] }, "locked": { - "lastModified": 1767122417, - "narHash": "sha256-yOt/FTB7oSEKQH9EZMFMeuldK1HGpQs2eAzdS9hNS/o=", + "lastModified": 1769691507, + "narHash": "sha256-8aAYwyVzSSwIhP2glDhw/G0i5+wOrren3v6WmxkVonM=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "dec15f37015ac2e774c84d0952d57fcdf169b54d", + "rev": "28b19c5844cc6e2257801d43f2772a4b4c050a1b", "type": "github" }, "original": { From a40b4f51905af2c73e2e7f9068952bcb618b88c9 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 10 Feb 2026 00:46:43 +0100 Subject: [PATCH 263/306] Add combined rustdoc package with cross-crate linking NCI's doc-merge doesn't support rustdoc's sharded search index format (Rust 1.78+), so we build all workspace crates together in a single cargo doc invocation. This enables cross-crate linking and produces a custom index page with rustdoc-matching theme support. --- dev/flake-module.nix | 113 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) diff --git a/dev/flake-module.nix b/dev/flake-module.nix index 384a2d8..449a560 100644 --- a/dev/flake-module.nix +++ b/dev/flake-module.nix @@ -56,6 +56,119 @@ ''}"; }; + # Combined rustdoc for all crates with cross-linking. + # NOTE: nci.outputs.nix-bindings.docs uses doc-merge which doesn't support + # rustdoc's new sharded search index format (Rust 1.78+). + # See https://github.com/90-008/nix-cargo-integration/issues/198 + # Instead, we build all workspace crates together so rustdoc can link them. + packages.docs = + let + # Use nix-bindings-flake (has most transitive deps) as base + base = config.nci.outputs.nix-bindings-flake.packages.release; + crates = [ + "nix-bindings-bdwgc-sys" + "nix-bindings-util-sys" + "nix-bindings-util" + "nix-bindings-store-sys" + "nix-bindings-store" + "nix-bindings-expr-sys" + "nix-bindings-expr" + "nix-bindings-fetchers-sys" + "nix-bindings-fetchers" + "nix-bindings-flake-sys" + "nix-bindings-flake" + ]; + packageFlags = pkgs.lib.concatMapStringsSep " " (c: "-p ${c}") crates; + in + (base.extendModules { + modules = [ + { + mkDerivation = { + # Build docs for all crates together (enabling cross-crate linking) + buildPhase = pkgs.lib.mkForce '' + cargo doc $cargoBuildFlags --no-deps --profile $cargoBuildProfile ${packageFlags} + ''; + checkPhase = pkgs.lib.mkForce ":"; + installPhase = pkgs.lib.mkForce '' + mv target/$CARGO_BUILD_TARGET/doc $out + + # Find rustdoc assets (have hashes in filenames) + find_asset() { + local pattern="$1" + local matches=($out/static.files/$pattern) + if [[ ''${#matches[@]} -ne 1 || ! -e "''${matches[0]}" ]]; then + echo "Expected exactly one match for $pattern, found: ''${matches[*]}" >&2 + exit 1 + fi + basename "''${matches[0]}" + } + rustdoc_css=$(find_asset 'rustdoc-*.css') + normalize_css=$(find_asset 'normalize-*.css') + storage_js=$(find_asset 'storage-*.js') + + cat > $out/index.html < + + + + nix-bindings-rust + + + + + + +

nix-bindings-rust

+

Rust bindings for the Nix C API

+

Crates

+ +
+

Low-level bindings

+

+ These -sys crates provide raw FFI bindings generated by + bindgen. + They expose the C API directly without safety wrappers. + Most users should prefer the high-level crates above. +

+ +
+ + + EOF + ''; + }; + } + ]; + }).config.public; + devShells.default = pkgs.mkShell { name = "nix-bindings-devshell"; strictDeps = true; From ee43f3787083ca41d9be2b12d929b854d43a79a0 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 10 Feb 2026 09:57:10 +0100 Subject: [PATCH 264/306] Add documentation links and deployment effect - Add API documentation links to all Cargo.toml files - Add API documentation links to all crate READMEs - Add API Reference link to main README - Add Hercules CI effect to push docs to gh-pages on main branch --- README.md | 1 + dev/flake-module.nix | 18 +++++++++++++++++- nix-bindings-bdwgc-sys/Cargo.toml | 1 + nix-bindings-bdwgc-sys/README.md | 2 ++ nix-bindings-expr-sys/Cargo.toml | 1 + nix-bindings-expr-sys/README.md | 2 ++ nix-bindings-expr/Cargo.toml | 1 + nix-bindings-expr/README.md | 2 ++ nix-bindings-fetchers-sys/Cargo.toml | 1 + nix-bindings-fetchers-sys/README.md | 2 ++ nix-bindings-fetchers/Cargo.toml | 1 + nix-bindings-fetchers/README.md | 2 ++ nix-bindings-flake-sys/Cargo.toml | 1 + nix-bindings-flake-sys/README.md | 2 ++ nix-bindings-flake/Cargo.toml | 1 + nix-bindings-flake/README.md | 2 ++ nix-bindings-store-sys/Cargo.toml | 1 + nix-bindings-store-sys/README.md | 2 ++ nix-bindings-store/Cargo.toml | 1 + nix-bindings-store/README.md | 2 ++ nix-bindings-util-sys/Cargo.toml | 1 + nix-bindings-util-sys/README.md | 2 ++ nix-bindings-util/Cargo.toml | 1 + nix-bindings-util/README.md | 2 ++ 24 files changed, 51 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ad6f2ac..e02b585 100644 --- a/README.md +++ b/README.md @@ -211,6 +211,7 @@ For VSCode, load the dev shell via Nix Env Selector extension or direnv. ## Documentation +- [API Reference](https://nixops4.github.io/nix-bindings-rust/development/) - [Changelog](CHANGELOG.md) - [Nix C API Reference][C API] - [nix-cargo-integration][nix-cargo-integration] diff --git a/dev/flake-module.nix b/dev/flake-module.nix index 449a560..4ca895a 100644 --- a/dev/flake-module.nix +++ b/dev/flake-module.nix @@ -1,5 +1,6 @@ { inputs, + withSystem, ... }: { @@ -218,9 +219,24 @@ }; }; herculesCI = - { ... }: + hci@{ lib, ... }: { ciSystems = [ "x86_64-linux" ]; + onPush.default.outputs = { + effects.pushDocs = lib.optionalAttrs (hci.config.repo.branch == "main") ( + withSystem "x86_64-linux" ( + { config, hci-effects, ... }: + hci-effects.gitWriteBranch { + git.checkout.remote.url = hci.config.repo.remoteHttpUrl; + git.checkout.forgeType = "github"; + git.checkout.user = "x-access-token"; + git.update.branch = "gh-pages"; + contents = config.packages.docs; + destination = "development"; # directory + } + ) + ); + }; }; hercules-ci.flake-update = { enable = true; diff --git a/nix-bindings-bdwgc-sys/Cargo.toml b/nix-bindings-bdwgc-sys/Cargo.toml index 734663e..488f41e 100644 --- a/nix-bindings-bdwgc-sys/Cargo.toml +++ b/nix-bindings-bdwgc-sys/Cargo.toml @@ -6,6 +6,7 @@ build = "build.rs" license = "LGPL-2.1" description = "Low-level FFI bindings to the Boehm-Demers-Weiser garbage collector" repository = "https://github.com/nixops4/nix-bindings-rust" +documentation = "https://nixops4.github.io/nix-bindings-rust/development/nix_bindings_bdwgc_sys/" readme = "README.md" [lib] diff --git a/nix-bindings-bdwgc-sys/README.md b/nix-bindings-bdwgc-sys/README.md index 80988a5..b49b5b4 100644 --- a/nix-bindings-bdwgc-sys/README.md +++ b/nix-bindings-bdwgc-sys/README.md @@ -3,6 +3,8 @@ This crate contains generated bindings for the Boehm-Demers-Weiser garbage collector (`bdw-gc`). **You should not have to use this crate directly,** and so you should probably not add it to your dependencies. +[API Documentation](https://nixops4.github.io/nix-bindings-rust/development/nix_bindings_bdwgc_sys/) + ## Changelog See the [nix-bindings-rust changelog](https://github.com/nixops4/nix-bindings-rust/blob/main/CHANGELOG.md). diff --git a/nix-bindings-expr-sys/Cargo.toml b/nix-bindings-expr-sys/Cargo.toml index 4f8e373..0cf3e12 100644 --- a/nix-bindings-expr-sys/Cargo.toml +++ b/nix-bindings-expr-sys/Cargo.toml @@ -6,6 +6,7 @@ build = "build.rs" license = "LGPL-2.1" description = "Low-level FFI bindings to the Nix expression evaluator" repository = "https://github.com/nixops4/nix-bindings-rust" +documentation = "https://nixops4.github.io/nix-bindings-rust/development/nix_bindings_expr_sys/" readme = "README.md" [lib] diff --git a/nix-bindings-expr-sys/README.md b/nix-bindings-expr-sys/README.md index 906f366..9fdb3a0 100644 --- a/nix-bindings-expr-sys/README.md +++ b/nix-bindings-expr-sys/README.md @@ -4,6 +4,8 @@ This crate contains generated bindings for the Nix C API (`nix-expr-c`). **You should not have to use this crate directly,** and so you should probably not add it to your dependencies. Instead, use the `nix-bindings-expr` crate, which _should_ be sufficient. +[API Documentation](https://nixops4.github.io/nix-bindings-rust/development/nix_bindings_expr_sys/) + ## Changelog See the [nix-bindings-rust changelog](https://github.com/nixops4/nix-bindings-rust/blob/main/CHANGELOG.md). diff --git a/nix-bindings-expr/Cargo.toml b/nix-bindings-expr/Cargo.toml index 4b1c777..ef9a373 100644 --- a/nix-bindings-expr/Cargo.toml +++ b/nix-bindings-expr/Cargo.toml @@ -6,6 +6,7 @@ build = "build.rs" license = "LGPL-2.1" description = "Rust bindings to Nix expression evaluator" repository = "https://github.com/nixops4/nix-bindings-rust" +documentation = "https://nixops4.github.io/nix-bindings-rust/development/nix_bindings_expr/" readme = "README.md" [lib] diff --git a/nix-bindings-expr/README.md b/nix-bindings-expr/README.md index ebd889a..cb92996 100644 --- a/nix-bindings-expr/README.md +++ b/nix-bindings-expr/README.md @@ -2,6 +2,8 @@ Rust bindings to the Nix expression evaluator. +[API Documentation](https://nixops4.github.io/nix-bindings-rust/development/nix_bindings_expr/) + ## Changelog See the [nix-bindings-rust changelog](https://github.com/nixops4/nix-bindings-rust/blob/main/CHANGELOG.md). diff --git a/nix-bindings-fetchers-sys/Cargo.toml b/nix-bindings-fetchers-sys/Cargo.toml index 99ff741..2dfaa3b 100644 --- a/nix-bindings-fetchers-sys/Cargo.toml +++ b/nix-bindings-fetchers-sys/Cargo.toml @@ -6,6 +6,7 @@ build = "build.rs" license = "LGPL-2.1" description = "Low-level FFI bindings to the nix-fetchers library" repository = "https://github.com/nixops4/nix-bindings-rust" +documentation = "https://nixops4.github.io/nix-bindings-rust/development/nix_bindings_fetchers_sys/" readme = "README.md" [lib] diff --git a/nix-bindings-fetchers-sys/README.md b/nix-bindings-fetchers-sys/README.md index 87113b6..8230b81 100644 --- a/nix-bindings-fetchers-sys/README.md +++ b/nix-bindings-fetchers-sys/README.md @@ -4,6 +4,8 @@ This crate contains generated bindings for the Nix C API (`nix-fetchers-c`). **You should not have to use this crate directly,** and so you should probably not add it to your dependencies. Instead, use the `nix-bindings-fetchers` crate, which _should_ be sufficient. +[API Documentation](https://nixops4.github.io/nix-bindings-rust/development/nix_bindings_fetchers_sys/) + ## Changelog See the [nix-bindings-rust changelog](https://github.com/nixops4/nix-bindings-rust/blob/main/CHANGELOG.md). diff --git a/nix-bindings-fetchers/Cargo.toml b/nix-bindings-fetchers/Cargo.toml index e0d06cb..4ae993d 100644 --- a/nix-bindings-fetchers/Cargo.toml +++ b/nix-bindings-fetchers/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" license = "LGPL-2.1" description = "Rust bindings to Nix fetchers" repository = "https://github.com/nixops4/nix-bindings-rust" +documentation = "https://nixops4.github.io/nix-bindings-rust/development/nix_bindings_fetchers/" readme = "README.md" [lib] diff --git a/nix-bindings-fetchers/README.md b/nix-bindings-fetchers/README.md index 6d23b13..b8f3941 100644 --- a/nix-bindings-fetchers/README.md +++ b/nix-bindings-fetchers/README.md @@ -2,6 +2,8 @@ Rust bindings to the nix-fetchers library. +[API Documentation](https://nixops4.github.io/nix-bindings-rust/development/nix_bindings_fetchers/) + ## Changelog See the [nix-bindings-rust changelog](https://github.com/nixops4/nix-bindings-rust/blob/main/CHANGELOG.md). diff --git a/nix-bindings-flake-sys/Cargo.toml b/nix-bindings-flake-sys/Cargo.toml index fe83419..987cd6d 100644 --- a/nix-bindings-flake-sys/Cargo.toml +++ b/nix-bindings-flake-sys/Cargo.toml @@ -6,6 +6,7 @@ build = "build.rs" license = "LGPL-2.1" description = "Low-level FFI bindings to Nix flakes" repository = "https://github.com/nixops4/nix-bindings-rust" +documentation = "https://nixops4.github.io/nix-bindings-rust/development/nix_bindings_flake_sys/" readme = "README.md" [lib] diff --git a/nix-bindings-flake-sys/README.md b/nix-bindings-flake-sys/README.md index 84114d7..1d26c8d 100644 --- a/nix-bindings-flake-sys/README.md +++ b/nix-bindings-flake-sys/README.md @@ -4,6 +4,8 @@ This crate contains generated bindings for the Nix C API (`nix-flake-c`). **You should not have to use this crate directly,** and so you should probably not add it to your dependencies. Instead, use the `nix-bindings-flake` crate, which _should_ be sufficient. +[API Documentation](https://nixops4.github.io/nix-bindings-rust/development/nix_bindings_flake_sys/) + ## Changelog See the [nix-bindings-rust changelog](https://github.com/nixops4/nix-bindings-rust/blob/main/CHANGELOG.md). diff --git a/nix-bindings-flake/Cargo.toml b/nix-bindings-flake/Cargo.toml index 336b1f8..5fbb42a 100644 --- a/nix-bindings-flake/Cargo.toml +++ b/nix-bindings-flake/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" license = "LGPL-2.1" description = "Rust bindings to Nix flakes" repository = "https://github.com/nixops4/nix-bindings-rust" +documentation = "https://nixops4.github.io/nix-bindings-rust/development/nix_bindings_flake/" readme = "README.md" [lib] diff --git a/nix-bindings-flake/README.md b/nix-bindings-flake/README.md index 6fda25d..43dd11b 100644 --- a/nix-bindings-flake/README.md +++ b/nix-bindings-flake/README.md @@ -2,6 +2,8 @@ Rust bindings to Nix flakes. +[API Documentation](https://nixops4.github.io/nix-bindings-rust/development/nix_bindings_flake/) + ## Changelog See the [nix-bindings-rust changelog](https://github.com/nixops4/nix-bindings-rust/blob/main/CHANGELOG.md). diff --git a/nix-bindings-store-sys/Cargo.toml b/nix-bindings-store-sys/Cargo.toml index f9f5ee2..9e8c374 100644 --- a/nix-bindings-store-sys/Cargo.toml +++ b/nix-bindings-store-sys/Cargo.toml @@ -6,6 +6,7 @@ build = "build.rs" license = "LGPL-2.1" description = "Low-level FFI bindings to the Nix store library" repository = "https://github.com/nixops4/nix-bindings-rust" +documentation = "https://nixops4.github.io/nix-bindings-rust/development/nix_bindings_store_sys/" readme = "README.md" [lib] diff --git a/nix-bindings-store-sys/README.md b/nix-bindings-store-sys/README.md index 31a2d7e..936505d 100644 --- a/nix-bindings-store-sys/README.md +++ b/nix-bindings-store-sys/README.md @@ -4,6 +4,8 @@ This crate contains generated bindings for the Nix C API (`nix-store-c`). **You should not have to use this crate directly,** and so you should probably not add it to your dependencies. Instead, use the `nix-bindings-store` crate, which _should_ be sufficient. +[API Documentation](https://nixops4.github.io/nix-bindings-rust/development/nix_bindings_store_sys/) + ## Changelog See the [nix-bindings-rust changelog](https://github.com/nixops4/nix-bindings-rust/blob/main/CHANGELOG.md). diff --git a/nix-bindings-store/Cargo.toml b/nix-bindings-store/Cargo.toml index 83aab99..24336db 100644 --- a/nix-bindings-store/Cargo.toml +++ b/nix-bindings-store/Cargo.toml @@ -6,6 +6,7 @@ build = "build.rs" license = "LGPL-2.1" description = "Rust bindings to Nix store library" repository = "https://github.com/nixops4/nix-bindings-rust" +documentation = "https://nixops4.github.io/nix-bindings-rust/development/nix_bindings_store/" readme = "README.md" [lib] diff --git a/nix-bindings-store/README.md b/nix-bindings-store/README.md index 1df35eb..8002364 100644 --- a/nix-bindings-store/README.md +++ b/nix-bindings-store/README.md @@ -2,6 +2,8 @@ Rust bindings to the Nix store library. +[API Documentation](https://nixops4.github.io/nix-bindings-rust/development/nix_bindings_store/) + ## Changelog See the [nix-bindings-rust changelog](https://github.com/nixops4/nix-bindings-rust/blob/main/CHANGELOG.md). diff --git a/nix-bindings-util-sys/Cargo.toml b/nix-bindings-util-sys/Cargo.toml index 7a0d01c..2441074 100644 --- a/nix-bindings-util-sys/Cargo.toml +++ b/nix-bindings-util-sys/Cargo.toml @@ -6,6 +6,7 @@ build = "build.rs" license = "LGPL-2.1" description = "Low-level FFI bindings to Nix utility library" repository = "https://github.com/nixops4/nix-bindings-rust" +documentation = "https://nixops4.github.io/nix-bindings-rust/development/nix_bindings_util_sys/" readme = "README.md" [lib] diff --git a/nix-bindings-util-sys/README.md b/nix-bindings-util-sys/README.md index 7e762d5..b504cb8 100644 --- a/nix-bindings-util-sys/README.md +++ b/nix-bindings-util-sys/README.md @@ -4,6 +4,8 @@ This crate contains generated bindings for the Nix C API (`nix-util-c`). **You should not have to use this crate directly,** and so you should probably not add it to your dependencies. Instead, use the `nix-bindings-util` crate, which _should_ be sufficient. +[API Documentation](https://nixops4.github.io/nix-bindings-rust/development/nix_bindings_util_sys/) + ## Changelog See the [nix-bindings-rust changelog](https://github.com/nixops4/nix-bindings-rust/blob/main/CHANGELOG.md). diff --git a/nix-bindings-util/Cargo.toml b/nix-bindings-util/Cargo.toml index 6a32fb1..78e8353 100644 --- a/nix-bindings-util/Cargo.toml +++ b/nix-bindings-util/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" license = "LGPL-2.1" description = "Rust bindings to Nix utility library" repository = "https://github.com/nixops4/nix-bindings-rust" +documentation = "https://nixops4.github.io/nix-bindings-rust/development/nix_bindings_util/" readme = "README.md" [lib] diff --git a/nix-bindings-util/README.md b/nix-bindings-util/README.md index 895b7cf..0a29f29 100644 --- a/nix-bindings-util/README.md +++ b/nix-bindings-util/README.md @@ -2,6 +2,8 @@ Rust bindings to the Nix utility library. +[API Documentation](https://nixops4.github.io/nix-bindings-rust/development/nix_bindings_util/) + ## Changelog See the [nix-bindings-rust changelog](https://github.com/nixops4/nix-bindings-rust/blob/main/CHANGELOG.md). From 6ee502a4f94fc2de189f825db9998b036ec74452 Mon Sep 17 00:00:00 2001 From: Hercules CI Effects Date: Sun, 1 Mar 2026 02:33:10 +0000 Subject: [PATCH 265/306] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'flake-parts': 'github:hercules-ci/flake-parts/80daad04eddbbf5a4d883996a73f3f542fa437ac?narHash=sha256-PVvu7OqHBGWN16zSi6tEmPwwHQ4rLPU9Plvs8/1TUBY%3D' (2026-01-11) → 'github:hercules-ci/flake-parts/57928607ea566b5db3ad13af0e57e921e6b12381?narHash=sha256-AnYjnFWgS49RlqX7LrC4uA%2BsCCDBj0Ry/WOJ5XWAsa0%3D' (2026-02-02) • Updated input 'flake-parts/nixpkgs-lib': 'github:nix-community/nixpkgs.lib/2075416fcb47225d9b68ac469a5c4801a9c4dd85?narHash=sha256-k00uTP4JNfmejrCLJOwdObYC9jHRrr/5M/a/8L2EIdo%3D' (2025-12-14) → 'github:nix-community/nixpkgs.lib/72716169fe93074c333e8d0173151350670b824c?narHash=sha256-cBEymOf4/o3FD5AZnzC3J9hLbiZ%2BQDT/KDuyHXVJOpM%3D' (2026-02-01) • Updated input 'nix': 'github:NixOS/nix/77b6b01b727f0cd1324e431a32a8854768b957ef?narHash=sha256-vpI7XEfX5zeCVRANUzhMNsZfrMWuN0rwNenQ3z0rJNo%3D' (2026-01-30) → 'github:NixOS/nix/0acd0566e85e4597269482824711bcde7b518600?narHash=sha256-jJIlRLPPVYu860MVFx4gsRx3sskmLDSRWXXue5tYncw%3D' (2026-02-27) • Updated input 'nix-cargo-integration': 'github:90-008/nix-cargo-integration/6d583e2098fa3df490c2597df06386e3efcc39b6?narHash=sha256-bjtDp0NHjLjDOjklQVHCDCVM5q39zDzuwenNri0p4Ys%3D' (2026-01-31) → 'github:90-008/nix-cargo-integration/c783c5dff02c06f2af6226d4dd4d494542d0a4d2?narHash=sha256-NaUqM0i6XIGdgRNxxQ9sfgCAVeE2Ko9rz7e19RsNUKw%3D' (2026-02-28) • Updated input 'nix-cargo-integration/parts': 'github:hercules-ci/flake-parts/80daad04eddbbf5a4d883996a73f3f542fa437ac?narHash=sha256-PVvu7OqHBGWN16zSi6tEmPwwHQ4rLPU9Plvs8/1TUBY%3D' (2026-01-11) → 'github:hercules-ci/flake-parts/57928607ea566b5db3ad13af0e57e921e6b12381?narHash=sha256-AnYjnFWgS49RlqX7LrC4uA%2BsCCDBj0Ry/WOJ5XWAsa0%3D' (2026-02-02) • Updated input 'nix-cargo-integration/rust-overlay': 'github:oxalica/rust-overlay/a1d32c90c8a4ea43e9586b7e5894c179d5747425?narHash=sha256-zmnvRUm15QrlKH0V1BZoiT3U%2BQ%2Btr%2BP5Osi8qgtL9fY%3D' (2026-01-31) → 'github:oxalica/rust-overlay/a1ab5e89ab12e1a37c0b264af6386a7472d68a15?narHash=sha256-x6IFQ9bL7YYfW2m2z8D3Em2YtAA3HE8kiCFwai2fwrw%3D' (2026-02-28) • Updated input 'nix-cargo-integration/treefmt': 'github:numtide/treefmt-nix/28b19c5844cc6e2257801d43f2772a4b4c050a1b?narHash=sha256-8aAYwyVzSSwIhP2glDhw/G0i5%2BwOrren3v6WmxkVonM%3D' (2026-01-29) → 'github:numtide/treefmt-nix/337a4fe074be1042a35086f15481d763b8ddc0e7?narHash=sha256-wQ6NJSuFqAEmIg2VMnLdCnUc0b7vslUohqqGGD%2BFyxk%3D' (2026-02-04) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/bfc1b8a4574108ceef22f02bafcf6611380c100d?narHash=sha256-msG8SU5WsBUfVVa/9RPLaymvi5bI8edTavbIq3vRlhI%3D' (2026-01-26) → 'github:NixOS/nixpkgs/dd9b079222d43e1943b6ebd802f04fd959dc8e61?narHash=sha256-I45esRSssFtJ8p/gLHUZ1OUaaTaVLluNkABkk6arQwE%3D' (2026-02-27) --- flake.lock | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/flake.lock b/flake.lock index 168537c..da94010 100644 --- a/flake.lock +++ b/flake.lock @@ -77,11 +77,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1768135262, - "narHash": "sha256-PVvu7OqHBGWN16zSi6tEmPwwHQ4rLPU9Plvs8/1TUBY=", + "lastModified": 1769996383, + "narHash": "sha256-AnYjnFWgS49RlqX7LrC4uA+sCCDBj0Ry/WOJ5XWAsa0=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "80daad04eddbbf5a4d883996a73f3f542fa437ac", + "rev": "57928607ea566b5db3ad13af0e57e921e6b12381", "type": "github" }, "original": { @@ -170,11 +170,11 @@ "nixpkgs-regression": "nixpkgs-regression" }, "locked": { - "lastModified": 1769798267, - "narHash": "sha256-vpI7XEfX5zeCVRANUzhMNsZfrMWuN0rwNenQ3z0rJNo=", + "lastModified": 1772224943, + "narHash": "sha256-jJIlRLPPVYu860MVFx4gsRx3sskmLDSRWXXue5tYncw=", "owner": "NixOS", "repo": "nix", - "rev": "77b6b01b727f0cd1324e431a32a8854768b957ef", + "rev": "0acd0566e85e4597269482824711bcde7b518600", "type": "github" }, "original": { @@ -196,11 +196,11 @@ "treefmt": "treefmt" }, "locked": { - "lastModified": 1769840916, - "narHash": "sha256-bjtDp0NHjLjDOjklQVHCDCVM5q39zDzuwenNri0p4Ys=", + "lastModified": 1772260057, + "narHash": "sha256-NaUqM0i6XIGdgRNxxQ9sfgCAVeE2Ko9rz7e19RsNUKw=", "owner": "90-008", "repo": "nix-cargo-integration", - "rev": "6d583e2098fa3df490c2597df06386e3efcc39b6", + "rev": "c783c5dff02c06f2af6226d4dd4d494542d0a4d2", "type": "github" }, "original": { @@ -211,11 +211,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1769461804, - "narHash": "sha256-msG8SU5WsBUfVVa/9RPLaymvi5bI8edTavbIq3vRlhI=", + "lastModified": 1772198003, + "narHash": "sha256-I45esRSssFtJ8p/gLHUZ1OUaaTaVLluNkABkk6arQwE=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "bfc1b8a4574108ceef22f02bafcf6611380c100d", + "rev": "dd9b079222d43e1943b6ebd802f04fd959dc8e61", "type": "github" }, "original": { @@ -243,11 +243,11 @@ }, "nixpkgs-lib": { "locked": { - "lastModified": 1765674936, - "narHash": "sha256-k00uTP4JNfmejrCLJOwdObYC9jHRrr/5M/a/8L2EIdo=", + "lastModified": 1769909678, + "narHash": "sha256-cBEymOf4/o3FD5AZnzC3J9hLbiZ+QDT/KDuyHXVJOpM=", "owner": "nix-community", "repo": "nixpkgs.lib", - "rev": "2075416fcb47225d9b68ac469a5c4801a9c4dd85", + "rev": "72716169fe93074c333e8d0173151350670b824c", "type": "github" }, "original": { @@ -280,11 +280,11 @@ ] }, "locked": { - "lastModified": 1768135262, - "narHash": "sha256-PVvu7OqHBGWN16zSi6tEmPwwHQ4rLPU9Plvs8/1TUBY=", + "lastModified": 1769996383, + "narHash": "sha256-AnYjnFWgS49RlqX7LrC4uA+sCCDBj0Ry/WOJ5XWAsa0=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "80daad04eddbbf5a4d883996a73f3f542fa437ac", + "rev": "57928607ea566b5db3ad13af0e57e921e6b12381", "type": "github" }, "original": { @@ -355,11 +355,11 @@ ] }, "locked": { - "lastModified": 1769828398, - "narHash": "sha256-zmnvRUm15QrlKH0V1BZoiT3U+Q+tr+P5Osi8qgtL9fY=", + "lastModified": 1772247314, + "narHash": "sha256-x6IFQ9bL7YYfW2m2z8D3Em2YtAA3HE8kiCFwai2fwrw=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "a1d32c90c8a4ea43e9586b7e5894c179d5747425", + "rev": "a1ab5e89ab12e1a37c0b264af6386a7472d68a15", "type": "github" }, "original": { @@ -399,11 +399,11 @@ ] }, "locked": { - "lastModified": 1769691507, - "narHash": "sha256-8aAYwyVzSSwIhP2glDhw/G0i5+wOrren3v6WmxkVonM=", + "lastModified": 1770228511, + "narHash": "sha256-wQ6NJSuFqAEmIg2VMnLdCnUc0b7vslUohqqGGD+Fyxk=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "28b19c5844cc6e2257801d43f2772a4b4c050a1b", + "rev": "337a4fe074be1042a35086f15481d763b8ddc0e7", "type": "github" }, "original": { From 1e61578f3589377d4ed8f6d879a74940f4086690 Mon Sep 17 00:00:00 2001 From: Hercules CI Effects Date: Sun, 1 Mar 2026 02:33:12 +0000 Subject: [PATCH 266/306] dev/flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'hercules-ci-effects': 'github:hercules-ci/hercules-ci-effects/c19e263e6e22ec7379d972f19e6a322f943c73fb?narHash=sha256-V0YOJRum50gtKgwavsAfwXc9%2BXAsJCC7386YZx1sWGQ%3D' (2026-01-15) → 'github:hercules-ci/hercules-ci-effects/0b152e0f7c5cc265a529cd63374b80e2771b207b?narHash=sha256-HPBNYf7HiKtBVy7/69vKpLYHX6wTcUxndxmybzDlXP8%3D' (2026-02-15) • Updated input 'hercules-ci-effects/flake-parts': 'github:hercules-ci/flake-parts/80daad04eddbbf5a4d883996a73f3f542fa437ac?narHash=sha256-PVvu7OqHBGWN16zSi6tEmPwwHQ4rLPU9Plvs8/1TUBY%3D' (2026-01-11) → 'github:hercules-ci/flake-parts/57928607ea566b5db3ad13af0e57e921e6b12381?narHash=sha256-AnYjnFWgS49RlqX7LrC4uA%2BsCCDBj0Ry/WOJ5XWAsa0%3D' (2026-02-02) • Updated input 'hercules-ci-effects/nixpkgs': 'github:NixOS/nixpkgs/1412caf7bf9e660f2f962917c14b1ea1c3bc695e?narHash=sha256-AIdl6WAn9aymeaH/NvBj0H9qM%2BXuAuYbGMZaP0zcXAQ%3D' (2026-01-13) → 'github:NixOS/nixpkgs/a82ccc39b39b621151d6732718e3e250109076fa?narHash=sha256-gf2AmWVTs8lEq7z/3ZAsgnZDhWIckkb%2BZnAo5RzSxJg%3D' (2026-02-13) • Updated input 'pre-commit-hooks-nix': 'github:cachix/pre-commit-hooks.nix/a1ef738813b15cf8ec759bdff5761b027e3e1d23?narHash=sha256-Efs3VUPelRduf3PpfPP2ovEB4CXT7vHf8W%2Bxc49RL/U%3D' (2026-01-22) → 'github:cachix/pre-commit-hooks.nix/6e34e97ed9788b17796ee43ccdbaf871a5c2b476?narHash=sha256-%2BeXlIc4/7dE6EcPs9a2DaSY3fTA9AE526hGqkNID3Wg%3D' (2026-02-25) • Updated input 'treefmt-nix': 'github:numtide/treefmt-nix/28b19c5844cc6e2257801d43f2772a4b4c050a1b?narHash=sha256-8aAYwyVzSSwIhP2glDhw/G0i5%2BwOrren3v6WmxkVonM%3D' (2026-01-29) → 'github:numtide/treefmt-nix/337a4fe074be1042a35086f15481d763b8ddc0e7?narHash=sha256-wQ6NJSuFqAEmIg2VMnLdCnUc0b7vslUohqqGGD%2BFyxk%3D' (2026-02-04) --- dev/flake.lock | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/dev/flake.lock b/dev/flake.lock index 44ec905..1604bb7 100644 --- a/dev/flake.lock +++ b/dev/flake.lock @@ -24,11 +24,11 @@ ] }, "locked": { - "lastModified": 1768135262, - "narHash": "sha256-PVvu7OqHBGWN16zSi6tEmPwwHQ4rLPU9Plvs8/1TUBY=", + "lastModified": 1769996383, + "narHash": "sha256-AnYjnFWgS49RlqX7LrC4uA+sCCDBj0Ry/WOJ5XWAsa0=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "80daad04eddbbf5a4d883996a73f3f542fa437ac", + "rev": "57928607ea566b5db3ad13af0e57e921e6b12381", "type": "github" }, "original": { @@ -63,11 +63,11 @@ "nixpkgs": "nixpkgs" }, "locked": { - "lastModified": 1768476106, - "narHash": "sha256-V0YOJRum50gtKgwavsAfwXc9+XAsJCC7386YZx1sWGQ=", + "lastModified": 1771131391, + "narHash": "sha256-HPBNYf7HiKtBVy7/69vKpLYHX6wTcUxndxmybzDlXP8=", "owner": "hercules-ci", "repo": "hercules-ci-effects", - "rev": "c19e263e6e22ec7379d972f19e6a322f943c73fb", + "rev": "0b152e0f7c5cc265a529cd63374b80e2771b207b", "type": "github" }, "original": { @@ -78,11 +78,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1768305791, - "narHash": "sha256-AIdl6WAn9aymeaH/NvBj0H9qM+XuAuYbGMZaP0zcXAQ=", + "lastModified": 1771008912, + "narHash": "sha256-gf2AmWVTs8lEq7z/3ZAsgnZDhWIckkb+ZnAo5RzSxJg=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "1412caf7bf9e660f2f962917c14b1ea1c3bc695e", + "rev": "a82ccc39b39b621151d6732718e3e250109076fa", "type": "github" }, "original": { @@ -99,11 +99,11 @@ "nixpkgs": [] }, "locked": { - "lastModified": 1769069492, - "narHash": "sha256-Efs3VUPelRduf3PpfPP2ovEB4CXT7vHf8W+xc49RL/U=", + "lastModified": 1772024342, + "narHash": "sha256-+eXlIc4/7dE6EcPs9a2DaSY3fTA9AE526hGqkNID3Wg=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "a1ef738813b15cf8ec759bdff5761b027e3e1d23", + "rev": "6e34e97ed9788b17796ee43ccdbaf871a5c2b476", "type": "github" }, "original": { @@ -124,11 +124,11 @@ "nixpkgs": [] }, "locked": { - "lastModified": 1769691507, - "narHash": "sha256-8aAYwyVzSSwIhP2glDhw/G0i5+wOrren3v6WmxkVonM=", + "lastModified": 1770228511, + "narHash": "sha256-wQ6NJSuFqAEmIg2VMnLdCnUc0b7vslUohqqGGD+Fyxk=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "28b19c5844cc6e2257801d43f2772a4b4c050a1b", + "rev": "337a4fe074be1042a35086f15481d763b8ddc0e7", "type": "github" }, "original": { From cf0e8fff6a14bcc4d0066020c5125b646cb09601 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 2 Mar 2026 23:22:08 +0100 Subject: [PATCH 267/306] test(nix-bindings-store): accept new Nix error format for invalid system builds Nix changed "required system or feature not available" to "platform mismatch". --- nix-bindings-store/src/store.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nix-bindings-store/src/store.rs b/nix-bindings-store/src/store.rs index 655a088..cdd0977 100644 --- a/nix-bindings-store/src/store.rs +++ b/nix-bindings-store/src/store.rs @@ -788,7 +788,8 @@ mod tests { Err(e) => e.to_string(), }; assert!( - err.contains("required system or feature not available"), + err.contains("required system or feature not available") + || err.contains("platform mismatch"), "Error should mention system not available, got: {}", err ); From fbf5fab083586234e2e6092a08cc93980c083360 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 2 Mar 2026 23:53:53 +0100 Subject: [PATCH 268/306] feat(nix-bindings-expr): add RecoverableError for non-memoized primop errors Nix 2.34 memoizes primop errors by default. RecoverableError uses NIX_ERR_RECOVERABLE so transient errors allow the thunk to be retried. --- CHANGELOG.md | 4 ++ nix-bindings-expr/build.rs | 2 +- nix-bindings-expr/src/eval_state.rs | 77 +++++++++++++++++++++++++++++ nix-bindings-expr/src/primop.rs | 45 ++++++++++++++++- 4 files changed, 126 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index da22679..307a96b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- `primop::RecoverableError` for primop errors that should not be memoized in the thunk, allowing retry on next force. Required by Nix >= 2.34 ([release note](https://nix.dev/manual/nix/2.34/release-notes/rl-2.34.html#c-api-changes)) for recoverable errors to remain recoverable, as Nix 2.34 memoizes errors by default. + ## [0.2.0] - 2026-01-13 ### Added diff --git a/nix-bindings-expr/build.rs b/nix-bindings-expr/build.rs index 6a038bd..f56bc3e 100644 --- a/nix-bindings-expr/build.rs +++ b/nix-bindings-expr/build.rs @@ -2,5 +2,5 @@ use nix_bindings_util::nix_version::emit_version_cfg; fn main() { let nix_version = pkg_config::probe_library("nix-expr-c").unwrap().version; - emit_version_cfg(&nix_version, &["2.26"]); + emit_version_cfg(&nix_version, &["2.26", "2.34.0pre"]); } diff --git a/nix-bindings-expr/src/eval_state.rs b/nix-bindings-expr/src/eval_state.rs index b8e27e6..5d8599f 100644 --- a/nix-bindings-expr/src/eval_state.rs +++ b/nix-bindings-expr/src/eval_state.rs @@ -2845,4 +2845,81 @@ mod tests { }) .unwrap(); } + + #[test] + #[cfg(nix_at_least = "2.34.0pre")] + fn eval_state_primop_recoverable_error() { + gc_registering_current_thread(|| { + let store = Store::open(None, []).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); + + let call_count = std::cell::Cell::new(0u32); + let v = es + .new_value_thunk( + "recoverable_test", + Box::new(move |es: &mut EvalState| { + let count = call_count.get(); + call_count.set(count + 1); + if count == 0 { + Err(primop::RecoverableError::new("transient failure").into()) + } else { + es.new_value_int(42) + } + }), + ) + .unwrap(); + + // First force should fail with the recoverable error + let r = es.force(&v); + assert!(r.is_err()); + assert!( + r.unwrap_err().to_string().contains("transient failure"), + "Error message should contain 'transient failure'" + ); + + // Second force should succeed because the error was recoverable + es.force(&v).unwrap(); + let i = es.require_int(&v).unwrap(); + assert_eq!(i, 42); + }) + .unwrap(); + } + + #[test] + #[cfg(nix_at_least = "2.34.0pre")] + fn eval_state_primop_recoverable_error_in_chain() { + gc_registering_current_thread(|| { + let store = Store::open(None, []).unwrap(); + let mut es = EvalState::new(store, []).unwrap(); + + let call_count = std::cell::Cell::new(0u32); + let v = es + .new_value_thunk( + "recoverable_chain_test", + Box::new(move |es: &mut EvalState| { + let count = call_count.get(); + call_count.set(count + 1); + if count == 0 { + // Wrap RecoverableError in .context(), pushing it down the chain + use anyhow::Context; + Err(primop::RecoverableError::new("transient failure")) + .context("wrapper context")? + } else { + es.new_value_int(42) + } + }), + ) + .unwrap(); + + // First force should fail + let r = es.force(&v); + assert!(r.is_err()); + + // Second force should succeed if RecoverableError is found in the chain + es.force(&v).unwrap(); + let i = es.require_int(&v).unwrap(); + assert_eq!(i, 42); + }) + .unwrap(); + } } diff --git a/nix-bindings-expr/src/primop.rs b/nix-bindings-expr/src/primop.rs index 02410fc..1504405 100644 --- a/nix-bindings-expr/src/primop.rs +++ b/nix-bindings-expr/src/primop.rs @@ -8,6 +8,35 @@ use std::ffi::{c_int, c_void, CStr, CString}; use std::mem::ManuallyDrop; use std::ptr::{null, null_mut}; +/// A primop error that is not memoized in the thunk that triggered it, +/// allowing the thunk to be forced again. +/// +/// Since [Nix 2.34](https://nix.dev/manual/nix/2.34/release-notes/rl-2.34.html#c-api-changes), +/// primop errors are memoized by default: once a thunk fails, forcing it +/// again returns the same error. Use `RecoverableError` for errors that +/// are transient, so the caller can retry. +/// +/// On Nix < 2.34, all errors are already recoverable, so this type has +/// no additional effect. +/// +/// Available since nix-bindings-expr 0.2.1. +#[derive(Debug)] +pub struct RecoverableError(String); + +impl RecoverableError { + pub fn new(msg: impl Into) -> Self { + RecoverableError(msg.into()) + } +} + +impl std::fmt::Display for RecoverableError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } +} + +impl std::error::Error for RecoverableError {} + /// Metadata for a primop, used with `PrimOp::new`. pub struct PrimOpMeta<'a, const N: usize> { /// Name of the primop. Note that primops do not have to be registered as @@ -36,6 +65,11 @@ impl Drop for PrimOp { } } impl PrimOp { + /// Create a new primop with the given metadata and implementation. + /// + /// When `f` returns an `Err`, the error is propagated to the Nix evaluator. + /// To return a [recoverable error](RecoverableError), include it in the + /// error chain (e.g. `Err(RecoverableError::new("...").into())`). pub fn new( eval_state: &mut EvalState, meta: PrimOpMeta, @@ -108,13 +142,22 @@ unsafe extern "C" fn function_adapter( raw::copy_value(context_out, ret, v.raw_ptr()); }, Err(e) => unsafe { + let err_code = error_code(&e); let cstr = CString::new(e.to_string()).unwrap_or_else(|_e| { CString::new("") .unwrap() }); - raw_util::set_err_msg(context_out, raw_util::err_NIX_ERR_UNKNOWN, cstr.as_ptr()); + raw_util::set_err_msg(context_out, err_code, cstr.as_ptr()); }, } } +fn error_code(e: &anyhow::Error) -> raw_util::err { + #[cfg(nix_at_least = "2.34.0pre")] + if e.downcast_ref::().is_some() { + return raw_util::err_NIX_ERR_RECOVERABLE; + } + raw_util::err_NIX_ERR_UNKNOWN +} + static FUNCTION_ADAPTER: raw::PrimOpFun = Some(function_adapter); From 716c028bb159e62d3d295c5101d50ee117f027c6 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Thu, 12 Mar 2026 21:39:54 +1000 Subject: [PATCH 269/306] FlakeReference derives Clone --- nix-bindings-flake/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/nix-bindings-flake/src/lib.rs b/nix-bindings-flake/src/lib.rs index cb8e850..beb6b31 100644 --- a/nix-bindings-flake/src/lib.rs +++ b/nix-bindings-flake/src/lib.rs @@ -98,6 +98,7 @@ impl FlakeReferenceParseFlags { } } +#[derive(Clone)] pub struct FlakeReference { pub(crate) ptr: NonNull, } From 27d68457153b6a05e8ea545de05a03a314e6320a Mon Sep 17 00:00:00 2001 From: foxxyora Date: Thu, 12 Mar 2026 19:03:10 +0100 Subject: [PATCH 270/306] Initial commit --- LICENSE | 232 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 3 + 2 files changed, 235 insertions(+) create mode 100644 LICENSE create mode 100644 README.md diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4aee4d1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,232 @@ +GNU GENERAL PUBLIC LICENSE +Version 3, 29 June 2007 + +Copyright © 2007 Free Software Foundation, Inc. + +Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. + +Preamble + +The GNU General Public License is a free, copyleft license for software and other kinds of works. + +The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. + +To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. + +For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. + +Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. + +For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. + +Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. + +Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. + +The precise terms and conditions for copying, distribution and modification follow. + +TERMS AND CONDITIONS + +0. Definitions. + +“This License” refers to version 3 of the GNU General Public License. + +“Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. + +“The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. “Licensees” and “recipients” may be individuals or organizations. + +To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work. + +A “covered work” means either the unmodified Program or a work based on the Program. + +To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. + +To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. + +An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. + +1. Source Code. +The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any non-source form of a work. + +A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. + +The “System Libraries” of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Component”, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. + +The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. + +The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. + +The Corresponding Source for a work in source code form is that same work. + +2. Basic Permissions. +All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. + +3. Protecting Users' Legal Rights From Anti-Circumvention Law. +No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. + +When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. + +4. Conveying Verbatim Copies. +You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. + +5. Conveying Modified Source Versions. +You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”. + + c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. + +A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. + +6. Conveying Non-Source Forms. +You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: + + a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. + + d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. + +A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. + +A “User Product” is either (1) a “consumer product”, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used” refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. + +“Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. + +If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). + +The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. + +Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. + +7. Additional Terms. +“Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or authors of the material; or + + e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. + +All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. + +8. Termination. +You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). + +However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. + +Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. + +Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. + +9. Acceptance Not Required for Having Copies. +You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. + +10. Automatic Licensing of Downstream Recipients. +Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. + +An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. + +11. Patents. +A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's “contributor version”. + +A contributor's “essential patent claims” are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. + +In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. + +If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. + +A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. + +12. No Surrender of Others' Freedom. +If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. + +13. Use with the GNU Affero General Public License. +Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. + +14. Revised Versions of this License. +The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. + +If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. + +Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. + +15. Disclaimer of Warranty. +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. Limitation of Liability. +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +17. Interpretation of Sections 15 and 16. +If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found. + + nixide + Copyright (C) 2026 luminary + + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + +If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: + + nixide Copyright (C) 2026 luminary + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an “about box”. + +You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimer” for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . + +The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . diff --git a/README.md b/README.md new file mode 100644 index 0000000..f40390a --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# nixide + +rust wrapper for libnix :3 \ No newline at end of file From 797923f7c50d608e1c6afb110860dc8e5e5b83a1 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Fri, 13 Mar 2026 12:18:22 +1000 Subject: [PATCH 271/306] add gitignore --- .gitignore | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aeed55a --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +# Nix +result +result-* + +# Rust +**/target From 9aa022ee41b6fab27bb2baac0b9db3b295d6637a Mon Sep 17 00:00:00 2001 From: _cry64 Date: Fri, 13 Mar 2026 12:23:50 +1000 Subject: [PATCH 272/306] bindgen-rs references :3 --- .gitignore | 3 +++ docs/ref.md | 3 +++ 2 files changed, 6 insertions(+) create mode 100644 docs/ref.md diff --git a/.gitignore b/.gitignore index aeed55a..1bd074b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,6 @@ result-* # Rust **/target + +# Other +nixops4-bindings-sys diff --git a/docs/ref.md b/docs/ref.md new file mode 100644 index 0000000..db53d00 --- /dev/null +++ b/docs/ref.md @@ -0,0 +1,3 @@ +# Nix Bindgen-rs References +- https://github.com/NotAShelf/nix-bindings +- https://github.com/nixops4/nix-bindings-rust From 87612df7b69dd40a35687f94206a851e13589812 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Fri, 13 Mar 2026 13:26:07 +1000 Subject: [PATCH 273/306] add flake.nix --- flake.lock | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ flake.nix | 74 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 156 insertions(+) create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..de5e452 --- /dev/null +++ b/flake.lock @@ -0,0 +1,82 @@ +{ + "nodes": { + "fenix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "rust-analyzer-src": "rust-analyzer-src" + }, + "locked": { + "lastModified": 1773299640, + "narHash": "sha256-kTsZ5xGZqaeJ8jWsfZNACo/VsW3riVuIQEPWVGiqWKM=", + "owner": "nix-community", + "repo": "fenix", + "rev": "8ac78ff968869cd05d9cb42fbf63bdbc6851ec19", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "fenix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1773222311, + "narHash": "sha256-BHoB/XpbqoZkVYZCfXJXfkR+GXFqwb/4zbWnOr2cRcU=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "0590cd39f728e129122770c029970378a79d076a", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-25.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "fenix": "fenix", + "nixpkgs": "nixpkgs", + "systems": "systems" + } + }, + "rust-analyzer-src": { + "flake": false, + "locked": { + "lastModified": 1773194001, + "narHash": "sha256-50PPXBtH2xfKuNfQfUNOyuIFgZPEz5QVertQWS2MQJE=", + "owner": "rust-lang", + "repo": "rust-analyzer", + "rev": "8ed3cca4d30610fd0d3c1179c85418de2dc0a7c1", + "type": "github" + }, + "original": { + "owner": "rust-lang", + "ref": "nightly", + "repo": "rust-analyzer", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..69f8747 --- /dev/null +++ b/flake.nix @@ -0,0 +1,74 @@ +{ + description = "Wire on your TTYs just feels better!"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11"; + systems.url = "github:nix-systems/default"; + + fenix.url = "github:nix-community/fenix"; + fenix.inputs.nixpkgs.follows = "nixpkgs"; + }; + + outputs = { + self, + nixpkgs, + ... + } @ inputs: let + systems = import inputs.systems; + + mkPkgs = system: repo: + import repo { + inherit system; + allowUnfree = false; + allowBroken = false; + overlays = builtins.attrValues self.overlays or {}; + }; + + forAllSystems = f: + nixpkgs.lib.genAttrs systems (system: + f rec { + inherit system; + inherit (pkgs) lib; + pkgs = mkPkgs system nixpkgs; + }); + in { + overlays.default = self: super: { + libclang = super.llvmPackages_21.libclang; + }; + + devShells = forAllSystems ( + { + system, + pkgs, + lib, + ... + }: { + default = pkgs.mkShell rec { + shell = "${pkgs.bash}/bin/bash"; + strictDeps = true; + + packages = with pkgs; [ + cargo + rustc + inputs.fenix.packages.${system}.complete.rustfmt + ]; + + # packages we should be able to link against + buildInputs = with pkgs; [ + # pipewire.dev + # libxkbcommon + # wayland + ]; + + # packages we run at build time / shellHook + nativeBuildInputs = with pkgs; [ + pkg-config + rustPlatform.bindgenHook + ]; + + LD_LIBRARY_PATH = "$LD_LIBRARY_PATH:${builtins.toString (lib.makeLibraryPath buildInputs)}"; + }; + } + ); + }; +} From 969cb97a188d0e54e0409392ae16503c49e0b69e Mon Sep 17 00:00:00 2001 From: _cry64 Date: Fri, 13 Mar 2026 13:26:33 +1000 Subject: [PATCH 274/306] add cargo workspace --- .gitignore | 2 +- Cargo.lock | 257 ++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 19 ++++ nixide-sys/Cargo.toml | 32 ++++++ nixide-sys/lib.rs | 14 +++ 5 files changed, 323 insertions(+), 1 deletion(-) create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 nixide-sys/Cargo.toml create mode 100644 nixide-sys/lib.rs diff --git a/.gitignore b/.gitignore index 1bd074b..b36bbaf 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,7 @@ result result-* # Rust -**/target +/target # Other nixops4-bindings-sys diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..cac84e4 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,257 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "bindgen" +version = "0.72.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools", + "log", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", +] + +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" + +[[package]] +name = "cc" +version = "1.2.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "doxygen-bindgen" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93ba4ed6eedf7f4ace1632149d8f0e8a65a480534024d65a7c3b9daacdedbad3" +dependencies = [ + "yap", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "libc" +version = "0.2.183" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nixide-sys" +version = "0.1.0" +dependencies = [ + "bindgen", + "cc", + "doxygen-bindgen", + "pkg-config", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "yap" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfe269e7b803a5e8e20cbd97860e136529cd83bf2c9c6d37b142467e7e1f051f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..e8dcde9 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,19 @@ +[workspace] +resolver = "3" +members = [ + # "nixide", + "nixide-sys" +] + +[workspace.dependencies] +cc = "1.2.56" +pkg-config = "0.3.32" + +# Building bindgen with optimizations makes the build script run faster, more +# than it is offset by the additional build time added to the crate itself by +# enabling optimizations. Since we work with bindgen, might as well. +# P.S.: it ranges from 3x to 5x faster depending on my system, and it'll only +# get better as the C API expands in size. So all things considered this is a +# good thing :) +[profile.dev.package.bindgen] +opt-level = 3 diff --git a/nixide-sys/Cargo.toml b/nixide-sys/Cargo.toml new file mode 100644 index 0000000..0bbcfcc --- /dev/null +++ b/nixide-sys/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "nixide-sys" +description = "Unsafe direct FFI bindings to libnix C API" +version = "0.1.0" +license = "GPL-3.0" +license-file = "../LICENSE" +authors = ["_cry64 ", + "foxxyora "] +repository = "https://codeberg.org/luminary/nixide" +edition = "2024" + +[package.metadata.docs.rs] +targets = [ "x86_64-unknown-linux-gnu" ] + +[lib] +path = "lib.rs" + +[features] +default = ["util"] +expr = [] +fetchers = [] +flakes = [] +store = [] +util = [] +gc = [] + +[build-dependencies] +bindgen = { default-features = false, features = [ "logging", "runtime" ], version = "0.72.1" } +doxygen-bindgen = "0.1.3" +pkg-config.workspace = true +cc.workspace = true + diff --git a/nixide-sys/lib.rs b/nixide-sys/lib.rs new file mode 100644 index 0000000..b93cf3f --- /dev/null +++ b/nixide-sys/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: u64, right: u64) -> u64 { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} From 25465a9f48f6a81db48c16e5e996c8d19af70ba9 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Fri, 13 Mar 2026 17:16:40 +1000 Subject: [PATCH 275/306] PRAY MORE --- nixide-sys/Cargo.toml | 21 +++++---- nixide-sys/build.rs | 87 ++++++++++++++++++++++++++++++++++++ nixide-sys/include/wrapper.h | 1 + 3 files changed, 101 insertions(+), 8 deletions(-) create mode 100644 nixide-sys/build.rs create mode 100644 nixide-sys/include/wrapper.h diff --git a/nixide-sys/Cargo.toml b/nixide-sys/Cargo.toml index 0bbcfcc..cc2c8a2 100644 --- a/nixide-sys/Cargo.toml +++ b/nixide-sys/Cargo.toml @@ -1,13 +1,18 @@ [package] -name = "nixide-sys" -description = "Unsafe direct FFI bindings to libnix C API" -version = "0.1.0" -license = "GPL-3.0" +name = "nixide-sys" +description = "Unsafe direct FFI bindings to libnix C API" +version = "0.1.0" +readme = "../README.md" +license = "GPL-3.0" license-file = "../LICENSE" -authors = ["_cry64 ", - "foxxyora "] -repository = "https://codeberg.org/luminary/nixide" -edition = "2024" +repository = "https://codeberg.org/luminary/nixide" +authors = [ + "_cry64 " + "foxxyora " +] + +edition = "2024" +build = "build.rs" [package.metadata.docs.rs] targets = [ "x86_64-unknown-linux-gnu" ] diff --git a/nixide-sys/build.rs b/nixide-sys/build.rs new file mode 100644 index 0000000..6d20acf --- /dev/null +++ b/nixide-sys/build.rs @@ -0,0 +1,87 @@ +use std::env; +use std::path::PathBuf; +use std::process::Command; + +use bindgen::callbacks::ParseCallbacks; + +#[derive(Debug)] +struct DoxygenCallbacks; + +impl ParseCallbacks for DoxygenCallbacks { + fn process_comment(&self, comment: &str) -> Option { + match doxygen_bindgen::transform(comment) { + Ok(res) => Some(res), + Err(err) => { + println!("cargo:warning=Problem processing doxygen comment: {comment}\n{err}"); + None + } + } + } +} + +fn main() { + // Invalidate the built crate whenever the wrapper changes + println!("cargo:rerun-if-changed=include/wrapper.h"); + + // Tell cargo to tell rustc to link the system shared library + println!("cargo:rustc-link-lib=bz2"); + + // Use pkg-config to find nix-store include and link paths + // This NEEDS to be included, or otherwise `nix_api_store.h` cannot + // be found. + let libs = [ + "nix-main-c", + "nix-expr-c", + "nix-store-c", + "nix-util-c", + "nix-flake-c", + ]; + + // Add all pkg-config include paths and GCC's include path to bindgen + let mut args = Vec::new(); + for nix_lib in libs { + let lib = pkg_config::probe_library(nix_lib) + .expect(&format!("Unable to find .pc file for {}", nix_lib)); + + for include_path in lib.include_paths { + args.push(format!("-I{}", incloude_path.display())); + // builder = builder.clang_arg(format!("-I{}", include_path.display())); + } + for link_file in lib.link_files { + println!("cargo:rustc-link-lib={}", link_file.display()); + } + } + + let lib_args = libs.map(|name| { + let lib = pkg_config::probe_library(name) + .expect(&format!("Unable to find .pc file for {}", nix_lib)); + + for p in lib.link_files { + println!("cargo:rustc-link-lib={}", p.display()); + } + + lib.include_paths.map(|p| format!("-I{}", p.display())) + }).flatten(); + + let bindings = bindgen::Builder::default() + .clang_args(lib_args) + // The input header we would like to generate bindings for + .header("include/wrapper.h") + // Invalidate the built crate when an included header file changes + .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) + // Add `doxygen_bindgen` callbacks + .parse_callbacks(Box::new(DoxygenCallbacks)) + // Format generated bindings with rustfmt + .formatter(bindgen::Formatter::Rustfmt) + .rustfmt_configuration_file(std::fs::canonicalize(".rustfmt.toml").ok()); + // Finish the builder and generate the bindings + .generate() + // Unwrap the Result and panic on failure + .expect("Unable to generate bindings"); + + // Write the bindings to the $OUT_DIR/bindings.rs file + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + bindings + .write_to_file(out_path.join("bindings.rs")) + .expect("Couldn't write bindings!"); +} diff --git a/nixide-sys/include/wrapper.h b/nixide-sys/include/wrapper.h new file mode 100644 index 0000000..41450d5 --- /dev/null +++ b/nixide-sys/include/wrapper.h @@ -0,0 +1 @@ +#include <> From 4508aeab76849ac62d18ef6744b90a08af3e09cb Mon Sep 17 00:00:00 2001 From: _cry64 Date: Fri, 13 Mar 2026 23:20:38 +1000 Subject: [PATCH 276/306] fix my shit ahh flake --- flake.lock | 39 -------------------- flake.nix | 103 ++++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 78 insertions(+), 64 deletions(-) diff --git a/flake.lock b/flake.lock index de5e452..b306085 100644 --- a/flake.lock +++ b/flake.lock @@ -1,26 +1,5 @@ { "nodes": { - "fenix": { - "inputs": { - "nixpkgs": [ - "nixpkgs" - ], - "rust-analyzer-src": "rust-analyzer-src" - }, - "locked": { - "lastModified": 1773299640, - "narHash": "sha256-kTsZ5xGZqaeJ8jWsfZNACo/VsW3riVuIQEPWVGiqWKM=", - "owner": "nix-community", - "repo": "fenix", - "rev": "8ac78ff968869cd05d9cb42fbf63bdbc6851ec19", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "fenix", - "type": "github" - } - }, "nixpkgs": { "locked": { "lastModified": 1773222311, @@ -39,28 +18,10 @@ }, "root": { "inputs": { - "fenix": "fenix", "nixpkgs": "nixpkgs", "systems": "systems" } }, - "rust-analyzer-src": { - "flake": false, - "locked": { - "lastModified": 1773194001, - "narHash": "sha256-50PPXBtH2xfKuNfQfUNOyuIFgZPEz5QVertQWS2MQJE=", - "owner": "rust-lang", - "repo": "rust-analyzer", - "rev": "8ed3cca4d30610fd0d3c1179c85418de2dc0a7c1", - "type": "github" - }, - "original": { - "owner": "rust-lang", - "ref": "nightly", - "repo": "rust-analyzer", - "type": "github" - } - }, "systems": { "locked": { "lastModified": 1681028828, diff --git a/flake.nix b/flake.nix index 69f8747..721ad93 100644 --- a/flake.nix +++ b/flake.nix @@ -4,9 +4,6 @@ inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11"; systems.url = "github:nix-systems/default"; - - fenix.url = "github:nix-community/fenix"; - fenix.inputs.nixpkgs.follows = "nixpkgs"; }; outputs = { @@ -38,36 +35,92 @@ devShells = forAllSystems ( { - system, pkgs, lib, ... }: { - default = pkgs.mkShell rec { - shell = "${pkgs.bash}/bin/bash"; - strictDeps = true; + default = let + nixForBindings = pkgs.nixVersions.nix_2_32; + inherit (pkgs.rustc) llvmPackages; + in + pkgs.mkShell rec { + name = "nixide"; + shell = "${pkgs.bash}/bin/bash"; + strictDeps = true; - packages = with pkgs; [ - cargo - rustc - inputs.fenix.packages.${system}.complete.rustfmt - ]; + # packages we need at runtime + packages = with pkgs; [ + rustc + llvmPackages.lld + lldb - # packages we should be able to link against - buildInputs = with pkgs; [ - # pipewire.dev - # libxkbcommon - # wayland - ]; + cargo + # cargo-c + cargo-llvm-cov + cargo-nextest - # packages we run at build time / shellHook - nativeBuildInputs = with pkgs; [ - pkg-config - rustPlatform.bindgenHook - ]; + rust-analyzer-unwrapped + (rustfmt.override {asNightly = true;}) + clippy + taplo + ]; - LD_LIBRARY_PATH = "$LD_LIBRARY_PATH:${builtins.toString (lib.makeLibraryPath buildInputs)}"; - }; + # packages we need at build time + nativeBuildInputs = with pkgs; [ + pkg-config + glibc.dev + nixForBindings.dev + + rustPlatform.bindgenHook + ]; + + # packages we link against + buildInputs = with pkgs; [ + stdenv.cc + + nixForBindings + ]; + + # bindgen uses clang to generate bindings, but it doesn't know where to + # find our stdenv cc's headers, so when it's gcc, we need to tell it. + postConfigure = lib.optionalString pkgs.stdenv.cc.isGNU '' + #!/usr/bin/env bash + # REF: https://github.com/nixops4/nix-bindings-rust/blob/main/bindgen-gcc.sh + # Rust bindgen uses Clang to generate bindings, but that means that it can't + # find the "system" or compiler headers when the stdenv compiler is GCC. + # This script tells it where to find them. + + echo "Extending BINDGEN_EXTRA_CLANG_ARGS with system include paths..." 2>&1 + BINDGEN_EXTRA_CLANG_ARGS="$${BINDGEN_EXTRA_CLANG_ARGS:-}" + export BINDGEN_EXTRA_CLANG_ARGS + include_paths=$( + echo | $NIX_CC_UNWRAPPED -v -E -x c - 2>&1 \ + | awk '/#include <...> search starts here:/{flag=1;next} \ + /End of search list./{flag=0} \ + flag==1 {print $1}' + ) + for path in $include_paths; do + echo " - $path" 2>&1 + BINDGEN_EXTRA_CLANG_ARGS="$BINDGEN_EXTRA_CLANG_ARGS -I$path" + done + ''; + shellHook = postConfigure; + + env = let + inherit (llvmPackages) llvm libclang; + in { + LD_LIBRARY_PATH = "$LD_LIBRARY_PATH:${builtins.toString (lib.makeLibraryPath buildInputs)}"; + LIBCLANG_PATH = "${libclang.lib}/lib"; + + RUST_SRC_PATH = "${pkgs.rustPlatform.rustLibSrc}"; + BINDGEN_EXTRA_CLANG_ARGS = "--sysroot=${pkgs.glibc.dev}"; + + # `cargo-llvm-cov` reads these environment variables to find these binaries, + # which are needed to run the tests + LLVM_COV = "${llvm}/bin/llvm-cov"; + LLVM_PROFDATA = "${llvm}/bin/llvm-profdata"; + }; + }; } ); }; From e9022e675b2753cfa6cff6d4973081477a5e85a8 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Fri, 13 Mar 2026 23:20:49 +1000 Subject: [PATCH 277/306] working sys level bindings :yippie: --- .gitignore | 3 - Cargo.lock | 148 ++++ nixide-sys/Cargo.toml | 5 +- nixide-sys/build.rs | 44 +- nixide-sys/include/wrapper.h | 24 +- nixide-sys/lib.rs | 27 +- nixide-sys/tests/eval.rs | 1235 ++++++++++++++++++++++++++++++++++ nixide-sys/tests/flake.rs | 78 +++ nixide-sys/tests/memory.rs | 503 ++++++++++++++ nixide-sys/tests/primop.rs | 639 ++++++++++++++++++ nixide-sys/tests/store.rs | 278 ++++++++ nixide-sys/tests/util.rs | 152 +++++ 12 files changed, 3091 insertions(+), 45 deletions(-) create mode 100644 nixide-sys/tests/eval.rs create mode 100644 nixide-sys/tests/flake.rs create mode 100644 nixide-sys/tests/memory.rs create mode 100644 nixide-sys/tests/primop.rs create mode 100644 nixide-sys/tests/store.rs create mode 100644 nixide-sys/tests/util.rs diff --git a/.gitignore b/.gitignore index b36bbaf..e6857d8 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,3 @@ result-* # Rust /target - -# Other -nixops4-bindings-sys diff --git a/Cargo.lock b/Cargo.lock index cac84e4..dfba9b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -93,6 +93,41 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + [[package]] name = "glob" version = "0.3.3" @@ -124,6 +159,15 @@ dependencies = [ "windows-link", ] +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + [[package]] name = "log" version = "0.4.29" @@ -150,6 +194,7 @@ dependencies = [ "cc", "doxygen-bindgen", "pkg-config", + "serial_test", ] [[package]] @@ -162,6 +207,41 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + [[package]] name = "pkg-config" version = "0.3.32" @@ -186,6 +266,15 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + [[package]] name = "regex" version = "1.12.3" @@ -221,12 +310,71 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +[[package]] +name = "scc" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46e6f046b7fef48e2660c57ed794263155d713de679057f2d0c169bfc6e756cc" +dependencies = [ + "sdd", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sdd" +version = "3.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490dcfcbfef26be6800d11870ff2df8774fa6e86d047e3e8c8a76b25655e41ca" + +[[package]] +name = "serial_test" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "911bd979bf1070a3f3aa7b691a3b3e9968f339ceeec89e08c280a8a22207a32f" +dependencies = [ + "futures-executor", + "futures-util", + "log", + "once_cell", + "parking_lot", + "scc", + "serial_test_derive", +] + +[[package]] +name = "serial_test_derive" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a7d91949b85b0d2fb687445e448b40d322b6b3e4af6b44a29b21d9a5f33e6d9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + [[package]] name = "syn" version = "2.0.117" diff --git a/nixide-sys/Cargo.toml b/nixide-sys/Cargo.toml index cc2c8a2..0be064c 100644 --- a/nixide-sys/Cargo.toml +++ b/nixide-sys/Cargo.toml @@ -4,10 +4,9 @@ description = "Unsafe direct FFI bindings to libnix C API" version = "0.1.0" readme = "../README.md" license = "GPL-3.0" -license-file = "../LICENSE" repository = "https://codeberg.org/luminary/nixide" authors = [ - "_cry64 " + "_cry64 ", "foxxyora " ] @@ -35,3 +34,5 @@ doxygen-bindgen = "0.1.3" pkg-config.workspace = true cc.workspace = true +[dev-dependencies] +serial_test = "3.4.0" diff --git a/nixide-sys/build.rs b/nixide-sys/build.rs index 6d20acf..4419125 100644 --- a/nixide-sys/build.rs +++ b/nixide-sys/build.rs @@ -1,6 +1,5 @@ use std::env; use std::path::PathBuf; -use std::process::Command; use bindgen::callbacks::ParseCallbacks; @@ -24,7 +23,7 @@ fn main() { println!("cargo:rerun-if-changed=include/wrapper.h"); // Tell cargo to tell rustc to link the system shared library - println!("cargo:rustc-link-lib=bz2"); + // println!("cargo:rustc-link-lib=bz2"); // Use pkg-config to find nix-store include and link paths // This NEEDS to be included, or otherwise `nix_api_store.h` cannot @@ -37,32 +36,23 @@ fn main() { "nix-flake-c", ]; - // Add all pkg-config include paths and GCC's include path to bindgen - let mut args = Vec::new(); - for nix_lib in libs { - let lib = pkg_config::probe_library(nix_lib) - .expect(&format!("Unable to find .pc file for {}", nix_lib)); - - for include_path in lib.include_paths { - args.push(format!("-I{}", incloude_path.display())); - // builder = builder.clang_arg(format!("-I{}", include_path.display())); - } - for link_file in lib.link_files { - println!("cargo:rustc-link-lib={}", link_file.display()); - } - } + let lib_args: Vec = libs + .iter() + .map(|&name| { + let lib = pkg_config::probe_library(name) + .expect(&format!("Unable to find .pc file for {}", name)); - let lib_args = libs.map(|name| { - let lib = pkg_config::probe_library(name) - .expect(&format!("Unable to find .pc file for {}", nix_lib)); + for p in lib.link_files { + println!("cargo:rustc-link-lib={}", p.display()); + } + + lib.include_paths + .into_iter() + .map(|p| format!("-I{}", p.display())) + }) + .flatten() + .collect(); - for p in lib.link_files { - println!("cargo:rustc-link-lib={}", p.display()); - } - - lib.include_paths.map(|p| format!("-I{}", p.display())) - }).flatten(); - let bindings = bindgen::Builder::default() .clang_args(lib_args) // The input header we would like to generate bindings for @@ -73,7 +63,7 @@ fn main() { .parse_callbacks(Box::new(DoxygenCallbacks)) // Format generated bindings with rustfmt .formatter(bindgen::Formatter::Rustfmt) - .rustfmt_configuration_file(std::fs::canonicalize(".rustfmt.toml").ok()); + .rustfmt_configuration_file(std::fs::canonicalize(".rustfmt.toml").ok()) // Finish the builder and generate the bindings .generate() // Unwrap the Result and panic on failure diff --git a/nixide-sys/include/wrapper.h b/nixide-sys/include/wrapper.h index 41450d5..30dd94e 100644 --- a/nixide-sys/include/wrapper.h +++ b/nixide-sys/include/wrapper.h @@ -1 +1,23 @@ -#include <> +// Pure C API for store operations +#include + +// Pure C API for error handling +#include + +// Pure C API for the Nix evaluator +#include + +// Pure C API for external values +#include + +// Pure C API for value manipulation +#include + +// Pure C API for fetcher operations +#include + +// Pure C API for flake support +#include + +// Pure C API for main/CLI support +#include diff --git a/nixide-sys/lib.rs b/nixide-sys/lib.rs index b93cf3f..4c744cd 100644 --- a/nixide-sys/lib.rs +++ b/nixide-sys/lib.rs @@ -1,14 +1,17 @@ -pub fn add(left: u64, right: u64) -> u64 { - left + right -} +//! # nixide-sys +//! +//! Unsafe direct FFI bindings to libnix C API. +//! +//! ## Safety +//! +//! These bindings are generated automatically and map directly to the C API. +//! They are unsafe to use directly. Prefer using the high-level safe API in the +//! parent crate unless you know what you're doing. -#[cfg(test)] -mod tests { - use super::*; +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(rustdoc::bare_urls)] +#![allow(rustdoc::invalid_html_tags)] - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } -} +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); diff --git a/nixide-sys/tests/eval.rs b/nixide-sys/tests/eval.rs new file mode 100644 index 0000000..742b4d0 --- /dev/null +++ b/nixide-sys/tests/eval.rs @@ -0,0 +1,1235 @@ +#![cfg(test)] + +use std::{ + ffi::{CStr, CString}, + ptr, +}; + +use nixide_sys::*; +use serial_test::serial; + +#[test] +#[serial] +fn eval_init_and_state_build() { + unsafe { + let ctx = nix_c_context_create(); + assert!(!ctx.is_null()); + let err = nix_libutil_init(ctx); + assert_eq!(err, nix_err_NIX_OK, "nix_libutil_init failed: {err}"); + + let err = nix_libstore_init(ctx); + assert_eq!(err, nix_err_NIX_OK, "nix_libstore_init failed: {err}"); + + let err = nix_libexpr_init(ctx); + assert_eq!(err, nix_err_NIX_OK, "nix_libexpr_init failed: {err}"); + + let store = nix_store_open(ctx, std::ptr::null(), std::ptr::null_mut()); + assert!(!store.is_null()); + + let builder = nix_eval_state_builder_new(ctx, store); + assert!(!builder.is_null()); + + let load_err = nix_eval_state_builder_load(ctx, builder); + assert_eq!(load_err, nix_err_NIX_OK); + + let state = nix_eval_state_build(ctx, builder); + assert!(!state.is_null()); + + nix_state_free(state); + nix_eval_state_builder_free(builder); + nix_store_free(store); + nix_c_context_free(ctx); + } +} + +#[test] +#[serial] +fn eval_simple_expression() { + unsafe { + let ctx = nix_c_context_create(); + assert!(!ctx.is_null()); + + let err = nix_libutil_init(ctx); + assert_eq!(err, nix_err_NIX_OK, "nix_libutil_init failed: {err}"); + + let err = nix_libstore_init(ctx); + assert_eq!(err, nix_err_NIX_OK, "nix_libstore_init failed: {err}"); + + let err = nix_libexpr_init(ctx); + assert_eq!(err, nix_err_NIX_OK, "nix_libexpr_init failed: {err}"); + + let store = nix_store_open(ctx, std::ptr::null(), std::ptr::null_mut()); + assert!(!store.is_null()); + + let builder = nix_eval_state_builder_new(ctx, store); + assert!(!builder.is_null()); + assert_eq!(nix_eval_state_builder_load(ctx, builder), nix_err_NIX_OK); + + let state = nix_eval_state_build(ctx, builder); + assert!(!state.is_null()); + + // Evaluate a simple integer expression + let expr = CString::new("1 + 2").unwrap(); + let path = CString::new("").unwrap(); + let value = nix_alloc_value(ctx, state); + assert!(!value.is_null()); + + let eval_err = nix_expr_eval_from_string(ctx, state, expr.as_ptr(), path.as_ptr(), value); + assert_eq!(eval_err, nix_err_NIX_OK); + + // Force the value (should not be a thunk) + let force_err = nix_value_force(ctx, state, value); + assert_eq!(force_err, nix_err_NIX_OK); + + nix_state_free(state); + nix_eval_state_builder_free(builder); + nix_store_free(store); + nix_c_context_free(ctx); + } +} + +#[test] +#[serial] +fn value_construction_and_inspection() { + unsafe { + let ctx = nix_c_context_create(); + assert!(!ctx.is_null()); + let err = nix_libutil_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + let err = nix_libstore_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + let err = nix_libexpr_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let store = nix_store_open(ctx, ptr::null(), ptr::null_mut()); + assert!(!store.is_null()); + let builder = nix_eval_state_builder_new(ctx, store); + assert!(!builder.is_null()); + assert_eq!(nix_eval_state_builder_load(ctx, builder), nix_err_NIX_OK); + let state = nix_eval_state_build(ctx, builder); + assert!(!state.is_null()); + + // Int + let int_val = nix_alloc_value(ctx, state); + assert!(!int_val.is_null()); + assert_eq!(nix_init_int(ctx, int_val, 42), nix_err_NIX_OK); + assert_eq!(nix_get_type(ctx, int_val), ValueType_NIX_TYPE_INT); + assert_eq!(nix_get_int(ctx, int_val), 42); + + // Float + let float_val = nix_alloc_value(ctx, state); + assert!(!float_val.is_null()); + assert_eq!( + nix_init_float(ctx, float_val, std::f64::consts::PI), + nix_err_NIX_OK + ); + assert_eq!(nix_get_type(ctx, float_val), ValueType_NIX_TYPE_FLOAT); + assert!((nix_get_float(ctx, float_val) - std::f64::consts::PI).abs() < 1e-10); + + // Bool + let bool_val = nix_alloc_value(ctx, state); + assert!(!bool_val.is_null()); + assert_eq!(nix_init_bool(ctx, bool_val, true), nix_err_NIX_OK); + assert_eq!(nix_get_type(ctx, bool_val), ValueType_NIX_TYPE_BOOL); + assert!(nix_get_bool(ctx, bool_val)); + + // Null + let null_val = nix_alloc_value(ctx, state); + assert!(!null_val.is_null()); + assert_eq!(nix_init_null(ctx, null_val), nix_err_NIX_OK); + assert_eq!(nix_get_type(ctx, null_val), ValueType_NIX_TYPE_NULL); + + // String + let string_val = nix_alloc_value(ctx, state); + assert!(!string_val.is_null()); + let s = CString::new("hello world").unwrap(); + assert_eq!(nix_init_string(ctx, string_val, s.as_ptr()), nix_err_NIX_OK); + assert_eq!(nix_get_type(ctx, string_val), ValueType_NIX_TYPE_STRING); + extern "C" fn string_cb( + start: *const ::std::os::raw::c_char, + n: ::std::os::raw::c_uint, + user_data: *mut ::std::os::raw::c_void, + ) { + let s = unsafe { std::slice::from_raw_parts(start.cast::(), n as usize) }; + let s = std::str::from_utf8(s).unwrap(); + let out = user_data.cast::>(); + unsafe { *out = Some(s.to_string()) }; + } + let mut got: Option = None; + assert_eq!( + nix_get_string(ctx, string_val, Some(string_cb), (&raw mut got).cast()), + nix_err_NIX_OK + ); + assert_eq!(got.as_deref(), Some("hello world")); + + // Path string + let path_val = nix_alloc_value(ctx, state); + assert!(!path_val.is_null()); + let p = CString::new("/nix/store/foo").unwrap(); + assert_eq!( + nix_init_path_string(ctx, state, path_val, p.as_ptr()), + nix_err_NIX_OK + ); + assert_eq!(nix_get_type(ctx, path_val), ValueType_NIX_TYPE_PATH); + let path_ptr = nix_get_path_string(ctx, path_val); + assert!(!path_ptr.is_null()); + let path_str = CStr::from_ptr(path_ptr).to_string_lossy(); + assert_eq!(path_str, "/nix/store/foo"); + + // Clean up + nix_state_free(state); + nix_eval_state_builder_free(builder); + nix_store_free(store); + nix_c_context_free(ctx); + } +} + +#[test] +#[serial] +fn list_and_attrset_manipulation() { + unsafe { + let ctx = nix_c_context_create(); + assert!(!ctx.is_null()); + let err = nix_libutil_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + let err = nix_libstore_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + let err = nix_libexpr_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let store = nix_store_open(ctx, ptr::null(), ptr::null_mut()); + assert!(!store.is_null()); + let builder = nix_eval_state_builder_new(ctx, store); + assert!(!builder.is_null()); + assert_eq!(nix_eval_state_builder_load(ctx, builder), nix_err_NIX_OK); + let state = nix_eval_state_build(ctx, builder); + assert!(!state.is_null()); + + // List: [1, 2, 3] + let list_builder = nix_make_list_builder(ctx, state, 3); + assert!(!list_builder.is_null()); + let v1 = nix_alloc_value(ctx, state); + let v2 = nix_alloc_value(ctx, state); + let v3 = nix_alloc_value(ctx, state); + nix_init_int(ctx, v1, 1); + nix_init_int(ctx, v2, 2); + nix_init_int(ctx, v3, 3); + nix_list_builder_insert(ctx, list_builder, 0, v1); + nix_list_builder_insert(ctx, list_builder, 1, v2); + nix_list_builder_insert(ctx, list_builder, 2, v3); + + let list_val = nix_alloc_value(ctx, state); + assert_eq!(nix_make_list(ctx, list_builder, list_val), nix_err_NIX_OK); + assert_eq!(nix_get_type(ctx, list_val), ValueType_NIX_TYPE_LIST); + assert_eq!(nix_get_list_size(ctx, list_val), 3); + + // Get elements by index + for i in 0..3 { + let elem = nix_get_list_byidx(ctx, list_val, state, i); + assert!(!elem.is_null()); + assert_eq!(nix_get_type(ctx, elem), ValueType_NIX_TYPE_INT); + assert_eq!(nix_get_int(ctx, elem), i64::from(i + 1)); + } + + nix_list_builder_free(list_builder); + + // Attrset: { foo = 42; bar = "baz"; } + let attr_builder = nix_make_bindings_builder(ctx, state, 2); + assert!(!attr_builder.is_null()); + let foo_val = nix_alloc_value(ctx, state); + let bar_val = nix_alloc_value(ctx, state); + nix_init_int(ctx, foo_val, 42); + let baz = CString::new("baz").unwrap(); + nix_init_string(ctx, bar_val, baz.as_ptr()); + let foo = CString::new("foo").unwrap(); + let bar = CString::new("bar").unwrap(); + nix_bindings_builder_insert(ctx, attr_builder, foo.as_ptr(), foo_val); + nix_bindings_builder_insert(ctx, attr_builder, bar.as_ptr(), bar_val); + + let attr_val = nix_alloc_value(ctx, state); + assert_eq!(nix_make_attrs(ctx, attr_val, attr_builder), nix_err_NIX_OK); + assert_eq!(nix_get_type(ctx, attr_val), ValueType_NIX_TYPE_ATTRS); + assert_eq!(nix_get_attrs_size(ctx, attr_val), 2); + + // Get by name + let foo_got = nix_get_attr_byname(ctx, attr_val, state, foo.as_ptr()); + assert!(!foo_got.is_null()); + assert_eq!(nix_get_type(ctx, foo_got), ValueType_NIX_TYPE_INT); + assert_eq!(nix_get_int(ctx, foo_got), 42); + + let bar_got = nix_get_attr_byname(ctx, attr_val, state, bar.as_ptr()); + assert!(!bar_got.is_null()); + assert_eq!(nix_get_type(ctx, bar_got), ValueType_NIX_TYPE_STRING); + + // Has attr + assert!(nix_has_attr_byname(ctx, attr_val, state, foo.as_ptr())); + assert!(nix_has_attr_byname(ctx, attr_val, state, bar.as_ptr())); + + nix_bindings_builder_free(attr_builder); + + nix_state_free(state); + nix_eval_state_builder_free(builder); + nix_store_free(store); + nix_c_context_free(ctx); + } +} + +#[test] +#[serial] +fn function_application_and_force() { + unsafe { + let ctx = nix_c_context_create(); + assert!(!ctx.is_null()); + let err = nix_libutil_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + let err = nix_libstore_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + let err = nix_libexpr_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let store = nix_store_open(ctx, ptr::null(), ptr::null_mut()); + assert!(!store.is_null()); + let builder = nix_eval_state_builder_new(ctx, store); + assert!(!builder.is_null()); + assert_eq!(nix_eval_state_builder_load(ctx, builder), nix_err_NIX_OK); + let state = nix_eval_state_build(ctx, builder); + assert!(!state.is_null()); + + // Evaluate a function and apply it: (x: x + 1) 41 + let expr = CString::new("(x: x + 1)").unwrap(); + let path = CString::new("").unwrap(); + let fn_val = nix_alloc_value(ctx, state); + assert!(!fn_val.is_null()); + assert_eq!( + nix_expr_eval_from_string(ctx, state, expr.as_ptr(), path.as_ptr(), fn_val), + nix_err_NIX_OK + ); + + // Argument: 41 + let arg_val = nix_alloc_value(ctx, state); + nix_init_int(ctx, arg_val, 41); + + // Result value + let result_val = nix_alloc_value(ctx, state); + assert!(!result_val.is_null()); + assert_eq!( + nix_value_call(ctx, state, fn_val, arg_val, result_val), + nix_err_NIX_OK + ); + + // Force result + assert_eq!(nix_value_force(ctx, state, result_val), nix_err_NIX_OK); + assert_eq!(nix_get_type(ctx, result_val), ValueType_NIX_TYPE_INT); + assert_eq!(nix_get_int(ctx, result_val), 42); + + // Deep force (should be a no-op for int) + assert_eq!(nix_value_force_deep(ctx, state, result_val), nix_err_NIX_OK); + + nix_state_free(state); + nix_eval_state_builder_free(builder); + nix_store_free(store); + nix_c_context_free(ctx); + } +} + +#[test] +#[serial] +fn error_handling_invalid_expression() { + unsafe { + let ctx = nix_c_context_create(); + assert!(!ctx.is_null()); + let err = nix_libutil_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + let err = nix_libstore_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + let err = nix_libexpr_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let store = nix_store_open(ctx, ptr::null(), ptr::null_mut()); + assert!(!store.is_null()); + let builder = nix_eval_state_builder_new(ctx, store); + assert!(!builder.is_null()); + assert_eq!(nix_eval_state_builder_load(ctx, builder), nix_err_NIX_OK); + let state = nix_eval_state_build(ctx, builder); + assert!(!state.is_null()); + + // Invalid expression + let expr = CString::new("this is not valid nix").unwrap(); + let path = CString::new("").unwrap(); + let value = nix_alloc_value(ctx, state); + assert!(!value.is_null()); + let eval_err = nix_expr_eval_from_string(ctx, state, expr.as_ptr(), path.as_ptr(), value); + assert_ne!(eval_err, nix_err_NIX_OK); + + nix_state_free(state); + nix_eval_state_builder_free(builder); + nix_store_free(store); + nix_c_context_free(ctx); + } +} + +#[test] +#[serial] +fn realised_string_and_gc() { + unsafe { + let ctx = nix_c_context_create(); + assert!(!ctx.is_null()); + let err = nix_libutil_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + let err = nix_libstore_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + let err = nix_libexpr_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let store = nix_store_open(ctx, ptr::null(), ptr::null_mut()); + assert!(!store.is_null()); + let builder = nix_eval_state_builder_new(ctx, store); + assert!(!builder.is_null()); + assert_eq!(nix_eval_state_builder_load(ctx, builder), nix_err_NIX_OK); + let state = nix_eval_state_build(ctx, builder); + assert!(!state.is_null()); + + // String value + let string_val = nix_alloc_value(ctx, state); + let s = CString::new("hello world").unwrap(); + assert_eq!(nix_init_string(ctx, string_val, s.as_ptr()), nix_err_NIX_OK); + + // Realise string + let realised = nix_string_realise(ctx, state, string_val, false); + assert!(!realised.is_null()); + let buf = nix_realised_string_get_buffer_start(realised); + let len = nix_realised_string_get_buffer_size(realised); + let realised_str = + std::str::from_utf8(std::slice::from_raw_parts(buf.cast::(), len)).unwrap(); + assert_eq!(realised_str, "hello world"); + assert_eq!(nix_realised_string_get_store_path_count(realised), 0); + + nix_realised_string_free(realised); + + nix_state_free(state); + nix_eval_state_builder_free(builder); + nix_store_free(store); + nix_c_context_free(ctx); + } +} + +#[test] +#[serial] +fn big_thunk_evaluation() { + unsafe { + let ctx = nix_c_context_create(); + assert!(!ctx.is_null()); + + let err = nix_libutil_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + let err = nix_libstore_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + let err = nix_libexpr_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let store = nix_store_open(ctx, ptr::null(), ptr::null_mut()); + assert!(!store.is_null()); + + let builder = nix_eval_state_builder_new(ctx, store); + assert!(!builder.is_null()); + assert_eq!(nix_eval_state_builder_load(ctx, builder), nix_err_NIX_OK); + + let state = nix_eval_state_build(ctx, builder); + assert!(!state.is_null()); + + // Create a complex expression with lazy evaluation + let expr = + CString::new("let x = 1 + 2; y = x * 3; in { result = y + 4; other = x; }").unwrap(); + let path = CString::new("").unwrap(); + let value = nix_alloc_value(ctx, state); + assert!(!value.is_null()); + + let eval_err = nix_expr_eval_from_string(ctx, state, expr.as_ptr(), path.as_ptr(), value); + assert_eq!(eval_err, nix_err_NIX_OK); + + // The top-level should be an attrset + assert_eq!(nix_get_type(ctx, value), ValueType_NIX_TYPE_ATTRS); + + // Get "result" attribute (ts should be a thunk initially) + let result_name = CString::new("result").unwrap(); + let result_val = nix_get_attr_byname(ctx, value, state, result_name.as_ptr()); + assert!(!result_val.is_null()); + + // Force the result + let force_err = nix_value_force(ctx, state, result_val); + assert_eq!(force_err, nix_err_NIX_OK); + + assert_eq!(nix_get_type(ctx, result_val), ValueType_NIX_TYPE_INT); + assert_eq!(nix_get_int(ctx, result_val), 13); // ((1+2)*3)+4 = 13 + + // Get "other" attribute + let other_name = CString::new("other").unwrap(); + let other_val = nix_get_attr_byname(ctx, value, state, other_name.as_ptr()); + assert!(!other_val.is_null()); + + let force_err2 = nix_value_force(ctx, state, other_val); + assert_eq!(force_err2, nix_err_NIX_OK); + + assert_eq!(nix_get_type(ctx, other_val), ValueType_NIX_TYPE_INT); + assert_eq!(nix_get_int(ctx, other_val), 3); // 1+2 = 3 + + nix_state_free(state); + nix_eval_state_builder_free(builder); + nix_store_free(store); + nix_c_context_free(ctx); + } +} + +#[test] +#[serial] +fn multi_argument_function_calls() { + unsafe { + let ctx = nix_c_context_create(); + assert!(!ctx.is_null()); + + let err = nix_libutil_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let err = nix_libstore_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let err = nix_libexpr_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let store = nix_store_open(ctx, std::ptr::null(), std::ptr::null_mut()); + assert!(!store.is_null()); + + let builder = nix_eval_state_builder_new(ctx, store); + assert!(!builder.is_null()); + + let load_err = nix_eval_state_builder_load(ctx, builder); + assert_eq!(load_err, nix_err_NIX_OK); + + let state = nix_eval_state_build(ctx, builder); + assert!(!state.is_null()); + + // Test evaluating a multi-argument function: (x: y: x + y) + let expr = CString::new("(x: y: x + y)").unwrap(); + let path = CString::new("/test").unwrap(); + + let func_value = nix_alloc_value(ctx, state); + assert!(!func_value.is_null()); + + let eval_err = + nix_expr_eval_from_string(ctx, state, expr.as_ptr(), path.as_ptr(), func_value); + assert_eq!(eval_err, nix_err_NIX_OK); + + // Force evaluation of the function + let force_err = nix_value_force(ctx, state, func_value); + assert_eq!(force_err, nix_err_NIX_OK); + + // Verify it's a function + let func_type = nix_get_type(ctx, func_value); + assert_eq!(func_type, ValueType_NIX_TYPE_FUNCTION); + + // Create arguments + let arg1 = nix_alloc_value(ctx, state); + let arg2 = nix_alloc_value(ctx, state); + assert!(!arg1.is_null() && !arg2.is_null()); + + let init_arg1_err = nix_init_int(ctx, arg1, 10); + let init_arg2_err = nix_init_int(ctx, arg2, 20); + assert_eq!(init_arg1_err, nix_err_NIX_OK); + assert_eq!(init_arg2_err, nix_err_NIX_OK); + + // Test multi-argument call using nix_value_call_multi + let mut args = [arg1, arg2]; + let result = nix_alloc_value(ctx, state); + assert!(!result.is_null()); + + let call_err = nix_value_call_multi(ctx, state, func_value, 2, args.as_mut_ptr(), result); + assert_eq!(call_err, nix_err_NIX_OK); + + // Force the result + let force_result_err = nix_value_force(ctx, state, result); + assert_eq!(force_result_err, nix_err_NIX_OK); + + // Check result type and value + let result_type = nix_get_type(ctx, result); + assert_eq!(result_type, ValueType_NIX_TYPE_INT); + + let result_value = nix_get_int(ctx, result); + assert_eq!(result_value, 30); // 10 + 20 + + // Clean up + nix_value_decref(ctx, result); + nix_value_decref(ctx, arg2); + nix_value_decref(ctx, arg1); + nix_value_decref(ctx, func_value); + nix_state_free(state); + nix_eval_state_builder_free(builder); + nix_store_free(store); + nix_c_context_free(ctx); + } +} + +#[test] +#[serial] +fn curried_function_evaluation() { + unsafe { + let ctx = nix_c_context_create(); + assert!(!ctx.is_null()); + + let err = nix_libutil_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let err = nix_libstore_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let err = nix_libexpr_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let store = nix_store_open(ctx, std::ptr::null(), std::ptr::null_mut()); + assert!(!store.is_null()); + + let builder = nix_eval_state_builder_new(ctx, store); + assert!(!builder.is_null()); + + let load_err = nix_eval_state_builder_load(ctx, builder); + assert_eq!(load_err, nix_err_NIX_OK); + + let state = nix_eval_state_build(ctx, builder); + assert!(!state.is_null()); + + // Test evaluating a curried function: (x: y: z: x + y + z) + let expr = CString::new("(x: y: z: x + y + z)").unwrap(); + let path = CString::new("/test").unwrap(); + + let func_value = nix_alloc_value(ctx, state); + assert!(!func_value.is_null()); + + let eval_err = + nix_expr_eval_from_string(ctx, state, expr.as_ptr(), path.as_ptr(), func_value); + assert_eq!(eval_err, nix_err_NIX_OK); + + // Create three arguments + let arg1 = nix_alloc_value(ctx, state); + let arg2 = nix_alloc_value(ctx, state); + let arg3 = nix_alloc_value(ctx, state); + assert!(!arg1.is_null() && !arg2.is_null() && !arg3.is_null()); + + let _ = nix_init_int(ctx, arg1, 5); + let _ = nix_init_int(ctx, arg2, 10); + let _ = nix_init_int(ctx, arg3, 15); + + // Test calling with multiple arguments at once + let mut args = [arg1, arg2, arg3]; + let result = nix_alloc_value(ctx, state); + assert!(!result.is_null()); + + let call_err = nix_value_call_multi(ctx, state, func_value, 3, args.as_mut_ptr(), result); + assert_eq!(call_err, nix_err_NIX_OK); + + // Force the result + let force_result_err = nix_value_force(ctx, state, result); + assert_eq!(force_result_err, nix_err_NIX_OK); + + // Check result + let result_type = nix_get_type(ctx, result); + assert_eq!(result_type, ValueType_NIX_TYPE_INT); + + let result_value = nix_get_int(ctx, result); + assert_eq!(result_value, 30); // 5 + 10 + 15 + + // Test partial application using single calls + let partial1 = nix_alloc_value(ctx, state); + assert!(!partial1.is_null()); + + let partial_call1_err = nix_value_call(ctx, state, func_value, arg1, partial1); + assert_eq!(partial_call1_err, nix_err_NIX_OK); + + // partial1 should still be a function + let force_partial1_err = nix_value_force(ctx, state, partial1); + assert_eq!(force_partial1_err, nix_err_NIX_OK); + + let partial1_type = nix_get_type(ctx, partial1); + assert_eq!(partial1_type, ValueType_NIX_TYPE_FUNCTION); + + // Apply second argument + let partial2 = nix_alloc_value(ctx, state); + assert!(!partial2.is_null()); + + let partial_call2_err = nix_value_call(ctx, state, partial1, arg2, partial2); + assert_eq!(partial_call2_err, nix_err_NIX_OK); + + // partial2 should still be a function + let force_partial2_err = nix_value_force(ctx, state, partial2); + assert_eq!(force_partial2_err, nix_err_NIX_OK); + + let partial2_type = nix_get_type(ctx, partial2); + assert_eq!(partial2_type, ValueType_NIX_TYPE_FUNCTION); + + // Apply final argument + let final_result = nix_alloc_value(ctx, state); + assert!(!final_result.is_null()); + + let final_call_err = nix_value_call(ctx, state, partial2, arg3, final_result); + assert_eq!(final_call_err, nix_err_NIX_OK); + + // Force and check final result + let force_final_err = nix_value_force(ctx, state, final_result); + assert_eq!(force_final_err, nix_err_NIX_OK); + + let final_type = nix_get_type(ctx, final_result); + assert_eq!(final_type, ValueType_NIX_TYPE_INT); + + let final_value = nix_get_int(ctx, final_result); + assert_eq!(final_value, 30); // same result as multi-arg call + + // Clean up + nix_value_decref(ctx, final_result); + nix_value_decref(ctx, partial2); + nix_value_decref(ctx, partial1); + nix_value_decref(ctx, result); + nix_value_decref(ctx, arg3); + nix_value_decref(ctx, arg2); + nix_value_decref(ctx, arg1); + nix_value_decref(ctx, func_value); + nix_state_free(state); + nix_eval_state_builder_free(builder); + nix_store_free(store); + nix_c_context_free(ctx); + } +} + +#[test] +#[serial] +fn thunk_creation_with_init_apply() { + unsafe { + let ctx = nix_c_context_create(); + assert!(!ctx.is_null()); + + let err = nix_libutil_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let err = nix_libstore_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let err = nix_libexpr_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let store = nix_store_open(ctx, std::ptr::null(), std::ptr::null_mut()); + assert!(!store.is_null()); + + let builder = nix_eval_state_builder_new(ctx, store); + assert!(!builder.is_null()); + + let load_err = nix_eval_state_builder_load(ctx, builder); + assert_eq!(load_err, nix_err_NIX_OK); + + let state = nix_eval_state_build(ctx, builder); + assert!(!state.is_null()); + + // Create a simple function + let func_expr = CString::new("(x: x * 2)").unwrap(); + let path = CString::new("/test").unwrap(); + + let func_value = nix_alloc_value(ctx, state); + assert!(!func_value.is_null()); + + let eval_err = + nix_expr_eval_from_string(ctx, state, func_expr.as_ptr(), path.as_ptr(), func_value); + assert_eq!(eval_err, nix_err_NIX_OK); + + // Create an argument + let arg = nix_alloc_value(ctx, state); + assert!(!arg.is_null()); + + let init_arg_err = nix_init_int(ctx, arg, 21); + assert_eq!(init_arg_err, nix_err_NIX_OK); + + // Create a thunk using nix_init_apply (lazy evaluation) + let thunk = nix_alloc_value(ctx, state); + assert!(!thunk.is_null()); + + let apply_err = nix_init_apply(ctx, thunk, func_value, arg); + assert_eq!(apply_err, nix_err_NIX_OK); + + // Initially, the thunk should be of type THUNK + let thunk_type = nix_get_type(ctx, thunk); + assert_eq!(thunk_type, ValueType_NIX_TYPE_THUNK); + + // Force evaluation of the thunk + let force_err = nix_value_force(ctx, state, thunk); + assert_eq!(force_err, nix_err_NIX_OK); + + // After forcing, it should be an integer + let forced_type = nix_get_type(ctx, thunk); + assert_eq!(forced_type, ValueType_NIX_TYPE_INT); + + let result_value = nix_get_int(ctx, thunk); + assert_eq!(result_value, 42); // 21 * 2 + + // Clean up + nix_value_decref(ctx, thunk); + nix_value_decref(ctx, arg); + nix_value_decref(ctx, func_value); + nix_state_free(state); + nix_eval_state_builder_free(builder); + nix_store_free(store); + nix_c_context_free(ctx); + } +} + +#[test] +#[serial] +fn lookup_path_configuration() { + unsafe { + let ctx = nix_c_context_create(); + assert!(!ctx.is_null()); + + let err = nix_libutil_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let err = nix_libstore_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let err = nix_libexpr_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let store = nix_store_open(ctx, std::ptr::null(), std::ptr::null_mut()); + assert!(!store.is_null()); + + let builder = nix_eval_state_builder_new(ctx, store); + assert!(!builder.is_null()); + + // Configure custom lookup path (NIX_PATH equivalent) + let lookup_paths = [ + CString::new("nixpkgs=/fake/nixpkgs").unwrap(), + CString::new("custom=/fake/custom").unwrap(), + ]; + + let lookup_path_ptrs: Vec<*const _> = lookup_paths.iter().map(|s| s.as_ptr()).collect(); + let mut lookup_path_ptrs_null_terminated = lookup_path_ptrs; + lookup_path_ptrs_null_terminated.push(std::ptr::null()); + + let set_lookup_err = nix_eval_state_builder_set_lookup_path( + ctx, + builder, + lookup_path_ptrs_null_terminated.as_mut_ptr(), + ); + assert_eq!(set_lookup_err, nix_err_NIX_OK); + + let load_err = nix_eval_state_builder_load(ctx, builder); + assert_eq!(load_err, nix_err_NIX_OK); + + let state = nix_eval_state_build(ctx, builder); + assert!(!state.is_null()); + + // Try to evaluate an expression that uses the lookup path + // NOTE: This will likely fail since the paths don't exist, but it tests the + // API + let expr = CString::new("builtins.nixPath").unwrap(); + let path = CString::new("/test").unwrap(); + + let result = nix_alloc_value(ctx, state); + assert!(!result.is_null()); + + let eval_err = nix_expr_eval_from_string(ctx, state, expr.as_ptr(), path.as_ptr(), result); + + // The evaluation might succeed or fail depending on Nix version and + // configuration The important thing is that setting the lookup path + // didn't crash + if eval_err == nix_err_NIX_OK { + let force_err = nix_value_force(ctx, state, result); + if force_err == nix_err_NIX_OK { + let result_type = nix_get_type(ctx, result); + // nixPath should be a list + assert_eq!(result_type, ValueType_NIX_TYPE_LIST); + } + } + + // Clean up + nix_value_decref(ctx, result); + nix_state_free(state); + nix_eval_state_builder_free(builder); + nix_store_free(store); + nix_c_context_free(ctx); + } +} + +#[test] +#[serial] +fn complex_nested_evaluation() { + unsafe { + let ctx = nix_c_context_create(); + assert!(!ctx.is_null()); + + let err = nix_libutil_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let err = nix_libstore_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let err = nix_libexpr_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let store = nix_store_open(ctx, std::ptr::null(), std::ptr::null_mut()); + assert!(!store.is_null()); + + let builder = nix_eval_state_builder_new(ctx, store); + assert!(!builder.is_null()); + + let load_err = nix_eval_state_builder_load(ctx, builder); + assert_eq!(load_err, nix_err_NIX_OK); + + let state = nix_eval_state_build(ctx, builder); + assert!(!state.is_null()); + + // Evaluate a simple nested expression + let expr = CString::new( + r#" + let + add = x: y: x + y; + data = { + values = [1 2 3 4 5]; + }; + in + { + original = data.values; + sum = builtins.foldl' add 0 data.values; + } + "#, + ) + .unwrap(); + let path = CString::new("/test").unwrap(); + + let result = nix_alloc_value(ctx, state); + assert!(!result.is_null()); + + let eval_err = nix_expr_eval_from_string(ctx, state, expr.as_ptr(), path.as_ptr(), result); + + // Complex expressions may fail sometimes, check for both success + // and error + if eval_err != nix_err_NIX_OK { + // If evaluation fails, skip the rest of the test + nix_value_decref(ctx, result); + nix_state_free(state); + nix_eval_state_builder_free(builder); + nix_store_free(store); + nix_c_context_free(ctx); + return; + } + + // Force deep evaluation + let force_err = nix_value_force_deep(ctx, state, result); + if force_err != nix_err_NIX_OK { + // If forcing fails, skip the rest of the test + nix_value_decref(ctx, result); + nix_state_free(state); + nix_eval_state_builder_free(builder); + nix_store_free(store); + nix_c_context_free(ctx); + return; + } + + // Verify result structure + let result_type = nix_get_type(ctx, result); + assert_eq!(result_type, ValueType_NIX_TYPE_ATTRS); + + let attrs_size = nix_get_attrs_size(ctx, result); + assert_eq!(attrs_size, 2); // original, sum + + // Check 'sum' attribute + let sum_key = CString::new("sum").unwrap(); + let sum_value = nix_get_attr_byname(ctx, result, state, sum_key.as_ptr()); + assert!(!sum_value.is_null()); + + let sum_type = nix_get_type(ctx, sum_value); + assert_eq!(sum_type, ValueType_NIX_TYPE_INT); + + let sum_result = nix_get_int(ctx, sum_value); + assert_eq!(sum_result, 15); // 1 + 2 + 3 + 4 + 5 + + // Check 'original' attribute (should be a list) + let original_key = CString::new("original").unwrap(); + let original_value = nix_get_attr_byname(ctx, result, state, original_key.as_ptr()); + if !original_value.is_null() { + let original_type = nix_get_type(ctx, original_value); + assert_eq!(original_type, ValueType_NIX_TYPE_LIST); + + let original_size = nix_get_list_size(ctx, original_value); + assert_eq!(original_size, 5); + + // Check first element of original list + let first_elem = nix_get_list_byidx(ctx, original_value, state, 0); + if !first_elem.is_null() { + let first_elem_type = nix_get_type(ctx, first_elem); + assert_eq!(first_elem_type, ValueType_NIX_TYPE_INT); + + let first_elem_value = nix_get_int(ctx, first_elem); + assert_eq!(first_elem_value, 1); + } + } + + // Clean up + nix_value_decref(ctx, result); + nix_state_free(state); + nix_eval_state_builder_free(builder); + nix_store_free(store); + nix_c_context_free(ctx); + } +} + +#[test] +#[serial] +fn evaluation_error_handling() { + unsafe { + let ctx = nix_c_context_create(); + assert!(!ctx.is_null()); + + let err = nix_libutil_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let err = nix_libstore_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let err = nix_libexpr_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let store = nix_store_open(ctx, std::ptr::null(), std::ptr::null_mut()); + assert!(!store.is_null()); + + let builder = nix_eval_state_builder_new(ctx, store); + assert!(!builder.is_null()); + + let load_err = nix_eval_state_builder_load(ctx, builder); + assert_eq!(load_err, nix_err_NIX_OK); + + let state = nix_eval_state_build(ctx, builder); + assert!(!state.is_null()); + + // Test evaluation with syntax error + let invalid_expr = CString::new("{ invalid syntax ").unwrap(); + let path = CString::new("/test").unwrap(); + + let result = nix_alloc_value(ctx, state); + assert!(!result.is_null()); + + let eval_err = + nix_expr_eval_from_string(ctx, state, invalid_expr.as_ptr(), path.as_ptr(), result); + assert_ne!(eval_err, nix_err_NIX_OK); // should fail + + // Clear error for next test + nix_clear_err(ctx); + + // Test evaluation with runtime error + let runtime_error_expr = CString::new("1 + \"string\"").unwrap(); + + let result2 = nix_alloc_value(ctx, state); + assert!(!result2.is_null()); + + let eval_err2 = nix_expr_eval_from_string( + ctx, + state, + runtime_error_expr.as_ptr(), + path.as_ptr(), + result2, + ); + + // May succeed at parse time but fail during evaluation + if eval_err2 == nix_err_NIX_OK { + let force_err = nix_value_force(ctx, state, result2); + assert_ne!(force_err, nix_err_NIX_OK); // should fail during forcing + } + + // Test error information retrieval + let error_code = nix_err_code(ctx); + assert_ne!(error_code, nix_err_NIX_OK); + + // Try to get error message + let mut error_len: std::os::raw::c_uint = 0; + let error_msg_ptr = nix_err_msg(ctx, ctx, &mut error_len as *mut _); + if !error_msg_ptr.is_null() && error_len > 0 { + let error_msg = std::str::from_utf8(std::slice::from_raw_parts( + error_msg_ptr as *const u8, + error_len as usize, + )) + .unwrap_or(""); + // Should contain some error information + assert!(!error_msg.is_empty()); + } + + // Test multi-argument call with wrong number of arguments + nix_clear_err(ctx); + + let func_expr = CString::new("(x: y: x + y)").unwrap(); + let func_value = nix_alloc_value(ctx, state); + assert!(!func_value.is_null()); + + let eval_func_err = + nix_expr_eval_from_string(ctx, state, func_expr.as_ptr(), path.as_ptr(), func_value); + assert_eq!(eval_func_err, nix_err_NIX_OK); + + // Try to call with wrong number of arguments. + // The function expects 2, but we give 1 + let arg = nix_alloc_value(ctx, state); + assert!(!arg.is_null()); + let _ = nix_init_int(ctx, arg, 5); + + let mut args = [arg]; + let result3 = nix_alloc_value(ctx, state); + assert!(!result3.is_null()); + + let call_err = nix_value_call_multi( + ctx, + state, + func_value, + 1, // only 1 argument, but function expects 2 + args.as_mut_ptr(), + result3, + ); + + // This should succeed but result should be a partially applied function + if call_err == nix_err_NIX_OK { + let force_err = nix_value_force(ctx, state, result3); + assert_eq!(force_err, nix_err_NIX_OK); + + let result_type = nix_get_type(ctx, result3); + assert_eq!(result_type, ValueType_NIX_TYPE_FUNCTION); // partially applied + } + + // Clean up + nix_value_decref(ctx, result3); + nix_value_decref(ctx, arg); + nix_value_decref(ctx, func_value); + nix_value_decref(ctx, result2); + nix_value_decref(ctx, result); + nix_state_free(state); + nix_eval_state_builder_free(builder); + nix_store_free(store); + nix_c_context_free(ctx); + } +} + +#[test] +#[serial] +fn builtin_function_calls() { + unsafe { + let ctx = nix_c_context_create(); + assert!(!ctx.is_null()); + + let err = nix_libutil_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let err = nix_libstore_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let err = nix_libexpr_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let store = nix_store_open(ctx, std::ptr::null(), std::ptr::null_mut()); + assert!(!store.is_null()); + + let builder = nix_eval_state_builder_new(ctx, store); + assert!(!builder.is_null()); + + let load_err = nix_eval_state_builder_load(ctx, builder); + assert_eq!(load_err, nix_err_NIX_OK); + + let state = nix_eval_state_build(ctx, builder); + assert!(!state.is_null()); + + // Test calling builtins.length + let length_expr = CString::new("builtins.length").unwrap(); + let path = CString::new("/test").unwrap(); + + let length_func = nix_alloc_value(ctx, state); + assert!(!length_func.is_null()); + + let eval_length_err = + nix_expr_eval_from_string(ctx, state, length_expr.as_ptr(), path.as_ptr(), length_func); + assert_eq!(eval_length_err, nix_err_NIX_OK); + + // Create a list to test with + let list_expr = CString::new("[1 2 3 4 5]").unwrap(); + let test_list = nix_alloc_value(ctx, state); + assert!(!test_list.is_null()); + + let eval_list_err = + nix_expr_eval_from_string(ctx, state, list_expr.as_ptr(), path.as_ptr(), test_list); + assert_eq!(eval_list_err, nix_err_NIX_OK); + + // Call length function with the list + let length_result = nix_alloc_value(ctx, state); + assert!(!length_result.is_null()); + + let call_length_err = nix_value_call(ctx, state, length_func, test_list, length_result); + assert_eq!(call_length_err, nix_err_NIX_OK); + + let force_length_err = nix_value_force(ctx, state, length_result); + assert_eq!(force_length_err, nix_err_NIX_OK); + + let length_type = nix_get_type(ctx, length_result); + assert_eq!(length_type, ValueType_NIX_TYPE_INT); + + let length_value = nix_get_int(ctx, length_result); + assert_eq!(length_value, 5); + + // Test builtins.map with multi-argument call + let map_expr = CString::new("builtins.map").unwrap(); + let map_func = nix_alloc_value(ctx, state); + assert!(!map_func.is_null()); + + let eval_map_err = + nix_expr_eval_from_string(ctx, state, map_expr.as_ptr(), path.as_ptr(), map_func); + assert_eq!(eval_map_err, nix_err_NIX_OK); + + // Create a simple function to map: (x: x * 2) + let double_expr = CString::new("(x: x * 2)").unwrap(); + let double_func = nix_alloc_value(ctx, state); + assert!(!double_func.is_null()); + + let eval_double_err = + nix_expr_eval_from_string(ctx, state, double_expr.as_ptr(), path.as_ptr(), double_func); + assert_eq!(eval_double_err, nix_err_NIX_OK); + + // Call map with the function and list + let mut args = [double_func, test_list]; + let map_result = nix_alloc_value(ctx, state); + assert!(!map_result.is_null()); + + let call_map_err = + nix_value_call_multi(ctx, state, map_func, 2, args.as_mut_ptr(), map_result); + assert_eq!(call_map_err, nix_err_NIX_OK); + + let force_map_err = nix_value_force(ctx, state, map_result); + assert_eq!(force_map_err, nix_err_NIX_OK); + + let map_result_type = nix_get_type(ctx, map_result); + assert_eq!(map_result_type, ValueType_NIX_TYPE_LIST); + + let map_result_size = nix_get_list_size(ctx, map_result); + assert_eq!(map_result_size, 5); + + // Check first element of mapped list (should be 2) + let first_mapped = nix_get_list_byidx(ctx, map_result, state, 0); + assert!(!first_mapped.is_null()); + + let force_first_err = nix_value_force(ctx, state, first_mapped); + assert_eq!(force_first_err, nix_err_NIX_OK); + + let first_mapped_type = nix_get_type(ctx, first_mapped); + assert_eq!(first_mapped_type, ValueType_NIX_TYPE_INT); + + let first_mapped_value = nix_get_int(ctx, first_mapped); + assert_eq!(first_mapped_value, 2); // 1 * 2 + + // Clean up + nix_value_decref(ctx, map_result); + nix_value_decref(ctx, double_func); + nix_value_decref(ctx, map_func); + nix_value_decref(ctx, length_result); + nix_value_decref(ctx, test_list); + nix_value_decref(ctx, length_func); + nix_state_free(state); + nix_eval_state_builder_free(builder); + nix_store_free(store); + nix_c_context_free(ctx); + } +} diff --git a/nixide-sys/tests/flake.rs b/nixide-sys/tests/flake.rs new file mode 100644 index 0000000..01fed21 --- /dev/null +++ b/nixide-sys/tests/flake.rs @@ -0,0 +1,78 @@ +#![cfg(test)] + +use std::ptr; + +use nixide_sys::*; +use serial_test::serial; + +#[test] +#[serial] +fn flake_settings_new_and_free() { + unsafe { + let ctx = nix_c_context_create(); + assert!(!ctx.is_null()); + + // Create new flake settings + let settings = nix_flake_settings_new(ctx); + assert!(!settings.is_null(), "nix_flake_settings_new returned null"); + + // Free flake settings (should not crash) + nix_flake_settings_free(settings); + + nix_c_context_free(ctx); + } +} + +#[test] +#[serial] +fn flake_settings_add_to_eval_state_builder() { + unsafe { + let ctx = nix_c_context_create(); + assert!(!ctx.is_null()); + + let err = nix_libutil_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let err = nix_libstore_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let err = nix_libexpr_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let store = nix_store_open(ctx, ptr::null(), ptr::null_mut()); + assert!(!store.is_null()); + + let builder = nix_eval_state_builder_new(ctx, store); + assert!(!builder.is_null()); + + let settings = nix_flake_settings_new(ctx); + assert!(!settings.is_null(), "nix_flake_settings_new returned null"); + + // Add flake settings to eval state builder + let err = nix_flake_settings_add_to_eval_state_builder(ctx, settings, builder); + // Accept OK or ERR_UNKNOWN (depends on Nix build/config) + assert!( + err == nix_err_NIX_OK || err == nix_err_NIX_ERR_UNKNOWN, + "nix_flake_settings_add_to_eval_state_builder returned unexpected error \ + code: {err}" + ); + + nix_flake_settings_free(settings); + nix_eval_state_builder_free(builder); + nix_store_free(store); + nix_c_context_free(ctx); + } +} + +#[test] +#[serial] +fn flake_settings_null_context() { + // Passing NULL context should not crash, but may error + unsafe { + let settings = nix_flake_settings_new(ptr::null_mut()); + // May return null if context is required + if !settings.is_null() { + nix_flake_settings_free(settings); + } + } +} diff --git a/nixide-sys/tests/memory.rs b/nixide-sys/tests/memory.rs new file mode 100644 index 0000000..229b6bd --- /dev/null +++ b/nixide-sys/tests/memory.rs @@ -0,0 +1,503 @@ +#![cfg(test)] + +use std::ffi::CString; + +use nixide_sys::*; +use serial_test::serial; + +#[test] +#[serial] +fn value_reference_counting() { + unsafe { + let ctx = nix_c_context_create(); + assert!(!ctx.is_null()); + + let err = nix_libutil_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let err = nix_libstore_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let err = nix_libexpr_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let store = nix_store_open(ctx, std::ptr::null(), std::ptr::null_mut()); + assert!(!store.is_null()); + + let builder = nix_eval_state_builder_new(ctx, store); + assert!(!builder.is_null()); + + let load_err = nix_eval_state_builder_load(ctx, builder); + assert_eq!(load_err, nix_err_NIX_OK); + + let state = nix_eval_state_build(ctx, builder); + assert!(!state.is_null()); + + // Create a value + let value = nix_alloc_value(ctx, state); + assert!(!value.is_null()); + + // Initialize with an integer + let init_err = nix_init_int(ctx, value, 42); + assert_eq!(init_err, nix_err_NIX_OK); + + // Test value-specific reference counting + let incref_err = nix_value_incref(ctx, value); + assert_eq!(incref_err, nix_err_NIX_OK); + + // Value should still be valid after increment + let int_val = nix_get_int(ctx, value); + assert_eq!(int_val, 42); + + // Test decrement + let decref_err = nix_value_decref(ctx, value); + assert_eq!(decref_err, nix_err_NIX_OK); + + // Value should still be valid (original reference still exists) + let int_val2 = nix_get_int(ctx, value); + assert_eq!(int_val2, 42); + + // Final decrement (should not crash) + let final_decref_err = nix_value_decref(ctx, value); + assert_eq!(final_decref_err, nix_err_NIX_OK); + + // Clean up + nix_state_free(state); + nix_eval_state_builder_free(builder); + nix_store_free(store); + nix_c_context_free(ctx); + } +} + +#[test] +#[serial] +fn general_gc_reference_counting() { + unsafe { + let ctx = nix_c_context_create(); + assert!(!ctx.is_null()); + + let err = nix_libutil_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let err = nix_libstore_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let err = nix_libexpr_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let store = nix_store_open(ctx, std::ptr::null(), std::ptr::null_mut()); + assert!(!store.is_null()); + + let builder = nix_eval_state_builder_new(ctx, store); + assert!(!builder.is_null()); + + let load_err = nix_eval_state_builder_load(ctx, builder); + assert_eq!(load_err, nix_err_NIX_OK); + + let state = nix_eval_state_build(ctx, builder); + assert!(!state.is_null()); + + // Create a value for general GC testing + let value = nix_alloc_value(ctx, state); + assert!(!value.is_null()); + + let init_err = nix_init_string( + ctx, + value, + CString::new("test string for GC").unwrap().as_ptr(), + ); + assert_eq!(init_err, nix_err_NIX_OK); + + // Test general GC reference counting + let gc_incref_err = nix_gc_incref(ctx, value as *const ::std::os::raw::c_void); + assert_eq!(gc_incref_err, nix_err_NIX_OK); + + // Value should still be accessible + let value_type = nix_get_type(ctx, value); + assert_eq!(value_type, ValueType_NIX_TYPE_STRING); + + // Test GC decrement + let gc_decref_err = nix_gc_decref(ctx, value as *const ::std::os::raw::c_void); + assert_eq!(gc_decref_err, nix_err_NIX_OK); + + // Final cleanup + nix_value_decref(ctx, value); + nix_state_free(state); + nix_eval_state_builder_free(builder); + nix_store_free(store); + nix_c_context_free(ctx); + } +} + +#[test] +#[serial] +fn manual_garbage_collection() { + unsafe { + let ctx = nix_c_context_create(); + assert!(!ctx.is_null()); + + let err = nix_libutil_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let err = nix_libstore_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let err = nix_libexpr_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let store = nix_store_open(ctx, std::ptr::null(), std::ptr::null_mut()); + assert!(!store.is_null()); + + let builder = nix_eval_state_builder_new(ctx, store); + assert!(!builder.is_null()); + + let load_err = nix_eval_state_builder_load(ctx, builder); + assert_eq!(load_err, nix_err_NIX_OK); + + let state = nix_eval_state_build(ctx, builder); + assert!(!state.is_null()); + + // Create a few values to test basic GC functionality + let mut values = Vec::new(); + for i in 0..3 { + let value = nix_alloc_value(ctx, state); + if !value.is_null() { + let init_err = nix_init_int(ctx, value, i); + if init_err == nix_err_NIX_OK { + values.push(value); + } + } + } + + // Verify values are accessible before GC + for (i, &value) in values.iter().enumerate() { + let int_val = nix_get_int(ctx, value); + assert_eq!(int_val, i as i64); + } + + // Clean up values before attempting GC to avoid signal issues + for value in values { + nix_value_decref(ctx, value); + } + + nix_state_free(state); + nix_eval_state_builder_free(builder); + nix_store_free(store); + nix_c_context_free(ctx); + } +} + +#[test] +#[serial] +fn value_copying_and_memory_management() { + unsafe { + let ctx = nix_c_context_create(); + assert!(!ctx.is_null()); + + let err = nix_libutil_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let err = nix_libstore_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let err = nix_libexpr_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let store = nix_store_open(ctx, std::ptr::null(), std::ptr::null_mut()); + assert!(!store.is_null()); + + let builder = nix_eval_state_builder_new(ctx, store); + assert!(!builder.is_null()); + + let load_err = nix_eval_state_builder_load(ctx, builder); + assert_eq!(load_err, nix_err_NIX_OK); + + let state = nix_eval_state_build(ctx, builder); + assert!(!state.is_null()); + + // Create original value + let original = nix_alloc_value(ctx, state); + assert!(!original.is_null()); + + let test_string = CString::new("test string for copying").unwrap(); + let init_err = nix_init_string(ctx, original, test_string.as_ptr()); + assert_eq!(init_err, nix_err_NIX_OK); + + // Create copy + let copy = nix_alloc_value(ctx, state); + assert!(!copy.is_null()); + + let copy_err = nix_copy_value(ctx, copy, original); + assert_eq!(copy_err, nix_err_NIX_OK); + + // Verify copy has same type and can be accessed + let original_type = nix_get_type(ctx, original); + let copy_type = nix_get_type(ctx, copy); + assert_eq!(original_type, copy_type); + assert_eq!(copy_type, ValueType_NIX_TYPE_STRING); + + // Test string contents using callback + unsafe extern "C" fn string_callback( + start: *const ::std::os::raw::c_char, + n: ::std::os::raw::c_uint, + user_data: *mut ::std::os::raw::c_void, + ) { + let s = unsafe { std::slice::from_raw_parts(start.cast::(), n as usize) }; + let s = std::str::from_utf8(s).unwrap_or(""); + let result = unsafe { &mut *(user_data as *mut Option) }; + *result = Some(s.to_string()); + } + + let mut original_string: Option = None; + let mut copy_string: Option = None; + + let _ = nix_get_string( + ctx, + original, + Some(string_callback), + &mut original_string as *mut Option as *mut ::std::os::raw::c_void, + ); + + let _ = nix_get_string( + ctx, + copy, + Some(string_callback), + &mut copy_string as *mut Option as *mut ::std::os::raw::c_void, + ); + + // Both should have the same string content + assert_eq!(original_string, copy_string); + assert!(original_string + .as_deref() + .unwrap_or("") + .contains("test string")); + + // Test reference counting on both values + let incref_orig = nix_value_incref(ctx, original); + let incref_copy = nix_value_incref(ctx, copy); + assert_eq!(incref_orig, nix_err_NIX_OK); + assert_eq!(incref_copy, nix_err_NIX_OK); + + // Values should still be accessible after increment + assert_eq!(nix_get_type(ctx, original), ValueType_NIX_TYPE_STRING); + assert_eq!(nix_get_type(ctx, copy), ValueType_NIX_TYPE_STRING); + + // Clean up with decrements + nix_value_decref(ctx, original); + nix_value_decref(ctx, original); // extra decref from incref + nix_value_decref(ctx, copy); + nix_value_decref(ctx, copy); // extra decref from incref + + nix_state_free(state); + nix_eval_state_builder_free(builder); + nix_store_free(store); + nix_c_context_free(ctx); + } +} + +#[test] +#[serial] +fn complex_value_memory_management() { + unsafe { + let ctx = nix_c_context_create(); + assert!(!ctx.is_null()); + + let err = nix_libutil_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let err = nix_libstore_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let err = nix_libexpr_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let store = nix_store_open(ctx, std::ptr::null(), std::ptr::null_mut()); + assert!(!store.is_null()); + + let builder = nix_eval_state_builder_new(ctx, store); + assert!(!builder.is_null()); + + let load_err = nix_eval_state_builder_load(ctx, builder); + assert_eq!(load_err, nix_err_NIX_OK); + + let state = nix_eval_state_build(ctx, builder); + assert!(!state.is_null()); + + // Create a complex structure: list containing attribute sets + let list_builder = nix_make_list_builder(ctx, state, 2); + assert!(!list_builder.is_null()); + + // Create first element: attribute set + let attrs1 = nix_alloc_value(ctx, state); + assert!(!attrs1.is_null()); + + let bindings_builder1 = nix_make_bindings_builder(ctx, state, 2); + assert!(!bindings_builder1.is_null()); + + // Add attributes to first set + let key1 = CString::new("name").unwrap(); + let val1 = nix_alloc_value(ctx, state); + assert!(!val1.is_null()); + let name_str = CString::new("first").unwrap(); + let _ = nix_init_string(ctx, val1, name_str.as_ptr()); + + let insert_err1 = nix_bindings_builder_insert(ctx, bindings_builder1, key1.as_ptr(), val1); + assert_eq!(insert_err1, nix_err_NIX_OK); + + let key2 = CString::new("value").unwrap(); + let val2 = nix_alloc_value(ctx, state); + assert!(!val2.is_null()); + let _ = nix_init_int(ctx, val2, 42); + + let insert_err2 = nix_bindings_builder_insert(ctx, bindings_builder1, key2.as_ptr(), val2); + assert_eq!(insert_err2, nix_err_NIX_OK); + + let make_attrs_err1 = nix_make_attrs(ctx, attrs1, bindings_builder1); + assert_eq!(make_attrs_err1, nix_err_NIX_OK); + + // Insert first attrs into list + let list_insert_err1 = nix_list_builder_insert(ctx, list_builder, 0, attrs1); + assert_eq!(list_insert_err1, nix_err_NIX_OK); + + // Create second element + let attrs2 = nix_alloc_value(ctx, state); + assert!(!attrs2.is_null()); + + let bindings_builder2 = nix_make_bindings_builder(ctx, state, 1); + assert!(!bindings_builder2.is_null()); + + let key3 = CString::new("data").unwrap(); + let val3 = nix_alloc_value(ctx, state); + assert!(!val3.is_null()); + let data_str = CString::new("second").unwrap(); + let _ = nix_init_string(ctx, val3, data_str.as_ptr()); + + let insert_err3 = nix_bindings_builder_insert(ctx, bindings_builder2, key3.as_ptr(), val3); + assert_eq!(insert_err3, nix_err_NIX_OK); + + let make_attrs_err2 = nix_make_attrs(ctx, attrs2, bindings_builder2); + assert_eq!(make_attrs_err2, nix_err_NIX_OK); + + let list_insert_err2 = nix_list_builder_insert(ctx, list_builder, 1, attrs2); + assert_eq!(list_insert_err2, nix_err_NIX_OK); + + // Create final list + let final_list = nix_alloc_value(ctx, state); + assert!(!final_list.is_null()); + + let make_list_err = nix_make_list(ctx, list_builder, final_list); + assert_eq!(make_list_err, nix_err_NIX_OK); + + // Test the complex structure + assert_eq!(nix_get_type(ctx, final_list), ValueType_NIX_TYPE_LIST); + assert_eq!(nix_get_list_size(ctx, final_list), 2); + + // Access nested elements + let elem0 = nix_get_list_byidx(ctx, final_list, state, 0); + let elem1 = nix_get_list_byidx(ctx, final_list, state, 1); + assert!(!elem0.is_null() && !elem1.is_null()); + + assert_eq!(nix_get_type(ctx, elem0), ValueType_NIX_TYPE_ATTRS); + assert_eq!(nix_get_type(ctx, elem1), ValueType_NIX_TYPE_ATTRS); + + // Test memory management with deep copying + let copied_list = nix_alloc_value(ctx, state); + assert!(!copied_list.is_null()); + + let copy_err = nix_copy_value(ctx, copied_list, final_list); + assert_eq!(copy_err, nix_err_NIX_OK); + + // Force deep evaluation on copy + let deep_force_err = nix_value_force_deep(ctx, state, copied_list); + assert_eq!(deep_force_err, nix_err_NIX_OK); + + // Both should still be accessible + assert_eq!(nix_get_list_size(ctx, final_list), 2); + assert_eq!(nix_get_list_size(ctx, copied_list), 2); + + // Clean up all the values + nix_value_decref(ctx, copied_list); + nix_value_decref(ctx, final_list); + nix_value_decref(ctx, attrs2); + nix_value_decref(ctx, attrs1); + nix_value_decref(ctx, val3); + nix_value_decref(ctx, val2); + nix_value_decref(ctx, val1); + nix_state_free(state); + nix_eval_state_builder_free(builder); + nix_store_free(store); + nix_c_context_free(ctx); + } +} + +#[test] +#[serial] +fn memory_management_error_conditions() { + unsafe { + let ctx = nix_c_context_create(); + assert!(!ctx.is_null()); + + // Test reference counting with NULL pointers (should handle gracefully) + let null_incref_err = nix_gc_incref(ctx, std::ptr::null() as *const ::std::os::raw::c_void); + + // XXX: May succeed or fail depending on implementation. We can't really + // know, so assert both. + assert!(null_incref_err == nix_err_NIX_OK || null_incref_err == nix_err_NIX_ERR_UNKNOWN); + + let null_decref_err = nix_gc_decref(ctx, std::ptr::null() as *const ::std::os::raw::c_void); + assert!(null_decref_err == nix_err_NIX_OK || null_decref_err == nix_err_NIX_ERR_UNKNOWN); + + let null_value_incref_err = nix_value_incref(ctx, std::ptr::null_mut()); + // Some Nix APIs gracefully handle null pointers and return OK + assert!( + null_value_incref_err == nix_err_NIX_OK + || null_value_incref_err == nix_err_NIX_ERR_UNKNOWN + ); + + let null_value_decref_err = nix_value_decref(ctx, std::ptr::null_mut()); + // Some Nix APIs gracefully handle null pointers and return OK + assert!( + null_value_decref_err == nix_err_NIX_OK + || null_value_decref_err == nix_err_NIX_ERR_UNKNOWN + ); + + // Test copy with NULL values + let err = nix_libutil_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let err = nix_libstore_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let err = nix_libexpr_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let store = nix_store_open(ctx, std::ptr::null(), std::ptr::null_mut()); + assert!(!store.is_null()); + + let builder = nix_eval_state_builder_new(ctx, store); + assert!(!builder.is_null()); + + let load_err = nix_eval_state_builder_load(ctx, builder); + assert_eq!(load_err, nix_err_NIX_OK); + + let state = nix_eval_state_build(ctx, builder); + assert!(!state.is_null()); + + let valid_value = nix_alloc_value(ctx, state); + assert!(!valid_value.is_null()); + + // Test copying to/from NULL + let copy_from_null_err = nix_copy_value(ctx, valid_value, std::ptr::null_mut()); + assert_ne!(copy_from_null_err, nix_err_NIX_OK); + + let copy_to_null_err = nix_copy_value(ctx, std::ptr::null_mut(), valid_value); + assert_ne!(copy_to_null_err, nix_err_NIX_OK); + + // Clean up + nix_value_decref(ctx, valid_value); + nix_state_free(state); + nix_eval_state_builder_free(builder); + nix_store_free(store); + nix_c_context_free(ctx); + } +} diff --git a/nixide-sys/tests/primop.rs b/nixide-sys/tests/primop.rs new file mode 100644 index 0000000..ecfacf8 --- /dev/null +++ b/nixide-sys/tests/primop.rs @@ -0,0 +1,639 @@ +#![cfg(test)] + +use std::{ + ffi::CString, + sync::atomic::{AtomicU32, Ordering}, +}; + +use nixide_sys::*; +use serial_test::serial; + +#[derive(Debug)] +struct TestPrimOpData { + call_count: AtomicU32, + last_arg_value: AtomicU32, +} + +// Simple PrimOp that adds 1 to an integer argument +unsafe extern "C" fn add_one_primop( + user_data: *mut ::std::os::raw::c_void, + context: *mut nix_c_context, + state: *mut EvalState, + args: *mut *mut nix_value, + ret: *mut nix_value, +) { + if user_data.is_null() + || context.is_null() + || state.is_null() + || args.is_null() + || ret.is_null() + { + let _ = unsafe { + nix_set_err_msg( + context, + nix_err_NIX_ERR_UNKNOWN, + b"Null pointer in add_one_primop\0".as_ptr() as *const _, + ) + }; + return; + } + + let data = unsafe { &*(user_data as *const TestPrimOpData) }; + data.call_count.fetch_add(1, Ordering::SeqCst); + + // Get first argument + let arg = unsafe { *args.offset(0) }; + if arg.is_null() { + let _ = unsafe { + nix_set_err_msg( + context, + nix_err_NIX_ERR_UNKNOWN, + b"Missing argument in add_one_primop\0".as_ptr() as *const _, + ) + }; + return; + } + + // Force evaluation of argument + if unsafe { nix_value_force(context, state, arg) } != nix_err_NIX_OK { + return; + } + + // Check if argument is integer + if unsafe { nix_get_type(context, arg) } != ValueType_NIX_TYPE_INT { + let _ = unsafe { + nix_set_err_msg( + context, + nix_err_NIX_ERR_UNKNOWN, + b"Expected integer argument in add_one_primop\0".as_ptr() as *const _, + ) + }; + return; + } + + // Get integer value and add 1 + let value = unsafe { nix_get_int(context, arg) }; + data.last_arg_value.store(value as u32, Ordering::SeqCst); + + // Set return value + let _ = unsafe { nix_init_int(context, ret, value + 1) }; +} + +// PrimOp that returns a constant string +unsafe extern "C" fn hello_world_primop( + _user_data: *mut ::std::os::raw::c_void, + context: *mut nix_c_context, + _state: *mut EvalState, + _args: *mut *mut nix_value, + ret: *mut nix_value, +) { + let hello = CString::new("Hello from Rust PrimOp!").unwrap(); + let _ = unsafe { nix_init_string(context, ret, hello.as_ptr()) }; +} + +// PrimOp that takes multiple arguments and concatenates them +unsafe extern "C" fn concat_strings_primop( + _user_data: *mut ::std::os::raw::c_void, + context: *mut nix_c_context, + state: *mut EvalState, + args: *mut *mut nix_value, + ret: *mut nix_value, +) { + if context.is_null() || state.is_null() || args.is_null() || ret.is_null() { + return; + } + + // This PrimOp expects exactly 2 string arguments + let mut result = String::new(); + + for i in 0..2 { + let arg = unsafe { *args.offset(i) }; + if arg.is_null() { + let _ = unsafe { + nix_set_err_msg( + context, + nix_err_NIX_ERR_UNKNOWN, + b"Missing argument in concat_strings_primop\0".as_ptr() as *const _, + ) + }; + return; + } + + // Force evaluation + if unsafe { nix_value_force(context, state, arg) } != nix_err_NIX_OK { + return; + } + + // Check if it's a string + if unsafe { nix_get_type(context, arg) } != ValueType_NIX_TYPE_STRING { + let _ = unsafe { + static ITEMS: &[u8] = b"Expected string argument in concat_strings_primop\0"; + nix_set_err_msg(context, nix_err_NIX_ERR_UNKNOWN, ITEMS.as_ptr() as *const _) + }; + return; + } + + // Get string value using callback + unsafe extern "C" fn string_callback( + start: *const ::std::os::raw::c_char, + n: ::std::os::raw::c_uint, + user_data: *mut ::std::os::raw::c_void, + ) { + let s = unsafe { std::slice::from_raw_parts(start.cast::(), n as usize) }; + let s = std::str::from_utf8(s).unwrap_or(""); + let result = unsafe { &mut *(user_data as *mut String) }; + result.push_str(s); + } + + let _ = unsafe { + nix_get_string( + context, + arg, + Some(string_callback), + &mut result as *mut String as *mut ::std::os::raw::c_void, + ) + }; + } + + let result_cstr = CString::new(result).unwrap_or_else(|_| CString::new("").unwrap()); + let _ = unsafe { nix_init_string(context, ret, result_cstr.as_ptr()) }; +} + +#[test] +#[serial] +fn primop_allocation_and_registration() { + unsafe { + let ctx = nix_c_context_create(); + assert!(!ctx.is_null()); + + let err = nix_libutil_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let err = nix_libstore_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let err = nix_libexpr_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let store = nix_store_open(ctx, std::ptr::null(), std::ptr::null_mut()); + assert!(!store.is_null()); + + let builder = nix_eval_state_builder_new(ctx, store); + assert!(!builder.is_null()); + + let load_err = nix_eval_state_builder_load(ctx, builder); + assert_eq!(load_err, nix_err_NIX_OK); + + let state = nix_eval_state_build(ctx, builder); + assert!(!state.is_null()); + + // Create test data + let test_data = Box::new(TestPrimOpData { + call_count: AtomicU32::new(0), + last_arg_value: AtomicU32::new(0), + }); + let test_data_ptr = Box::into_raw(test_data); + + // Create argument names + let arg_names = [CString::new("x").unwrap()]; + let arg_name_ptrs: Vec<*const _> = arg_names.iter().map(|s| s.as_ptr()).collect(); + let mut arg_name_ptrs_null_terminated = arg_name_ptrs; + arg_name_ptrs_null_terminated.push(std::ptr::null()); + + let name = CString::new("addOne").unwrap(); + let doc = CString::new("Add 1 to the argument").unwrap(); + + // Allocate PrimOp + let primop = nix_alloc_primop( + ctx, + Some(add_one_primop), + 1, // arity + name.as_ptr(), + arg_name_ptrs_null_terminated.as_mut_ptr(), + doc.as_ptr(), + test_data_ptr as *mut ::std::os::raw::c_void, + ); + + if !primop.is_null() { + // Register the PrimOp globally + let register_err = nix_register_primop(ctx, primop); + // Registration may fail in some environments, but allocation should work + assert!( + register_err == nix_err_NIX_OK || register_err == nix_err_NIX_ERR_UNKNOWN, + "Unexpected error code: {register_err}" + ); + + // Test using the PrimOp by creating a value with it + let primop_value = nix_alloc_value(ctx, state); + assert!(!primop_value.is_null()); + + let init_err = nix_init_primop(ctx, primop_value, primop); + assert_eq!(init_err, nix_err_NIX_OK); + + // Clean up value + nix_value_decref(ctx, primop_value); + } + + // Clean up + let _ = Box::from_raw(test_data_ptr); + nix_state_free(state); + nix_eval_state_builder_free(builder); + nix_store_free(store); + nix_c_context_free(ctx); + } +} + +#[test] +#[serial] +fn primop_function_call() { + unsafe { + let ctx = nix_c_context_create(); + assert!(!ctx.is_null()); + + let err = nix_libutil_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let err = nix_libstore_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let err = nix_libexpr_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let store = nix_store_open(ctx, std::ptr::null(), std::ptr::null_mut()); + assert!(!store.is_null()); + + let builder = nix_eval_state_builder_new(ctx, store); + assert!(!builder.is_null()); + + let load_err = nix_eval_state_builder_load(ctx, builder); + assert_eq!(load_err, nix_err_NIX_OK); + + let state = nix_eval_state_build(ctx, builder); + assert!(!state.is_null()); + + // Create test data + let test_data = Box::new(TestPrimOpData { + call_count: AtomicU32::new(0), + last_arg_value: AtomicU32::new(0), + }); + let test_data_ptr = Box::into_raw(test_data); + + // Create simple hello world PrimOp (no arguments) + let name = CString::new("helloWorld").unwrap(); + let doc = CString::new("Returns hello world string").unwrap(); + let mut empty_args: Vec<*const ::std::os::raw::c_char> = vec![std::ptr::null()]; + + let hello_primop = nix_alloc_primop( + ctx, + Some(hello_world_primop), + 0, // arity + name.as_ptr(), + empty_args.as_mut_ptr(), + doc.as_ptr(), + std::ptr::null_mut(), + ); + + if !hello_primop.is_null() { + // Create a value with the PrimOp + let primop_value = nix_alloc_value(ctx, state); + assert!(!primop_value.is_null()); + + let init_err = nix_init_primop(ctx, primop_value, hello_primop); + assert_eq!(init_err, nix_err_NIX_OK); + + // Call the PrimOp (no arguments) + let result = nix_alloc_value(ctx, state); + assert!(!result.is_null()); + + let call_err = nix_value_call(ctx, state, primop_value, std::ptr::null_mut(), result); + if call_err == nix_err_NIX_OK { + // Force the result + let force_err = nix_value_force(ctx, state, result); + assert_eq!(force_err, nix_err_NIX_OK); + + // Check if result is a string + let result_type = nix_get_type(ctx, result); + if result_type == ValueType_NIX_TYPE_STRING { + // Get string value + unsafe extern "C" fn string_callback( + start: *const ::std::os::raw::c_char, + n: ::std::os::raw::c_uint, + user_data: *mut ::std::os::raw::c_void, + ) { + let s = + unsafe { std::slice::from_raw_parts(start.cast::(), n as usize) }; + let s = std::str::from_utf8(s).unwrap_or(""); + let result = unsafe { &mut *(user_data as *mut Option) }; + *result = Some(s.to_string()); + } + + let mut string_result: Option = None; + let _ = nix_get_string( + ctx, + result, + Some(string_callback), + &mut string_result as *mut Option as *mut ::std::os::raw::c_void, + ); + + // Verify we got the expected string + assert!(string_result + .as_deref() + .unwrap_or("") + .contains("Hello from Rust")); + } + } + + nix_value_decref(ctx, result); + nix_value_decref(ctx, primop_value); + } + + // Clean up + let _ = Box::from_raw(test_data_ptr); + nix_state_free(state); + nix_eval_state_builder_free(builder); + nix_store_free(store); + nix_c_context_free(ctx); + } +} + +#[test] +#[serial] +fn primop_with_arguments() { + unsafe { + let ctx = nix_c_context_create(); + assert!(!ctx.is_null()); + + let err = nix_libutil_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let err = nix_libstore_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let err = nix_libexpr_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let store = nix_store_open(ctx, std::ptr::null(), std::ptr::null_mut()); + assert!(!store.is_null()); + + let builder = nix_eval_state_builder_new(ctx, store); + assert!(!builder.is_null()); + + let load_err = nix_eval_state_builder_load(ctx, builder); + assert_eq!(load_err, nix_err_NIX_OK); + + let state = nix_eval_state_build(ctx, builder); + assert!(!state.is_null()); + + // Create test data + let test_data = Box::new(TestPrimOpData { + call_count: AtomicU32::new(0), + last_arg_value: AtomicU32::new(0), + }); + let test_data_ptr = Box::into_raw(test_data); + + // Create add one PrimOp + let arg_names = [CString::new("x").unwrap()]; + let arg_name_ptrs: Vec<*const _> = arg_names.iter().map(|s| s.as_ptr()).collect(); + let mut arg_name_ptrs_null_terminated = arg_name_ptrs; + arg_name_ptrs_null_terminated.push(std::ptr::null()); + + let name = CString::new("addOne").unwrap(); + let doc = CString::new("Add 1 to the argument").unwrap(); + + let add_primop = nix_alloc_primop( + ctx, + Some(add_one_primop), + 1, // arity + name.as_ptr(), + arg_name_ptrs_null_terminated.as_mut_ptr(), + doc.as_ptr(), + test_data_ptr as *mut ::std::os::raw::c_void, + ); + + if !add_primop.is_null() { + // Create a value with the PrimOp + let primop_value = nix_alloc_value(ctx, state); + assert!(!primop_value.is_null()); + + let init_err = nix_init_primop(ctx, primop_value, add_primop); + assert_eq!(init_err, nix_err_NIX_OK); + + // Create an integer argument + let arg_value = nix_alloc_value(ctx, state); + assert!(!arg_value.is_null()); + + let init_arg_err = nix_init_int(ctx, arg_value, 42); + assert_eq!(init_arg_err, nix_err_NIX_OK); + + // Call the PrimOp with the argument + let result = nix_alloc_value(ctx, state); + assert!(!result.is_null()); + + let call_err = nix_value_call(ctx, state, primop_value, arg_value, result); + if call_err == nix_err_NIX_OK { + // Force the result + let force_err = nix_value_force(ctx, state, result); + assert_eq!(force_err, nix_err_NIX_OK); + + // Check if result is an integer + let result_type = nix_get_type(ctx, result); + if result_type == ValueType_NIX_TYPE_INT { + let result_value = nix_get_int(ctx, result); + assert_eq!(result_value, 43); // 42 + 1 + + // Verify callback was called + let test_data_ref = &*test_data_ptr; + assert_eq!(test_data_ref.call_count.load(Ordering::SeqCst), 1); + assert_eq!(test_data_ref.last_arg_value.load(Ordering::SeqCst), 42); + } + } + + nix_value_decref(ctx, result); + nix_value_decref(ctx, arg_value); + nix_value_decref(ctx, primop_value); + } + + // Clean up + let _ = Box::from_raw(test_data_ptr); + nix_state_free(state); + nix_eval_state_builder_free(builder); + nix_store_free(store); + nix_c_context_free(ctx); + } +} + +#[test] +#[serial] +fn primop_multi_argument() { + unsafe { + let ctx = nix_c_context_create(); + assert!(!ctx.is_null()); + + let err = nix_libutil_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let err = nix_libstore_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let err = nix_libexpr_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let store = nix_store_open(ctx, std::ptr::null(), std::ptr::null_mut()); + assert!(!store.is_null()); + + let builder = nix_eval_state_builder_new(ctx, store); + assert!(!builder.is_null()); + + let load_err = nix_eval_state_builder_load(ctx, builder); + assert_eq!(load_err, nix_err_NIX_OK); + + let state = nix_eval_state_build(ctx, builder); + assert!(!state.is_null()); + + // Create concat strings PrimOp + let arg_names = [CString::new("s1").unwrap(), CString::new("s2").unwrap()]; + let arg_name_ptrs: Vec<*const _> = arg_names.iter().map(|s| s.as_ptr()).collect(); + let mut arg_name_ptrs_null_terminated = arg_name_ptrs; + arg_name_ptrs_null_terminated.push(std::ptr::null()); + + let name = CString::new("concatStrings").unwrap(); + let doc = CString::new("Concatenate two strings").unwrap(); + + let concat_primop = nix_alloc_primop( + ctx, + Some(concat_strings_primop), + 2, // arity + name.as_ptr(), + arg_name_ptrs_null_terminated.as_mut_ptr(), + doc.as_ptr(), + std::ptr::null_mut(), + ); + + if !concat_primop.is_null() { + // Create a value with the PrimOp + let primop_value = nix_alloc_value(ctx, state); + assert!(!primop_value.is_null()); + + let init_err = nix_init_primop(ctx, primop_value, concat_primop); + assert_eq!(init_err, nix_err_NIX_OK); + + // Create string arguments + let arg1 = nix_alloc_value(ctx, state); + let arg2 = nix_alloc_value(ctx, state); + assert!(!arg1.is_null() && !arg2.is_null()); + + let hello_cstr = CString::new("Hello, ").unwrap(); + let world_cstr = CString::new("World!").unwrap(); + + let init_arg1_err = nix_init_string(ctx, arg1, hello_cstr.as_ptr()); + let init_arg2_err = nix_init_string(ctx, arg2, world_cstr.as_ptr()); + assert_eq!(init_arg1_err, nix_err_NIX_OK); + assert_eq!(init_arg2_err, nix_err_NIX_OK); + + // Test multi-argument call using nix_value_call_multi + let mut args = [arg1, arg2]; + let result = nix_alloc_value(ctx, state); + assert!(!result.is_null()); + + let call_err = + nix_value_call_multi(ctx, state, primop_value, 2, args.as_mut_ptr(), result); + if call_err == nix_err_NIX_OK { + // Force the result + let force_err = nix_value_force(ctx, state, result); + assert_eq!(force_err, nix_err_NIX_OK); + + // Check if result is a string + let result_type = nix_get_type(ctx, result); + if result_type == ValueType_NIX_TYPE_STRING { + unsafe extern "C" fn string_callback( + start: *const ::std::os::raw::c_char, + n: ::std::os::raw::c_uint, + user_data: *mut ::std::os::raw::c_void, + ) { + let s = + unsafe { std::slice::from_raw_parts(start.cast::(), n as usize) }; + let s = std::str::from_utf8(s).unwrap_or(""); + let result = unsafe { &mut *(user_data as *mut Option) }; + *result = Some(s.to_string()); + } + + let mut string_result: Option = None; + let _ = nix_get_string( + ctx, + result, + Some(string_callback), + &mut string_result as *mut Option as *mut ::std::os::raw::c_void, + ); + + // Verify concatenation worked + assert_eq!(string_result.as_deref(), Some("Hello, World!")); + } + } + + nix_value_decref(ctx, result); + nix_value_decref(ctx, arg2); + nix_value_decref(ctx, arg1); + nix_value_decref(ctx, primop_value); + } + + nix_state_free(state); + nix_eval_state_builder_free(builder); + nix_store_free(store); + nix_c_context_free(ctx); + } +} + +#[test] +#[serial] +fn primop_error_handling() { + unsafe { + let ctx = nix_c_context_create(); + assert!(!ctx.is_null()); + + let err = nix_libutil_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let err = nix_libstore_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let err = nix_libexpr_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let store = nix_store_open(ctx, std::ptr::null(), std::ptr::null_mut()); + assert!(!store.is_null()); + + let builder = nix_eval_state_builder_new(ctx, store); + assert!(!builder.is_null()); + + let load_err = nix_eval_state_builder_load(ctx, builder); + assert_eq!(load_err, nix_err_NIX_OK); + + let state = nix_eval_state_build(ctx, builder); + assert!(!state.is_null()); + + // Test invalid PrimOp allocation (NULL callback) + let name = CString::new("invalid").unwrap(); + let doc = CString::new("Invalid PrimOp").unwrap(); + let mut empty_args: Vec<*const ::std::os::raw::c_char> = vec![std::ptr::null()]; + + let _invalid_primop = nix_alloc_primop( + ctx, + None, // NULL callback should cause error + 0, + name.as_ptr(), + empty_args.as_mut_ptr(), + doc.as_ptr(), + std::ptr::null_mut(), + ); + + // Test initializing value with NULL PrimOp (should fail) + let test_value = nix_alloc_value(ctx, state); + assert!(!test_value.is_null()); + + nix_value_decref(ctx, test_value); + nix_state_free(state); + nix_eval_state_builder_free(builder); + nix_store_free(store); + nix_c_context_free(ctx); + } +} diff --git a/nixide-sys/tests/store.rs b/nixide-sys/tests/store.rs new file mode 100644 index 0000000..bb12a33 --- /dev/null +++ b/nixide-sys/tests/store.rs @@ -0,0 +1,278 @@ +#![cfg(test)] + +use std::{ffi::CString, ptr}; + +use nixide_sys::*; +use serial_test::serial; + +#[test] +#[serial] +fn libstore_init_and_open_free() { + unsafe { + let ctx = nix_c_context_create(); + assert!(!ctx.is_null()); + let err = nix_libstore_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + // Open the default store (NULL URI, NULL params) + let store = nix_store_open(ctx, ptr::null(), ptr::null_mut()); + assert!(!store.is_null()); + + // Free the store and context + nix_store_free(store); + nix_c_context_free(ctx); + } +} + +#[test] +#[serial] +fn parse_and_clone_free_store_path() { + unsafe { + let ctx = nix_c_context_create(); + assert!(!ctx.is_null()); + let err = nix_libstore_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let store = nix_store_open(ctx, ptr::null(), ptr::null_mut()); + assert!(!store.is_null()); + + // Parse a store path (I'm using a dummy path, will likely be invalid but + // should not segfault) XXX: store_path may be null if path is invalid, + // but should not crash + let path_str = CString::new("/nix/store/dummy-path").unwrap(); + let store_path = nix_store_parse_path(ctx, store, path_str.as_ptr()); + + if !store_path.is_null() { + // Clone and free + let cloned = nix_store_path_clone(store_path); + assert!(!cloned.is_null()); + nix_store_path_free(cloned); + nix_store_path_free(store_path); + } + + nix_store_free(store); + nix_c_context_free(ctx); + } +} + +#[test] +#[serial] +fn store_get_uri_and_storedir() { + unsafe extern "C" fn string_callback( + start: *const ::std::os::raw::c_char, + n: ::std::os::raw::c_uint, + user_data: *mut ::std::os::raw::c_void, + ) { + let s = unsafe { std::slice::from_raw_parts(start.cast::(), n as usize) }; + let s = std::str::from_utf8(s).unwrap(); + let out = user_data.cast::>(); + unsafe { *out = Some(s.to_string()) }; + } + + unsafe { + let ctx = nix_c_context_create(); + assert!(!ctx.is_null()); + let err = nix_libstore_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let store = nix_store_open(ctx, ptr::null(), ptr::null_mut()); + assert!(!store.is_null()); + + let mut uri: Option = None; + let res = nix_store_get_uri(ctx, store, Some(string_callback), (&raw mut uri).cast()); + assert_eq!(res, nix_err_NIX_OK); + assert!(uri.is_some()); + + let mut storedir: Option = None; + let res = nix_store_get_storedir( + ctx, + store, + Some(string_callback), + (&raw mut storedir).cast(), + ); + assert_eq!(res, nix_err_NIX_OK); + assert!(storedir.is_some()); + + nix_store_free(store); + nix_c_context_free(ctx); + } +} + +#[test] +#[serial] +fn libstore_init_no_load_config() { + unsafe { + let ctx = nix_c_context_create(); + assert!(!ctx.is_null()); + let err = nix_libstore_init_no_load_config(ctx); + assert_eq!(err, nix_err_NIX_OK); + nix_c_context_free(ctx); + } +} + +#[test] +#[serial] +fn store_is_valid_path_and_real_path() { + unsafe extern "C" fn string_callback( + start: *const ::std::os::raw::c_char, + n: ::std::os::raw::c_uint, + user_data: *mut ::std::os::raw::c_void, + ) { + let s = unsafe { std::slice::from_raw_parts(start.cast::(), n as usize) }; + let s = std::str::from_utf8(s).unwrap(); + let out = user_data.cast::>(); + unsafe { *out = Some(s.to_string()) }; + } + + unsafe { + let ctx = nix_c_context_create(); + assert!(!ctx.is_null()); + let err = nix_libstore_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let store = nix_store_open(ctx, std::ptr::null(), std::ptr::null_mut()); + assert!(!store.is_null()); + + // Use a dummy path (should not be valid, but should not crash) + let path_str = CString::new("/nix/store/dummy-path").unwrap(); + let store_path = nix_store_parse_path(ctx, store, path_str.as_ptr()); + if !store_path.is_null() { + let valid = nix_store_is_valid_path(ctx, store, store_path); + assert!(!valid, "Dummy path should not be valid"); + + let mut real_path: Option = None; + let res = nix_store_real_path( + ctx, + store, + store_path, + Some(string_callback), + (&raw mut real_path).cast(), + ); + // May fail, but should not crash + assert!(res == nix_err_NIX_OK || res == nix_err_NIX_ERR_UNKNOWN); + nix_store_path_free(store_path); + } + + nix_store_free(store); + nix_c_context_free(ctx); + } +} + +#[test] +#[serial] +fn store_path_name() { + unsafe extern "C" fn string_callback( + start: *const ::std::os::raw::c_char, + n: ::std::os::raw::c_uint, + user_data: *mut ::std::os::raw::c_void, + ) { + let s = unsafe { std::slice::from_raw_parts(start.cast::(), n as usize) }; + let s = std::str::from_utf8(s).unwrap(); + let out = user_data.cast::>(); + unsafe { *out = Some(s.to_string()) }; + } + + unsafe { + let ctx = nix_c_context_create(); + assert!(!ctx.is_null()); + let err = nix_libstore_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let store = nix_store_open(ctx, std::ptr::null(), std::ptr::null_mut()); + assert!(!store.is_null()); + + let path_str = CString::new("/nix/store/foo-bar-baz").unwrap(); + let store_path = nix_store_parse_path(ctx, store, path_str.as_ptr()); + if !store_path.is_null() { + let mut name: Option = None; + nix_store_path_name(store_path, Some(string_callback), (&raw mut name).cast()); + // Should extract the name part ("foo-bar-baz") + assert!(name.as_deref().unwrap_or("").contains("foo-bar-baz")); + nix_store_path_free(store_path); + } + + nix_store_free(store); + nix_c_context_free(ctx); + } +} + +#[test] +#[serial] +fn store_get_version() { + unsafe extern "C" fn string_callback( + start: *const ::std::os::raw::c_char, + n: ::std::os::raw::c_uint, + user_data: *mut ::std::os::raw::c_void, + ) { + let s = unsafe { std::slice::from_raw_parts(start.cast::(), n as usize) }; + let s = std::str::from_utf8(s).unwrap(); + let out = user_data.cast::>(); + unsafe { *out = Some(s.to_string()) }; + } + + unsafe { + let ctx = nix_c_context_create(); + assert!(!ctx.is_null()); + let err = nix_libstore_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let store = nix_store_open(ctx, std::ptr::null(), std::ptr::null_mut()); + assert!(!store.is_null()); + + let mut version: Option = None; + let res = + nix_store_get_version(ctx, store, Some(string_callback), (&raw mut version).cast()); + assert_eq!(res, nix_err_NIX_OK); + // Version may be empty for dummy stores, but should not crash + assert!(version.is_some()); + + nix_store_free(store); + nix_c_context_free(ctx); + } +} + +#[test] +#[serial] +fn store_realise_and_copy_closure() { + unsafe extern "C" fn realise_callback( + _userdata: *mut ::std::os::raw::c_void, + outname: *const ::std::os::raw::c_char, + out: *const StorePath, + ) { + // Just check that callback is called with non-null pointers + assert!(!outname.is_null()); + assert!(!out.is_null()); + } + + unsafe { + let ctx = nix_c_context_create(); + assert!(!ctx.is_null()); + let err = nix_libstore_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + let store = nix_store_open(ctx, std::ptr::null(), std::ptr::null_mut()); + assert!(!store.is_null()); + + // Use a dummy path (should not crash, may not realise) + let path_str = CString::new("/nix/store/dummy-path").unwrap(); + let store_path = nix_store_parse_path(ctx, store, path_str.as_ptr()); + if !store_path.is_null() { + // Realise (should fail, but must not crash) + let _ = nix_store_realise( + ctx, + store, + store_path, + std::ptr::null_mut(), + Some(realise_callback), + ); + + // Copy closure to same store (should fail, but must not crash) + let _ = nix_store_copy_closure(ctx, store, store, store_path); + + nix_store_path_free(store_path); + } + + nix_store_free(store); + nix_c_context_free(ctx); + } +} diff --git a/nixide-sys/tests/util.rs b/nixide-sys/tests/util.rs new file mode 100644 index 0000000..038d30b --- /dev/null +++ b/nixide-sys/tests/util.rs @@ -0,0 +1,152 @@ +#![cfg(test)] + +use std::ffi::{CStr, CString}; + +use nixide_sys::*; +use serial_test::serial; + +#[test] +#[serial] +fn context_create_and_free() { + unsafe { + let ctx = nix_c_context_create(); + assert!(!ctx.is_null()); + nix_c_context_free(ctx); + } +} + +#[test] +#[serial] +fn libutil_init() { + unsafe { + let ctx = nix_c_context_create(); + assert!(!ctx.is_null()); + let err = nix_libutil_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + nix_c_context_free(ctx); + } +} + +#[test] +#[serial] +fn version_get() { + unsafe { + let version_ptr = nix_version_get(); + assert!(!version_ptr.is_null()); + let version = CStr::from_ptr(version_ptr).to_string_lossy(); + assert!(!version.is_empty(), "Version string should not be empty"); + assert!( + version.chars().next().unwrap().is_ascii_digit(), + "Version string should start with a digit: {version}" + ); + } +} + +#[test] +#[serial] +fn setting_set_and_get() { + unsafe extern "C" fn string_callback( + start: *const ::std::os::raw::c_char, + n: ::std::os::raw::c_uint, + user_data: *mut ::std::os::raw::c_void, + ) { + let s = unsafe { std::slice::from_raw_parts(start.cast::(), n as usize) }; + let s = std::str::from_utf8(s).unwrap(); + let out = user_data.cast::>(); + *unsafe { &mut *out } = Some(s.to_string()); + } + + unsafe { + let ctx = nix_c_context_create(); + assert!(!ctx.is_null()); + let err = nix_libutil_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + // Set a setting (use a dummy/extra setting to avoid breaking global config) + let key = CString::new("extra-test-setting").unwrap(); + let value = CString::new("test-value").unwrap(); + let set_err = nix_setting_set(ctx, key.as_ptr(), value.as_ptr()); + // Setting may not exist, but should not crash + assert!( + set_err == nix_err_NIX_OK || set_err == nix_err_NIX_ERR_KEY, + "Unexpected error code: {set_err}" + ); + + // Try to get the setting (may not exist, but should not crash) + let mut got: Option = None; + let get_err = nix_setting_get( + ctx, + key.as_ptr(), + Some(string_callback), + (&raw mut got).cast(), + ); + assert!( + get_err == nix_err_NIX_OK || get_err == nix_err_NIX_ERR_KEY, + "Unexpected error code: {get_err}" + ); + // If OK, we should have gotten a value + if get_err == nix_err_NIX_OK { + assert_eq!(got.as_deref(), Some("test-value")); + } + + nix_c_context_free(ctx); + } +} + +#[test] +#[serial] +fn error_handling_apis() { + unsafe extern "C" fn string_callback( + start: *const ::std::os::raw::c_char, + n: ::std::os::raw::c_uint, + user_data: *mut ::std::os::raw::c_void, + ) { + let s = unsafe { std::slice::from_raw_parts(start.cast::(), n as usize) }; + let s = std::str::from_utf8(s).unwrap(); + let out = user_data.cast::>(); + *unsafe { &mut *out } = Some(s.to_string()); + } + + unsafe { + let ctx = nix_c_context_create(); + assert!(!ctx.is_null()); + let err = nix_libutil_init(ctx); + assert_eq!(err, nix_err_NIX_OK); + + // Set an error message + let msg = CString::new("custom error message").unwrap(); + let set_err = nix_set_err_msg(ctx, nix_err_NIX_ERR_UNKNOWN, msg.as_ptr()); + assert_eq!(set_err, nix_err_NIX_ERR_UNKNOWN); + + // Get error code + let code = nix_err_code(ctx); + assert_eq!(code, nix_err_NIX_ERR_UNKNOWN); + + // Get error message + let mut len: std::os::raw::c_uint = 0; + let err_msg_ptr = nix_err_msg(ctx, ctx, &mut len as *mut _); + if !err_msg_ptr.is_null() && len > 0 { + let err_msg = std::str::from_utf8(std::slice::from_raw_parts( + err_msg_ptr as *const u8, + len as usize, + )) + .unwrap(); + assert!(err_msg.contains("custom error message")); + } + + // Get error info message (should work, but may be empty) + let mut info: Option = None; + let _ = nix_err_info_msg(ctx, ctx, Some(string_callback), (&raw mut info).cast()); + + // Get error name (should work, but may be empty) + let mut name: Option = None; + let _ = nix_err_name(ctx, ctx, Some(string_callback), (&raw mut name).cast()); + + // Clear error + nix_clear_err(ctx); + let code_after_clear = nix_err_code(ctx); + assert_eq!(code_after_clear, nix_err_NIX_OK); + + nix_c_context_free(ctx); + } +} From 49a3d9d9d9a360f08d232b24d8369c46eddf512e Mon Sep 17 00:00:00 2001 From: _cry64 Date: Fri, 13 Mar 2026 23:40:48 +1000 Subject: [PATCH 278/306] good use of my time mhmm --- flake.nix | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/flake.nix b/flake.nix index 721ad93..c0c523c 100644 --- a/flake.nix +++ b/flake.nix @@ -55,7 +55,8 @@ lldb cargo - # cargo-c + cargo-c + cargo-mommy cargo-llvm-cov cargo-nextest @@ -104,7 +105,12 @@ BINDGEN_EXTRA_CLANG_ARGS="$BINDGEN_EXTRA_CLANG_ARGS -I$path" done ''; - shellHook = postConfigure; + + shellHook = + postConfigure + + '' + alias cargo="CARGO_MOMMYS_MOODS=\"chill/ominous/thirsty/yikes\" ${pkgs.cargo}/bin/cargo mommy" + ''; env = let inherit (llvmPackages) llvm libclang; From 4d4d54e4d3a5d343e2d827e8d2b3b862f25ccc2e Mon Sep 17 00:00:00 2001 From: _cry64 Date: Fri, 13 Mar 2026 23:41:43 +1000 Subject: [PATCH 279/306] the fox will love this one <3 --- flake.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/flake.nix b/flake.nix index c0c523c..b1b95ce 100644 --- a/flake.nix +++ b/flake.nix @@ -109,6 +109,7 @@ shellHook = postConfigure + '' + # the fox will love this one <3 alias cargo="CARGO_MOMMYS_MOODS=\"chill/ominous/thirsty/yikes\" ${pkgs.cargo}/bin/cargo mommy" ''; From c1181fdb2a243b00a10a55df8494b374518a5a9d Mon Sep 17 00:00:00 2001 From: _cry64 Date: Fri, 13 Mar 2026 23:48:55 +1000 Subject: [PATCH 280/306] add nixide workspace project --- Cargo.toml | 2 +- nixide/Cargo.toml | 27 +++++++++++++++++++++++++++ nixide/src/lib.rs | 14 ++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 nixide/Cargo.toml create mode 100644 nixide/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index e8dcde9..4b249ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [workspace] resolver = "3" members = [ - # "nixide", + "nixide", "nixide-sys" ] diff --git a/nixide/Cargo.toml b/nixide/Cargo.toml new file mode 100644 index 0000000..4c640de --- /dev/null +++ b/nixide/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "nixide" +description = "Safe & oxidized bindings to the Nix API" +version = "0.1.0" +readme = "../README.md" +license = "GPL-3.0" +repository = "https://codeberg.org/luminary/nixide" +authors = [ + "_cry64 ", + "foxxyora " +] +edition = "2024" + +[lib] +path = "src/lib.rs" + +[features] +default = ["util"] +expr = [] +fetchers = [] +flakes = [] +store = [] +util = [] +gc = [] + +[dependencies] +nixide-sys = { path = "../nixide-sys", version = "0.1.0" } diff --git a/nixide/src/lib.rs b/nixide/src/lib.rs new file mode 100644 index 0000000..b93cf3f --- /dev/null +++ b/nixide/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: u64, right: u64) -> u64 { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} From 29132df3d142af90fc641ace786ec24a5c08d537 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Fri, 13 Mar 2026 23:51:56 +1000 Subject: [PATCH 281/306] progress lockfile --- Cargo.lock | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index dfba9b0..1cd2461 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -186,6 +186,13 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "nixide" +version = "0.1.0" +dependencies = [ + "nixide-sys", +] + [[package]] name = "nixide-sys" version = "0.1.0" From f20ae2d56fe1e85da5cd87711ba588197b9e5cc5 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Fri, 13 Mar 2026 23:52:05 +1000 Subject: [PATCH 282/306] shitty old comment --- nixide-sys/build.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/nixide-sys/build.rs b/nixide-sys/build.rs index 4419125..6631264 100644 --- a/nixide-sys/build.rs +++ b/nixide-sys/build.rs @@ -22,9 +22,6 @@ fn main() { // Invalidate the built crate whenever the wrapper changes println!("cargo:rerun-if-changed=include/wrapper.h"); - // Tell cargo to tell rustc to link the system shared library - // println!("cargo:rustc-link-lib=bz2"); - // Use pkg-config to find nix-store include and link paths // This NEEDS to be included, or otherwise `nix_api_store.h` cannot // be found. From ae6251c3a1b48dad3462772664d2f2bfb74a7e4c Mon Sep 17 00:00:00 2001 From: _cry64 Date: Sat, 14 Mar 2026 02:39:27 +1000 Subject: [PATCH 283/306] silly fox Date: Wed, 18 Mar 2026 16:57:55 +1000 Subject: [PATCH 284/306] add expr and store support --- Cargo.lock | 1 + nixide/Cargo.toml | 5 +- nixide/src/context.rs | 71 ++++++ nixide/src/error.rs | 183 ++++++++++++++++ nixide/src/expr/evalstate.rs | 117 ++++++++++ nixide/src/expr/evalstatebuilder.rs | 82 +++++++ nixide/src/expr/mod.rs | 12 ++ nixide/src/expr/tests.rs | 153 +++++++++++++ nixide/src/expr/value.rs | 322 ++++++++++++++++++++++++++++ nixide/src/expr/valuetype.rs | 68 ++++++ nixide/src/lib.rs | 24 +-- nixide/src/store/mod.rs | 275 ++++++++++++++++++++++++ nixide/src/store/path.rs | 156 ++++++++++++++ nixide/src/store/tests.rs | 66 ++++++ nixide/src/util/bindings.rs | 34 +++ nixide/src/util/mod.rs | 1 + 16 files changed, 1557 insertions(+), 13 deletions(-) create mode 100644 nixide/src/context.rs create mode 100644 nixide/src/error.rs create mode 100644 nixide/src/expr/evalstate.rs create mode 100644 nixide/src/expr/evalstatebuilder.rs create mode 100644 nixide/src/expr/mod.rs create mode 100644 nixide/src/expr/tests.rs create mode 100644 nixide/src/expr/value.rs create mode 100644 nixide/src/expr/valuetype.rs create mode 100644 nixide/src/store/mod.rs create mode 100644 nixide/src/store/path.rs create mode 100644 nixide/src/store/tests.rs create mode 100644 nixide/src/util/bindings.rs create mode 100644 nixide/src/util/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 1cd2461..5346be7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -191,6 +191,7 @@ name = "nixide" version = "0.1.0" dependencies = [ "nixide-sys", + "serial_test", ] [[package]] diff --git a/nixide/Cargo.toml b/nixide/Cargo.toml index 4c640de..fc0fa79 100644 --- a/nixide/Cargo.toml +++ b/nixide/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0" repository = "https://codeberg.org/luminary/nixide" authors = [ "_cry64 ", - "foxxyora " + "foxxyora ", ] edition = "2024" @@ -25,3 +25,6 @@ gc = [] [dependencies] nixide-sys = { path = "../nixide-sys", version = "0.1.0" } + +[dev-dependencies] +serial_test = "3.4.0" diff --git a/nixide/src/context.rs b/nixide/src/context.rs new file mode 100644 index 0000000..189a246 --- /dev/null +++ b/nixide/src/context.rs @@ -0,0 +1,71 @@ +use std::ptr::NonNull; + +use crate::error::NixError; +use nixide_sys as sys; + +/// Nix context for managing library state. +/// +/// This is the root object for all Nix operations. It manages the lifetime +/// of the Nix C API context and provides automatic cleanup. +pub struct Context { + inner: NonNull, +} + +impl Context { + /// Create a new Nix context. + /// + /// This initializes the Nix C API context and the required libraries. + /// + /// # Errors + /// + /// Returns an error if context creation or library initialization fails. + pub fn new() -> Result { + // SAFETY: nix_c_context_create is safe to call + let ctx_ptr = unsafe { sys::nix_c_context_create() }; + let ctx = Context { + inner: NonNull::new(ctx_ptr).ok_or(NixError::NullPtr { + location: "nix_c_context_create", + })?, + }; + + // Initialize required libraries + unsafe { + NixError::from( + sys::nix_libutil_init(ctx.inner.as_ptr()), + "nix_libutil_init", + )?; + NixError::from( + sys::nix_libstore_init(ctx.inner.as_ptr()), + "nix_libstore_init", + )?; + NixError::from( + sys::nix_libexpr_init(ctx.inner.as_ptr()), + "nix_libexpr_init", + )?; + }; + + Ok(ctx) + } + + /// Get the raw context pointer. + /// + /// # Safety + /// + /// The caller must ensure the pointer is used safely. + pub unsafe fn as_ptr(&self) -> *mut sys::nix_c_context { + self.inner.as_ptr() + } +} + +impl Drop for Context { + fn drop(&mut self) { + // SAFETY: We own the context and it's valid until drop + unsafe { + sys::nix_c_context_free(self.inner.as_ptr()); + } + } +} + +// SAFETY: Context can be shared between threads +unsafe impl Send for Context {} +unsafe impl Sync for Context {} diff --git a/nixide/src/error.rs b/nixide/src/error.rs new file mode 100644 index 0000000..0e2de14 --- /dev/null +++ b/nixide/src/error.rs @@ -0,0 +1,183 @@ +use std::ffi::NulError; +use std::fmt::{Display, Formatter, Result as FmtResult}; +use std::ptr::NonNull; + +use crate::sys; + +/// Standard (nix_err) and some additional error codes +/// produced by the libnix C API. +#[derive(Debug)] +pub enum NixError { + /// A generic Nix error occurred. + /// + /// # Reason + /// + /// This error code is returned when a generic Nix error occurred during the + /// function execution. + NixError { location: &'static str }, + + /// An overflow error occurred. + /// + /// # Reason + /// + /// This error code is returned when an overflow error occurred during the + /// function execution. + Overflow { location: &'static str }, + + /// A key/index access error occurred in C API functions. + /// + /// # Reason + /// + /// This error code is returned when accessing a key, index, or identifier that + /// does not exist in C API functions. Common scenarios include: + /// - Setting keys that don't exist (nix_setting_get, nix_setting_set) + /// - List indices that are out of bounds (nix_get_list_byidx*) + /// - Attribute names that don't exist (nix_get_attr_byname*) + /// - Attribute indices that are out of bounds (nix_get_attr_byidx*, nix_get_attr_name_byidx) + /// + /// This error typically indicates incorrect usage or assumptions about data structure + /// contents, rather than internal Nix evaluation errors. + /// + /// # Note + /// + /// This error code should ONLY be returned by C API functions themselves, + /// not by underlying Nix evaluation. For example, evaluating `{}.foo` in Nix + /// will throw a normal error (NIX_ERR_NIX_ERROR), not NIX_ERR_KEY. + KeyNotFound { + location: &'static str, + key: Option, + }, + + /// An unknown error occurred. + /// + /// # Reason + /// + /// This error code is returned when an unknown error occurred during the + /// function execution. + Unknown { + location: &'static str, + reason: String, + }, + + /// An undocumented error occurred. + /// + /// # Reason + /// + /// The libnix C API defines `enum nix_err` as a signed integer value. + /// In the (unexpected) event libnix returns an error code with an + /// invalid enum value, or one I new addition I didn't know existed, + /// then an [NixError::Undocumented] is considered to have occurred. + Undocumented { + location: &'static str, + err_code: sys::nix_err, + }, + + ////////////////////// + // NON-STANDARD ERRORS + ////////////////////// + /// NulError + NulError { location: &'static str }, + + /// Non-standard + NullPtr { location: &'static str }, + + /// Invalid Argument + InvalidArg { + location: &'static str, + reason: &'static str, // XXX: TODO: make this a String + }, + + /// Invalid Type + InvalidType { + location: &'static str, + expected: &'static str, + got: String, + }, +} + +impl NixError { + pub fn from(err_code: sys::nix_err, location: &'static str) -> Result<(), NixError> { + #[allow(nonstandard_style)] + match err_code { + sys::nix_err_NIX_OK => Ok(()), + + sys::nix_err_NIX_ERR_OVERFLOW => Err(NixError::Overflow { location }), + sys::nix_err_NIX_ERR_KEY => Err(NixError::KeyNotFound { + location, + key: None, + }), + sys::nix_err_NIX_ERR_NIX_ERROR => Err(NixError::NixError { location }), + + sys::nix_err_NIX_ERR_UNKNOWN => Err(NixError::Unknown { + location, + reason: "Unknown error occurred".to_string(), + }), + _ => Err(NixError::Undocumented { location, err_code }), + } + } + + pub fn from_nulerror( + result: Result, + location: &'static str, + ) -> Result { + result.or(Err(NixError::NulError { location })) + } + + pub fn new_nonnull(ptr: *mut T, location: &'static str) -> Result, Self> + where + T: Sized, + { + NonNull::new(ptr).ok_or(NixError::NullPtr { location }) + } +} + +impl std::error::Error for NixError {} + +impl Display for NixError { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + let msg = match self { + NixError::NixError { location } => { + format!("[libnix] Generic error (at location `{location}`)") + } + NixError::Overflow { location } => { + format!("[libnix] Overflow error (at location `{location}`)") + } + NixError::KeyNotFound { location, key } => format!( + "[libnix] Key not found {} (at location `{location}`)", + match key { + Some(key) => format!("`{key}`"), + None => "".to_owned(), + } + ), + + NixError::Unknown { location, reason } => { + format!("Unknown error \"{reason}\" (at location `{location}`)") + } + NixError::Undocumented { location, err_code } => { + format!( + "[libnix] An undocumented nix_err was returned with {err_code} (at location `{location}`)" + ) + } + + NixError::NulError { location } => { + format!("Nul error (at location `{location}`)") + } + NixError::NullPtr { location } => { + format!("[libnix] Null pointer (at location `{location}`)") + } + + NixError::InvalidArg { location, reason } => { + format!("Invalid argument \"{reason}\" (at location `{location}`)") + } + NixError::InvalidType { + location, + expected, + got, + } => { + format!("Invalid type, expected \"{expected}\" ${got} (at location `{location}`)") + } + }; + + write!(f, "{msg}") + } +} diff --git a/nixide/src/expr/evalstate.rs b/nixide/src/expr/evalstate.rs new file mode 100644 index 0000000..b0f0220 --- /dev/null +++ b/nixide/src/expr/evalstate.rs @@ -0,0 +1,117 @@ +use std::ffi::CString; +use std::ptr::NonNull; +use std::sync::Arc; + +use super::Value; +use crate::sys; +use crate::{Context, NixError, Store}; + +/// Nix evaluation state for evaluating expressions. +/// +/// This provides the main interface for evaluating Nix expressions +/// and creating values. +pub struct EvalState { + inner: NonNull, + #[allow(dead_code)] + store: Arc, + pub(super) context: Arc, +} + +impl EvalState { + /// Construct a new EvalState directly from its attributes + pub(super) fn new( + inner: NonNull, + store: Arc, + context: Arc, + ) -> Self { + Self { + inner, + store, + context, + } + } + + /// Evaluate a Nix expression from a string. + /// + /// # Arguments + /// + /// * `expr` - The Nix expression to evaluate + /// * `path` - The path to use for error reporting (e.g., "") + /// + /// # Errors + /// + /// Returns an error if evaluation fails. + pub fn eval_from_string(&self, expr: &str, path: &str) -> Result, NixError> { + let expr_c = + NixError::from_nulerror(CString::new(expr), "nixide::EvalState::eval_from_string")?; + let path_c = + NixError::from_nulerror(CString::new(path), "nixide::EvalState::eval_from_string")?; + + // Allocate value for result + // SAFETY: context and state are valid + let value_ptr = unsafe { sys::nix_alloc_value(self.context.as_ptr(), self.inner.as_ptr()) }; + if value_ptr.is_null() { + return Err(NixError::NullPtr { + location: "nix_alloc_value", + }); + } + + // Evaluate expression + // SAFETY: all pointers are valid + NixError::from( + unsafe { + sys::nix_expr_eval_from_string( + self.context.as_ptr(), + self.inner.as_ptr(), + expr_c.as_ptr(), + path_c.as_ptr(), + value_ptr, + ) + }, + "nix_expr_eval_from_string", + )?; + + let inner = NonNull::new(value_ptr).ok_or(NixError::NullPtr { + location: "nix_expr_eval_from_string", + })?; + + Ok(Value { inner, state: self }) + } + + /// Allocate a new value. + /// + /// # Errors + /// + /// Returns an error if value allocation fails. + pub fn alloc_value(&self) -> Result, NixError> { + // SAFETY: context and state are valid + let value_ptr = unsafe { sys::nix_alloc_value(self.context.as_ptr(), self.inner.as_ptr()) }; + let inner = NonNull::new(value_ptr).ok_or(NixError::NullPtr { + location: "nix_alloc_value", + })?; + + Ok(Value { inner, state: self }) + } + + /// Get the raw state pointer. + /// + /// # Safety + /// + /// The caller must ensure the pointer is used safely. + pub(super) unsafe fn as_ptr(&self) -> *mut sys::EvalState { + self.inner.as_ptr() + } +} + +impl Drop for EvalState { + fn drop(&mut self) { + // SAFETY: We own the state and it's valid until drop + unsafe { + sys::nix_state_free(self.inner.as_ptr()); + } + } +} + +// SAFETY: EvalState can be shared between threads +unsafe impl Send for EvalState {} +unsafe impl Sync for EvalState {} diff --git a/nixide/src/expr/evalstatebuilder.rs b/nixide/src/expr/evalstatebuilder.rs new file mode 100644 index 0000000..6ca906c --- /dev/null +++ b/nixide/src/expr/evalstatebuilder.rs @@ -0,0 +1,82 @@ +use std::ptr::NonNull; +use std::sync::Arc; + +use super::EvalState; +use crate::sys; +use crate::{Context, NixError, Store}; + +/// Builder for Nix evaluation state. +/// +/// This allows configuring the evaluation environment before creating +/// the evaluation state. +pub struct EvalStateBuilder { + inner: NonNull, + store: Arc, + context: Arc, +} + +impl EvalStateBuilder { + /// Create a new evaluation state builder. + /// + /// # Arguments + /// + /// * `store` - The Nix store to use for evaluation + /// + /// # Errors + /// + /// Returns an error if the builder cannot be created. + pub fn new(store: &Arc) -> Result { + // SAFETY: store context and store are valid + let builder_ptr = + unsafe { sys::nix_eval_state_builder_new(store._context.as_ptr(), store.as_ptr()) }; + + let inner = NonNull::new(builder_ptr).ok_or(NixError::NullPtr { + location: "nix_eval_state_builder_new", + })?; + + Ok(EvalStateBuilder { + inner, + store: Arc::clone(store), + context: Arc::clone(&store._context), + }) + } + + /// Build the evaluation state. + /// + /// # Errors + /// + /// Returns an error if the evaluation state cannot be built. + pub fn build(self) -> Result { + // Load configuration first + // SAFETY: context and builder are valid + NixError::from( + unsafe { sys::nix_eval_state_builder_load(self.context.as_ptr(), self.inner.as_ptr()) }, + "nix_eval_state_builder_load", + )?; + + // Build the state + // SAFETY: context and builder are valid + let state_ptr = + unsafe { sys::nix_eval_state_build(self.context.as_ptr(), self.inner.as_ptr()) }; + + let inner = NonNull::new(state_ptr).ok_or(NixError::NullPtr { + location: "nix_eval_state_build", + })?; + + // The builder is consumed here - its Drop will clean up + Ok(EvalState::new( + inner, + self.store.clone(), + self.context.clone(), + )) + } +} + +impl Drop for EvalStateBuilder { + fn drop(&mut self) { + // SAFETY: We own the builder and it's valid until drop + unsafe { + sys::nix_eval_state_builder_free(self.inner.as_ptr()); + } + } +} diff --git a/nixide/src/expr/mod.rs b/nixide/src/expr/mod.rs new file mode 100644 index 0000000..ab4ae81 --- /dev/null +++ b/nixide/src/expr/mod.rs @@ -0,0 +1,12 @@ +#[cfg(test)] +mod tests; + +mod evalstate; +mod evalstatebuilder; +mod value; +mod valuetype; + +pub use evalstate::EvalState; +pub use evalstatebuilder::EvalStateBuilder; +pub use value::Value; +pub use valuetype::ValueType; diff --git a/nixide/src/expr/tests.rs b/nixide/src/expr/tests.rs new file mode 100644 index 0000000..688824d --- /dev/null +++ b/nixide/src/expr/tests.rs @@ -0,0 +1,153 @@ +use std::sync::Arc; + +use serial_test::serial; + +use super::{EvalStateBuilder, ValueType}; +use crate::{Context, Store}; + +#[test] +#[serial] +fn test_context_creation() { + let _ctx = Context::new().expect("Failed to create context"); + // Context should be dropped automatically +} + +#[test] +#[serial] +fn test_eval_state_builder() { + let ctx = Arc::new(Context::new().expect("Failed to create context")); + let store = Arc::new(Store::open(&ctx, None).expect("Failed to open store")); + let _state = EvalStateBuilder::new(&store) + .expect("Failed to create builder") + .build() + .expect("Failed to build state"); + // State should be dropped automatically +} + +#[test] +#[serial] +fn test_simple_evaluation() { + let ctx = Arc::new(Context::new().expect("Failed to create context")); + let store = Arc::new(Store::open(&ctx, None).expect("Failed to open store")); + let state = EvalStateBuilder::new(&store) + .expect("Failed to create builder") + .build() + .expect("Failed to build state"); + + let result = state + .eval_from_string("1 + 2", "") + .expect("Failed to evaluate expression"); + + assert_eq!(result.value_type(), ValueType::Int); + assert_eq!(result.as_int().expect("Failed to get int value"), 3); +} + +#[test] +#[serial] +fn test_value_types() { + let ctx = Arc::new(Context::new().expect("Failed to create context")); + let store = Arc::new(Store::open(&ctx, None).expect("Failed to open store")); + let state = EvalStateBuilder::new(&store) + .expect("Failed to create builder") + .build() + .expect("Failed to build state"); + + // Test integer + let int_val = state + .eval_from_string("42", "") + .expect("Failed to evaluate int"); + assert_eq!(int_val.value_type(), ValueType::Int); + assert_eq!(int_val.as_int().expect("Failed to get int"), 42); + + // Test boolean + let bool_val = state + .eval_from_string("true", "") + .expect("Failed to evaluate bool"); + assert_eq!(bool_val.value_type(), ValueType::Bool); + assert!(bool_val.as_bool().expect("Failed to get bool")); + + // Test string + let str_val = state + .eval_from_string("\"hello\"", "") + .expect("Failed to evaluate string"); + assert_eq!(str_val.value_type(), ValueType::String); + assert_eq!(str_val.as_string().expect("Failed to get string"), "hello"); +} + +#[test] +#[serial] +fn test_value_formatting() { + let ctx = Arc::new(Context::new().expect("Failed to create context")); + let store = Arc::new(Store::open(&ctx, None).expect("Failed to open store")); + let state = EvalStateBuilder::new(&store) + .expect("Failed to create builder") + .build() + .expect("Failed to build state"); + + // Test integer formatting + let int_val = state + .eval_from_string("42", "") + .expect("Failed to evaluate int"); + assert_eq!(format!("{int_val}"), "42"); + assert_eq!(format!("{int_val:?}"), "Value::Int(42)"); + assert_eq!(int_val.to_nix_string().expect("Failed to format"), "42"); + + // Test boolean formatting + let bool_val = state + .eval_from_string("true", "") + .expect("Failed to evaluate bool"); + assert_eq!(format!("{bool_val}"), "true"); + assert_eq!(format!("{bool_val:?}"), "Value::Bool(true)"); + assert_eq!(bool_val.to_nix_string().expect("Failed to format"), "true"); + + let false_val = state + .eval_from_string("false", "") + .expect("Failed to evaluate bool"); + assert_eq!(format!("{false_val}"), "false"); + assert_eq!( + false_val.to_nix_string().expect("Failed to format"), + "false" + ); + + // Test string formatting + let str_val = state + .eval_from_string("\"hello world\"", "") + .expect("Failed to evaluate string"); + assert_eq!(format!("{str_val}"), "hello world"); + assert_eq!(format!("{str_val:?}"), "Value::String(\"hello world\")"); + assert_eq!( + str_val.to_nix_string().expect("Failed to format"), + "\"hello world\"" + ); + + // Test string with quotes + let quoted_str = state + .eval_from_string("\"say \\\"hello\\\"\"", "") + .expect("Failed to evaluate quoted string"); + assert_eq!(format!("{quoted_str}"), "say \"hello\""); + assert_eq!( + quoted_str.to_nix_string().expect("Failed to format"), + "\"say \\\"hello\\\"\"" + ); + + // Test null formatting + let null_val = state + .eval_from_string("null", "") + .expect("Failed to evaluate null"); + assert_eq!(format!("{null_val}"), "null"); + assert_eq!(format!("{null_val:?}"), "Value::Null"); + assert_eq!(null_val.to_nix_string().expect("Failed to format"), "null"); + + // Test collection formatting + let attrs_val = state + .eval_from_string("{ a = 1; }", "") + .expect("Failed to evaluate attrs"); + assert_eq!(format!("{attrs_val}"), "{ }"); + assert_eq!(format!("{attrs_val:?}"), "Value::Attrs({ })"); + + let list_val = state + .eval_from_string("[ 1 2 3 ]", "") + .expect("Failed to evaluate list"); + assert_eq!(format!("{list_val}"), "[ ]"); + assert_eq!(format!("{list_val:?}"), "Value::List([ ])"); +} diff --git a/nixide/src/expr/value.rs b/nixide/src/expr/value.rs new file mode 100644 index 0000000..fce1b1b --- /dev/null +++ b/nixide/src/expr/value.rs @@ -0,0 +1,322 @@ +use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; +use std::ptr::NonNull; + +use super::{EvalState, ValueType}; +use crate::sys; +use crate::NixError; + +/// A Nix value +/// +/// This represents any value in the Nix language, including primitives, +/// collections, and functions. +pub struct Value<'a> { + pub(crate) inner: NonNull, + pub(crate) state: &'a EvalState, +} + +impl Value<'_> { + /// Force evaluation of this value. + /// + /// If the value is a thunk, this will evaluate it to its final form. + /// + /// # Errors + /// + /// Returns an error if evaluation fails. + pub fn force(&mut self) -> Result<(), NixError> { + NixError::from( + // SAFETY: context, state, and value are valid + unsafe { + sys::nix_value_force( + self.state.context.as_ptr(), + self.state.as_ptr(), + self.inner.as_ptr(), + ) + }, + "nix_value_force", + ) + } + + /// Force deep evaluation of this value. + /// + /// This forces evaluation of the value and all its nested components. + /// + /// # Errors + /// + /// Returns an error if evaluation fails. + pub fn force_deep(&mut self) -> Result<(), NixError> { + NixError::from( + // SAFETY: context, state, and value are valid + unsafe { + sys::nix_value_force_deep( + self.state.context.as_ptr(), + self.state.as_ptr(), + self.inner.as_ptr(), + ) + }, + "nix_value_force_deep", + ) + } + + /// Get the type of this value. + #[must_use] + pub fn value_type(&self) -> ValueType { + // SAFETY: context and value are valid + let c_type = unsafe { sys::nix_get_type(self.state.context.as_ptr(), self.inner.as_ptr()) }; + ValueType::from_c(c_type) + } + + /// Convert this value to an integer. + /// + /// # Errors + /// + /// Returns an error if the value is not an integer. + pub fn as_int(&self) -> Result { + if self.value_type() != ValueType::Int { + return Err(NixError::InvalidType { + location: "nixide::Value::as_int", + expected: "int", + got: self.value_type().to_string(), + }); + } + + // SAFETY: context and value are valid, type is checked + let result = unsafe { sys::nix_get_int(self.state.context.as_ptr(), self.inner.as_ptr()) }; + + Ok(result) + } + + /// Convert this value to a float. + /// + /// # Errors + /// + /// Returns an error if the value is not a float. + pub fn as_float(&self) -> Result { + if self.value_type() != ValueType::Float { + return Err(NixError::InvalidType { + location: "nixide::Value::as_float", + expected: "float", + got: self.value_type().to_string(), + }); + } + + // SAFETY: context and value are valid, type is checked + let result = + unsafe { sys::nix_get_float(self.state.context.as_ptr(), self.inner.as_ptr()) }; + + Ok(result) + } + + /// Convert this value to a boolean. + /// + /// # Errors + /// + /// Returns an error if the value is not a boolean. + pub fn as_bool(&self) -> Result { + if self.value_type() != ValueType::Bool { + return Err(NixError::InvalidType { + location: "nixide::Value::as_bool", + expected: "bool", + got: self.value_type().to_string(), + }); + } + + // SAFETY: context and value are valid, type is checked + let result = unsafe { sys::nix_get_bool(self.state.context.as_ptr(), self.inner.as_ptr()) }; + + Ok(result) + } + + /// Convert this value to a string. + /// + /// # Errors + /// + /// Returns an error if the value is not a string. + pub fn as_string(&self) -> Result { + if self.value_type() != ValueType::String { + return Err(NixError::InvalidType { + location: "nixide::Value::as_string", + expected: "string", + got: self.value_type().to_string(), + }); + } + + // For string values, we need to use realised string API + // SAFETY: context and value are valid, type is checked + let realised_str = unsafe { + sys::nix_string_realise( + self.state.context.as_ptr(), + self.state.as_ptr(), + self.inner.as_ptr(), + false, // don't copy more + ) + }; + + if realised_str.is_null() { + return Err(NixError::NullPtr { + location: "nix_string_realise", + }); + } + + // SAFETY: realised_str is non-null and points to valid RealizedString + let buffer_start = unsafe { sys::nix_realised_string_get_buffer_start(realised_str) }; + let buffer_size = unsafe { sys::nix_realised_string_get_buffer_size(realised_str) }; + if buffer_start.is_null() { + // Clean up realised string + unsafe { + sys::nix_realised_string_free(realised_str); + } + return Err(NixError::NullPtr { + location: "nix_realised_string_free", + }); + } + + // SAFETY: buffer_start is non-null and buffer_size gives us the length + let bytes = unsafe { std::slice::from_raw_parts(buffer_start.cast::(), buffer_size) }; + let string = std::str::from_utf8(bytes) + .map_err(|_| NixError::Unknown { + location: "nixide::Value::as_string", + reason: "Invalid UTF-8 in string".to_string(), + })? + .to_owned(); + + // Clean up realised string + unsafe { + sys::nix_realised_string_free(realised_str); + } + + Ok(string) + } + + /// Get the raw value pointer. + /// + /// # Safety + /// + /// The caller must ensure the pointer is used safely. + #[allow(dead_code)] + unsafe fn as_ptr(&self) -> *mut sys::nix_value { + self.inner.as_ptr() + } + + /// Format this value as Nix syntax. + /// + /// This provides a string representation that matches Nix's own syntax, + /// making it useful for debugging and displaying values to users. + /// + /// # Errors + /// + /// Returns an error if the value cannot be converted to a string + /// representation. + pub fn to_nix_string(&self) -> Result { + match self.value_type() { + ValueType::Int => Ok(self.as_int()?.to_string()), + ValueType::Float => Ok(self.as_float()?.to_string()), + ValueType::Bool => Ok(if self.as_bool()? { + "true".to_string() + } else { + "false".to_string() + }), + ValueType::String => Ok(format!("\"{}\"", self.as_string()?.replace('"', "\\\""))), + ValueType::Null => Ok("null".to_string()), + ValueType::Attrs => Ok("{ }".to_string()), + ValueType::List => Ok("[ ]".to_string()), + ValueType::Function => Ok("".to_string()), + ValueType::Path => Ok("".to_string()), + ValueType::Thunk => Ok("".to_string()), + ValueType::External => Ok("".to_string()), + } + } +} + +impl Drop for Value<'_> { + fn drop(&mut self) { + // SAFETY: We own the value and it's valid until drop + unsafe { + sys::nix_value_decref(self.state.context.as_ptr(), self.inner.as_ptr()); + } + } +} + +impl Display for Value<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + match self.value_type() { + ValueType::Int => { + if let Ok(val) = self.as_int() { + write!(f, "{val}") + } else { + write!(f, "") + } + } + ValueType::Float => { + if let Ok(val) = self.as_float() { + write!(f, "{val}") + } else { + write!(f, "") + } + } + ValueType::Bool => { + if let Ok(val) = self.as_bool() { + write!(f, "{val}") + } else { + write!(f, "") + } + } + ValueType::String => { + if let Ok(val) = self.as_string() { + write!(f, "{val}") + } else { + write!(f, "") + } + } + ValueType::Null => write!(f, "null"), + ValueType::Attrs => write!(f, "{{ }}"), + ValueType::List => write!(f, "[ ]"), + ValueType::Function => write!(f, ""), + ValueType::Path => write!(f, ""), + ValueType::Thunk => write!(f, ""), + ValueType::External => write!(f, ""), + } + } +} + +impl Debug for Value<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + let value_type = self.value_type(); + match value_type { + ValueType::Int => { + if let Ok(val) = self.as_int() { + write!(f, "Value::Int({val})") + } else { + write!(f, "Value::Int()") + } + } + ValueType::Float => { + if let Ok(val) = self.as_float() { + write!(f, "Value::Float({val})") + } else { + write!(f, "Value::Float()") + } + } + ValueType::Bool => { + if let Ok(val) = self.as_bool() { + write!(f, "Value::Bool({val})") + } else { + write!(f, "Value::Bool()") + } + } + ValueType::String => { + if let Ok(val) = self.as_string() { + write!(f, "Value::String({val:?})") + } else { + write!(f, "Value::String()") + } + } + ValueType::Null => write!(f, "Value::Null"), + ValueType::Attrs => write!(f, "Value::Attrs({{ }})"), + ValueType::List => write!(f, "Value::List([ ])"), + ValueType::Function => write!(f, "Value::Function()"), + ValueType::Path => write!(f, "Value::Path()"), + ValueType::Thunk => write!(f, "Value::Thunk()"), + ValueType::External => write!(f, "Value::External()"), + } + } +} diff --git a/nixide/src/expr/valuetype.rs b/nixide/src/expr/valuetype.rs new file mode 100644 index 0000000..408bd65 --- /dev/null +++ b/nixide/src/expr/valuetype.rs @@ -0,0 +1,68 @@ +use std::fmt::{Display, Formatter, Result as FmtResult}; + +use crate::sys; + +/// Nix value types. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ValueType { + /// Thunk (unevaluated expression). + Thunk, + /// Integer value. + Int, + /// Float value. + Float, + /// Boolean value. + Bool, + /// String value. + String, + /// Path value. + Path, + /// Null value. + Null, + /// Attribute set. + Attrs, + /// List. + List, + /// Function. + Function, + /// External value. + External, +} + +impl ValueType { + pub(super) fn from_c(value_type: sys::ValueType) -> Self { + match value_type { + sys::ValueType_NIX_TYPE_THUNK => ValueType::Thunk, + sys::ValueType_NIX_TYPE_INT => ValueType::Int, + sys::ValueType_NIX_TYPE_FLOAT => ValueType::Float, + sys::ValueType_NIX_TYPE_BOOL => ValueType::Bool, + sys::ValueType_NIX_TYPE_STRING => ValueType::String, + sys::ValueType_NIX_TYPE_PATH => ValueType::Path, + sys::ValueType_NIX_TYPE_NULL => ValueType::Null, + sys::ValueType_NIX_TYPE_ATTRS => ValueType::Attrs, + sys::ValueType_NIX_TYPE_LIST => ValueType::List, + sys::ValueType_NIX_TYPE_FUNCTION => ValueType::Function, + sys::ValueType_NIX_TYPE_EXTERNAL => ValueType::External, + _ => ValueType::Thunk, // fallback (TODO: is this ok?) + } + } +} + +impl Display for ValueType { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + let name = match self { + ValueType::Thunk => "thunk", + ValueType::Int => "int", + ValueType::Float => "float", + ValueType::Bool => "bool", + ValueType::String => "string", + ValueType::Path => "path", + ValueType::Null => "null", + ValueType::Attrs => "attrs", + ValueType::List => "list", + ValueType::Function => "function", + ValueType::External => "external", + }; + write!(f, "{name}") + } +} diff --git a/nixide/src/lib.rs b/nixide/src/lib.rs index b93cf3f..8dba35d 100644 --- a/nixide/src/lib.rs +++ b/nixide/src/lib.rs @@ -1,14 +1,14 @@ -pub fn add(left: u64, right: u64) -> u64 { - left + right -} +// #![warn(missing_docs)] -#[cfg(test)] -mod tests { - use super::*; +mod context; +mod error; +mod expr; +mod store; +mod util; - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } -} +pub use context::Context; +pub use error::NixError; +pub use expr::{EvalState, EvalStateBuilder, Value, ValueType}; +pub use store::{Store, StorePath}; + +pub use nixide_sys as sys; diff --git a/nixide/src/store/mod.rs b/nixide/src/store/mod.rs new file mode 100644 index 0000000..668f6b9 --- /dev/null +++ b/nixide/src/store/mod.rs @@ -0,0 +1,275 @@ +// XXX: TODO: should I add support for `nix_libstore_init_no_load_config` +// XXX: TODO: add support for nix_realised_string_* family of functions +// nix_realised_string_get_store_path +// nix_realised_string_get_store_path_count +// # nix_store_real_path +// # nix_store_is_valid_path +// # nix_store_get_version +// # nix_store_get_uri +// # nix_store_get_storedir +// # nix_store_copy_closure +// nix_libstore_init_no_load_config + +#[cfg(test)] +mod tests; + +mod path; +pub use path::*; + +use std::ffi::{CStr, CString, NulError}; +use std::os::raw::{c_char, c_void}; +use std::path::PathBuf; +use std::ptr::NonNull; +use std::result::Result; +use std::sync::Arc; + +use super::{Context, NixError}; +use crate::util::bindings::{wrap_libnix_pathbuf_callback, wrap_libnix_string_callback}; +use nixide_sys as sys; + +/// Nix store for managing packages and derivations. +/// +/// The store provides access to Nix packages, derivations, and store paths. +pub struct Store { + pub(crate) inner: NonNull, + pub(crate) _context: Arc, +} + +impl Store { + /// Open a Nix store. + /// + /// # Arguments + /// + /// * `context` - The Nix context + /// * `uri` - Optional store URI (None for default store) + /// + /// # Errors + /// + /// Returns an error if the store cannot be opened. + pub fn open(context: &Arc, uri: Option<&str>) -> Result { + let uri_cstring: CString; + let uri_ptr = if let Some(uri) = uri { + uri_cstring = NixError::from_nulerror(CString::new(uri), "nixide::Store::open")?; + uri_cstring.as_ptr() + } else { + std::ptr::null() + }; + + // SAFETY: context is valid, uri_ptr is either null or valid CString + let store_ptr = + unsafe { sys::nix_store_open(context.as_ptr(), uri_ptr, std::ptr::null_mut()) }; + + let inner = NonNull::new(store_ptr).ok_or(NixError::NullPtr { + location: "nix_store_open", + })?; + + Ok(Store { + inner, + _context: Arc::clone(context), + }) + } + + /// Get the raw store pointer. + /// + /// # Safety + /// + /// The caller must ensure the pointer is used safely. + pub(crate) unsafe fn as_ptr(&self) -> *mut sys::Store { + self.inner.as_ptr() + } + + /// Realize a store path. + /// + /// This builds/downloads the store path and all its dependencies, + /// making them available in the local store. + /// + /// # Arguments + /// + /// * `path` - The store path to realize + /// + /// # Returns + /// + /// A vector of (output_name, store_path) tuples for each realized output. + /// For example, a derivation might produce outputs like ("out", path1), ("dev", path2). + /// + /// # Errors + /// + /// Returns an error if the path cannot be realized. + pub fn realise( + &self, + path: &StorePath, + callback: fn(&str, &StorePath), + ) -> Result, NixError> { + // Type alias for our userdata: (outputs vector, context) + type Userdata = (Vec<(String, StorePath)>, Arc, fn(&str, &StorePath)); + + // Callback function that will be called for each realized output + unsafe extern "C" fn realise_callback( + userdata: *mut c_void, + out_name_ptr: *const c_char, + out_path_ptr: *const sys::StorePath, + ) { + // SAFETY: userdata is a valid pointer to our (Vec, Arc) tuple + let (outputs, context, callback) = unsafe { &mut *(userdata as *mut Userdata) }; + + // SAFETY: outname is a valid C string from Nix + let output_name = if !out_name_ptr.is_null() { + unsafe { CStr::from_ptr(out_name_ptr).to_string_lossy().into_owned() } + } else { + String::from("out") // Default output name + }; + + // SAFETY: out is a valid StorePath pointer from Nix, we need to clone it + // because Nix owns the original and may free it after the callback + if !out_path_ptr.is_null() { + let cloned_path_ptr = + unsafe { sys::nix_store_path_clone(out_path_ptr as *mut sys::StorePath) }; + if let Some(inner) = NonNull::new(cloned_path_ptr) { + let store_path = StorePath { + inner, + _context: Arc::clone(context), + }; + + callback(output_name.as_ref(), &store_path); + + outputs.push((output_name, store_path)); + } + } + } + + // Create userdata with empty outputs vector and context + let mut userdata: Userdata = (Vec::new(), Arc::clone(&self._context), callback); + let userdata_ptr = &mut userdata as *mut Userdata as *mut std::os::raw::c_void; + + // SAFETY: All pointers are valid, callback is compatible with the FFI signature + // - self._context is valid for the duration of this call + // - self.inner is valid (checked in Store::open) + // - path.inner is valid (checked in StorePath::parse) + // - userdata_ptr points to valid stack memory + // - realize_callback matches the expected C function signature + let err = unsafe { + sys::nix_store_realise( + self._context.as_ptr(), + self.inner.as_ptr(), + path.as_ptr(), + userdata_ptr, + Some(realise_callback), + ) + }; + + NixError::from(err, "nix_store_realise")?; + + // Return the collected outputs + Ok(userdata.0) + } + + /// Parse a store path string into a StorePath. + /// + /// This is a convenience method that wraps `StorePath::parse()`. + /// + /// # Arguments + /// + /// * `path` - The store path string (e.g., "/nix/store/...") + /// + /// # Errors + /// + /// Returns an error if the path cannot be parsed. + /// + /// # Example + /// + /// ```no_run + /// # use std::sync::Arc; + /// # use nixide::{Context, Store}; + /// # fn main() -> Result<(), Box> { + /// let ctx = Arc::new(Context::new()?); + /// let store = Store::open(&ctx, None)?; + /// let path = store.store_path("/nix/store/...")?; + /// # Ok(()) + /// # } + /// ``` + pub fn store_path(&self, path: &str) -> Result { + StorePath::parse(&self._context, self, path) + } + + /// Get the version of a Nix store + /// + /// If the store doesn't have a version (like the dummy store), returns None + pub fn version(&self) -> Result { + wrap_libnix_string_callback("nix_store_get_version", |callback, user_data| unsafe { + sys::nix_store_get_version( + self._context.as_ptr(), + self.inner.as_ptr(), + Some(callback), + user_data, + ) + }) + } + + /// Get the URI of a Nix store + pub fn uri(&self) -> Result { + wrap_libnix_string_callback("nix_store_get_uri", |callback, user_data| unsafe { + sys::nix_store_get_uri( + self._context.as_ptr(), + self.inner.as_ptr(), + Some(callback), + user_data, + ) + }) + } + + pub fn store_dir(&self) -> Result { + wrap_libnix_pathbuf_callback("nix_store_get_storedir", |callback, user_data| unsafe { + sys::nix_store_get_storedir( + self._context.as_ptr(), + self.inner.as_ptr(), + Some(callback), + user_data, + ) + }) + } + + pub fn copy_closure_to( + &self, + dst_store: &Store, + store_path: &StorePath, + ) -> Result<(), NixError> { + let err = unsafe { + sys::nix_store_copy_closure( + self._context.as_ptr(), + self.inner.as_ptr(), + dst_store.inner.as_ptr(), + store_path.inner.as_ptr(), + ) + }; + NixError::from(err, "nix_store_copy_closure") + } + + pub fn copy_closure_from( + &self, + src_store: &Store, + store_path: &StorePath, + ) -> Result<(), NixError> { + let err = unsafe { + sys::nix_store_copy_closure( + self._context.as_ptr(), + src_store.inner.as_ptr(), + self.inner.as_ptr(), + store_path.inner.as_ptr(), + ) + }; + NixError::from(err, "nix_store_copy_closure") + } +} + +impl Drop for Store { + fn drop(&mut self) { + // SAFETY: We own the store and it's valid until drop + unsafe { + sys::nix_store_free(self.inner.as_ptr()); + } + } +} + +// SAFETY: Store can be shared between threads +unsafe impl Send for Store {} +unsafe impl Sync for Store {} diff --git a/nixide/src/store/path.rs b/nixide/src/store/path.rs new file mode 100644 index 0000000..8b8b6e4 --- /dev/null +++ b/nixide/src/store/path.rs @@ -0,0 +1,156 @@ +use std::ffi::CString; +use std::path::PathBuf; +use std::ptr::NonNull; +use std::sync::Arc; + +use super::Store; +use crate::util::bindings::{wrap_libnix_pathbuf_callback, wrap_libnix_string_callback}; +use crate::{Context, NixError}; +use nixide_sys::{self as sys, nix_err_NIX_OK}; + +/// A path in the Nix store. +/// +/// Represents a store path that can be realized, queried, or manipulated. +pub struct StorePath { + pub(crate) inner: NonNull, + pub(crate) _context: Arc, +} + +impl StorePath { + /// Parse a store path string into a StorePath. + /// + /// # Arguments + /// + /// * `context` - The Nix context + /// * `store` - The store containing the path + /// * `path` - The store path string (e.g., "/nix/store/...") + /// + /// # Errors + /// + /// Returns an error if the path cannot be parsed. + pub fn parse(context: &Arc, store: &Store, path: &str) -> Result { + let path_cstring = CString::new(path).or(Err(NixError::InvalidArg { + location: "nixide::StorePath::parse", + reason: "`path` contains NUL char", + }))?; + + // SAFETY: context, store, and path_cstring are valid + let path_ptr = unsafe { + sys::nix_store_parse_path(context.as_ptr(), store.as_ptr(), path_cstring.as_ptr()) + }; + + let inner = NonNull::new(path_ptr).ok_or(NixError::NullPtr { + location: "nix_store_parse_path", + })?; + + Ok(Self { + inner, + _context: Arc::clone(context), + }) + } + + /// Get the name component of the store path. + /// + /// This returns the name part of the store path (everything after the hash). + /// For example, for "/nix/store/abc123...-hello-1.0", this returns "hello-1.0". + /// + /// # Errors + /// + /// Returns an error if the name cannot be retrieved. + pub fn name(&self) -> Result { + wrap_libnix_string_callback("nix_store_path_name", |callback, user_data| unsafe { + sys::nix_store_path_name(self.inner.as_ptr(), Some(callback), user_data); + + // NOTE: nix_store_path_name doesn't return nix_err, so we force it to return successfully + nix_err_NIX_OK + }) + } + + /// Get the physical location of a store path + /// + /// A store may reside at a different location than its `storeDir` suggests. + /// This situation is called a relocated store. + /// + /// Relocated stores are used during NixOS installation, as well as in restricted + /// computing environments that don't offer a writable `/nix/store`. + /// + /// Not all types of stores support this operation. + /// + /// # Arguments + /// * `context` [in] - Optional, stores error information + /// * `store` [in] - nix store reference + /// * `path` [in] - the path to get the real path from + /// * `callback` [in] - called with the real path + /// * `user_data` [in] - arbitrary data, passed to the callback when it's called. + /// + /// # Arguments + /// + /// * `store` - The store containing the path + /// + pub fn real_path(&self, store: &Store) -> Result { + wrap_libnix_pathbuf_callback("nix_store_real_path", |callback, user_data| unsafe { + sys::nix_store_real_path( + self._context.as_ptr(), + store.inner.as_ptr(), + self.inner.as_ptr(), + Some(callback), + user_data, + ) + }) + } + + /// Check if a [StorePath] is valid (i.e. that its corresponding store object + /// and its closure of references exists in the store). + /// + /// # Arguments + /// + /// * `store` - The store containing the path + /// + pub fn is_valid(&self, store: &Store) -> bool { + unsafe { + sys::nix_store_is_valid_path( + self._context.as_ptr(), + store.inner.as_ptr(), + self.inner.as_ptr(), + ) + } + } + + /// Get the raw store path pointer. + /// + /// # Safety + /// + /// The caller must ensure the pointer is used safely. + pub(crate) unsafe fn as_ptr(&self) -> *mut sys::StorePath { + self.inner.as_ptr() + } +} + +impl Clone for StorePath { + fn clone(&self) -> Self { + // SAFETY: self.inner is valid, nix_store_path_clone creates a new copy + let cloned_ptr = unsafe { sys::nix_store_path_clone(self.inner.as_ptr()) }; + + // This should never fail as cloning a valid path should always succeed + let inner = + NonNull::new(cloned_ptr).expect("nix_store_path_clone returned null for valid path"); + + StorePath { + inner, + _context: Arc::clone(&self._context), + } + } +} + +impl Drop for StorePath { + fn drop(&mut self) { + // SAFETY: We own the store path and it's valid until drop + unsafe { + sys::nix_store_path_free(self.inner.as_ptr()); + } + } +} + +// SAFETY: StorePath can be shared between threads +unsafe impl Send for StorePath {} +unsafe impl Sync for StorePath {} diff --git a/nixide/src/store/tests.rs b/nixide/src/store/tests.rs new file mode 100644 index 0000000..e00c3c9 --- /dev/null +++ b/nixide/src/store/tests.rs @@ -0,0 +1,66 @@ +use serial_test::serial; + +use super::*; + +#[test] +#[serial] +fn test_store_opening() { + let ctx = Arc::new(Context::new().expect("Failed to create context")); + let _store = Store::open(&ctx, None).expect("Failed to open store"); +} + +#[test] +#[serial] +fn test_store_path_parse() { + let ctx = Arc::new(Context::new().expect("Failed to create context")); + let store = Store::open(&ctx, None).expect("Failed to open store"); + + // Try parsing a well-formed store path + // Note: This may fail if the path doesn't exist in the store + let result = StorePath::parse( + &ctx, + &store, + "/nix/store/00000000000000000000000000000000-test", + ); + + // We don't assert success here because the path might not exist + // This test mainly checks that the API works correctly + match result { + Ok(_path) => { + // Successfully parsed the path + } + Err(_) => { + // Path doesn't exist or is invalid, which is expected + } + } +} + +#[test] +#[serial] +fn test_store_path_clone() { + let ctx = Arc::new(Context::new().expect("Failed to create context")); + let store = Store::open(&ctx, None).expect("Failed to open store"); + + // Try to get a valid store path by parsing + // Note: This test is somewhat limited without a guaranteed valid path + if let Ok(path) = StorePath::parse( + &ctx, + &store, + "/nix/store/00000000000000000000000000000000-test", + ) { + let cloned = path.clone(); + + // Assert that the cloned path has the same name as the original + let original_name = path.name().expect("Failed to get original path name"); + let cloned_name = cloned.name().expect("Failed to get cloned path name"); + + assert_eq!( + original_name, cloned_name, + "Cloned path should have the same name as original" + ); + } +} + +// Note: test_realize is not included because it requires a valid store path +// to realize, which we can't guarantee in a unit test. Integration tests +// would be more appropriate for testing realize() with actual derivations. diff --git a/nixide/src/util/bindings.rs b/nixide/src/util/bindings.rs new file mode 100644 index 0000000..ea9b45f --- /dev/null +++ b/nixide/src/util/bindings.rs @@ -0,0 +1,34 @@ +use std::os::raw::{c_char, c_uint, c_void}; +use std::path::PathBuf; + +use crate::NixError; + +pub fn wrap_libnix_string_callback(name: &'static str, callback: F) -> Result +where + F: FnOnce(unsafe extern "C" fn(*const c_char, c_uint, *mut c_void), *mut c_void) -> i32, +{ + // Callback to receive the string + unsafe extern "C" fn wrapper_callback(start: *const c_char, n: c_uint, user_data: *mut c_void) { + let result = unsafe { &mut *(user_data as *mut Option) }; + + if !start.is_null() && n > 0 { + let bytes = unsafe { std::slice::from_raw_parts(start.cast::(), n as usize) }; + if let Ok(s) = std::str::from_utf8(bytes) { + *result = Some(s.to_string()); + } + } + } + + let mut result: Option = None; + let user_data = &mut result as *mut _ as *mut c_void; + + NixError::from(callback(wrapper_callback, user_data), name)?; + result.ok_or(NixError::NullPtr { location: name }) +} + +pub fn wrap_libnix_pathbuf_callback(name: &'static str, callback: F) -> Result +where + F: FnOnce(unsafe extern "C" fn(*const c_char, c_uint, *mut c_void), *mut c_void) -> i32, +{ + wrap_libnix_string_callback(name, callback).map(PathBuf::from) +} diff --git a/nixide/src/util/mod.rs b/nixide/src/util/mod.rs new file mode 100644 index 0000000..90c70dc --- /dev/null +++ b/nixide/src/util/mod.rs @@ -0,0 +1 @@ +pub mod bindings; From d9808d6a21ed93018680155c4a4f17a067b0804f Mon Sep 17 00:00:00 2001 From: _cry64 Date: Wed, 18 Mar 2026 18:13:32 +1000 Subject: [PATCH 285/306] add sys::nix_version_get() wrappings --- nixide/src/lib.rs | 4 +- nixide/src/version.rs | 92 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 nixide/src/version.rs diff --git a/nixide/src/lib.rs b/nixide/src/lib.rs index 8dba35d..99cabca 100644 --- a/nixide/src/lib.rs +++ b/nixide/src/lib.rs @@ -4,11 +4,13 @@ mod context; mod error; mod expr; mod store; -mod util; +pub mod util; +mod version; pub use context::Context; pub use error::NixError; pub use expr::{EvalState, EvalStateBuilder, Value, ValueType}; pub use store::{Store, StorePath}; +pub use version::*; pub use nixide_sys as sys; diff --git a/nixide/src/version.rs b/nixide/src/version.rs new file mode 100644 index 0000000..631cc67 --- /dev/null +++ b/nixide/src/version.rs @@ -0,0 +1,92 @@ +use std::cmp::Ordering; +use std::ffi::CStr; +use std::num::ParseIntError; + +use crate::sys; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct NixVersion { + pub major: u32, + pub minor: u32, + pub patch: u32, + pub is_prerelease: bool, +} + +impl NixVersion { + /// Constructs a new [NixVersion] struct given the raw attributes. + /// + /// # Warning + /// + /// You are most likely interested in using the [NixVersion::current] + /// and [NixVersion::parse] functions instead of this one. + pub fn new(major: u32, minor: u32, patch: u32, is_prerelease: bool) -> Self { + Self { + major, + minor, + patch, + is_prerelease, + } + } + + /// Get the current Nix library version in the comparable [NixVersion] type. + pub fn current() -> Result { + NixVersion::parse(NixVersion::current_string().as_ref()) + } + + /// Get the current Nix library version as an owned [String]. + pub fn current_string() -> String { + unsafe { + let version_ptr = sys::nix_version_get(); + CStr::from_ptr(version_ptr).to_string_lossy().into_owned() + } + } + + /// Parse a Nix version string into the comparable [NixVersion] type. + /// + /// # Examples + /// + /// ``` + /// use nixide::NixVersion::parse; + /// + /// assert_eq!(parse("2.26"), NixVersion::new(2, 26, 0, false)); + /// assert_eq!(parse("2.33.0pre"), NixVersion::new(2, 33, 0, true)); + /// assert_eq!(parse("2.33"), NixVersion::new(2, 33, 0, false)); + /// assert_eq!(parse("2.33.1"), NixVersion::new(2, 33, 1, false)); + /// + /// // Pre-release versions sort before stable + /// assert!(parse("2.33.0pre") < parse("2.33")); + /// ``` + pub fn parse(version_str: &str) -> Result { + let parts = version_str.split('.').collect::>(); + let major = parts[0].parse::()?; + let minor = parts[1].parse::()?; + + let (patch, is_prerelease) = match parts.get(2) { + Some(s) => (s[..s.len() - 3].parse::()?, s.ends_with("pre")), + None => (0, false), + }; + + Ok(Self { + major, + minor, + patch, + is_prerelease, + }) + } +} + +impl PartialOrd for NixVersion { + fn partial_cmp(&self, other: &Self) -> Option { + if self == other { + Some(Ordering::Equal) + } else if self.major < other.major + || self.minor < other.minor + || (self.patch < other.patch) + || (self.patch == other.patch && self.is_prerelease && !other.is_prerelease) + { + Some(Ordering::Less) + } else { + Some(Ordering::Greater) + } + } +} From c0b10a8a32a3cea163d7dc43d452e308623c5e9c Mon Sep 17 00:00:00 2001 From: _cry64 Date: Wed, 18 Mar 2026 18:19:23 +1000 Subject: [PATCH 286/306] add NixVersion tests --- nixide/src/version.rs | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/nixide/src/version.rs b/nixide/src/version.rs index 631cc67..ed891c2 100644 --- a/nixide/src/version.rs +++ b/nixide/src/version.rs @@ -90,3 +90,39 @@ impl PartialOrd for NixVersion { } } } + +#[cfg(test)] +mod tests { + use super::NixVersion; + + #[test] + fn test_parse_version() { + assert_eq!( + NixVersion::parse("2.26"), + Ok(NixVersion::new(2, 26, 0, false)) + ); + assert_eq!( + NixVersion::parse("2.33.0pre"), + Ok(NixVersion::new(2, 33, 0, true)) + ); + assert_eq!( + NixVersion::parse("2.33"), + Ok(NixVersion::new(2, 33, 0, false)) + ); + assert_eq!( + NixVersion::parse("2.33.1"), + Ok(NixVersion::new(2, 33, 1, false)) + ); + } + + #[test] + fn test_version_ordering() { + // Pre-release versions should sort before stable + assert!(NixVersion::parse("2.33.0pre").unwrap() < NixVersion::parse("2.33").unwrap()); + assert!(NixVersion::parse("2.33.0pre").unwrap() < NixVersion::parse("2.33.0").unwrap()); + + // Normal version ordering + assert!(NixVersion::parse("2.26").unwrap() < NixVersion::parse("2.33").unwrap()); + assert!(NixVersion::parse("2.33").unwrap() < NixVersion::parse("2.33.1").unwrap()); + } +} From 9323f1ab3bef4db48c394332a7a2019eba52b2a5 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Wed, 18 Mar 2026 18:44:38 +1000 Subject: [PATCH 287/306] fix crate::version::tests failing --- nixide/src/version.rs | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/nixide/src/version.rs b/nixide/src/version.rs index ed891c2..dfe8890 100644 --- a/nixide/src/version.rs +++ b/nixide/src/version.rs @@ -46,15 +46,15 @@ impl NixVersion { /// # Examples /// /// ``` - /// use nixide::NixVersion::parse; + /// use nixide::NixVersion; /// - /// assert_eq!(parse("2.26"), NixVersion::new(2, 26, 0, false)); - /// assert_eq!(parse("2.33.0pre"), NixVersion::new(2, 33, 0, true)); - /// assert_eq!(parse("2.33"), NixVersion::new(2, 33, 0, false)); - /// assert_eq!(parse("2.33.1"), NixVersion::new(2, 33, 1, false)); + /// assert_eq!(NixVersion::parse("2.26"), Ok(NixVersion::new(2, 26, 0, false))); + /// assert_eq!(NixVersion::parse("2.33.0pre"), Ok(NixVersion::new(2, 33, 0, true))); + /// assert_eq!(NixVersion::parse("2.33"), Ok(NixVersion::new(2, 33, 0, false))); + /// assert_eq!(NixVersion::parse("2.33.1"), Ok(NixVersion::new(2, 33, 1, false))); /// /// // Pre-release versions sort before stable - /// assert!(parse("2.33.0pre") < parse("2.33")); + /// assert!(NixVersion::parse("2.33.0pre").unwrap() < NixVersion::parse("2.33").unwrap()); /// ``` pub fn parse(version_str: &str) -> Result { let parts = version_str.split('.').collect::>(); @@ -62,7 +62,17 @@ impl NixVersion { let minor = parts[1].parse::()?; let (patch, is_prerelease) = match parts.get(2) { - Some(s) => (s[..s.len() - 3].parse::()?, s.ends_with("pre")), + Some(s) => { + let length = s.len(); + let mut offset = length; + if length > 3 { + offset = offset.saturating_sub(3) + } + ( + s[..offset].parse::()?, // patch + length > 3 && s.ends_with("pre"), // is_prerelease + ) + } None => (0, false), }; From f8c1625d5c8c0ecb8e11ecad0ca495061c543ff2 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Wed, 18 Mar 2026 18:44:50 +1000 Subject: [PATCH 288/306] something --- flake.nix | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/flake.nix b/flake.nix index 43eab91..c8c63ec 100644 --- a/flake.nix +++ b/flake.nix @@ -56,7 +56,6 @@ cargo cargo-c - cargo-mommy cargo-llvm-cov cargo-nextest @@ -106,12 +105,7 @@ done ''; - shellHook = - postConfigure - + '' - # the fox will love this one <3 - alias cargo="CARGO_MOMMYS_MOODS=\"chill/ominous/thirsty/yikes\" ${pkgs.cargo}/bin/cargo mommy" - ''; + shellHook = postConfigure; env = let inherit (llvmPackages) llvm libclang; From 88b575b8cad0e52f880b7a2c053a7286fdafe3db Mon Sep 17 00:00:00 2001 From: _cry64 Date: Thu, 19 Mar 2026 00:39:44 +1000 Subject: [PATCH 289/306] idk, like a bunch --- nixide/src/context.rs | 196 +++++++-- nixide/src/error.rs | 42 +- nixide/src/expr/evalstate.rs | 22 +- nixide/src/expr/evalstatebuilder.rs | 18 +- nixide/src/expr/tests.rs | 12 +- nixide/src/expr/value.rs | 34 +- nixide/src/flake/eval_state_builder_ext.rs | 17 + nixide/src/flake/fetchers_settings.rs | 42 ++ nixide/src/flake/flake_lock_flags.rs | 61 +++ nixide/src/flake/flake_reference.rs | 43 ++ .../src/flake/flake_reference_parse_flags.rs | 45 ++ nixide/src/flake/flake_settings.rs | 52 +++ nixide/src/flake/flakeref.rs | 3 + nixide/src/flake/locked_flake.rs | 409 ++++++++++++++++++ nixide/src/flake/mod.rs | 15 + nixide/src/lib.rs | 20 +- nixide/src/store/mod.rs | 36 +- nixide/src/store/path.rs | 18 +- nixide/src/store/tests.rs | 6 +- nixide/src/util/bindings.rs | 16 +- nixide/src/verbosity.rs | 34 ++ 21 files changed, 1013 insertions(+), 128 deletions(-) create mode 100644 nixide/src/flake/eval_state_builder_ext.rs create mode 100644 nixide/src/flake/fetchers_settings.rs create mode 100644 nixide/src/flake/flake_lock_flags.rs create mode 100644 nixide/src/flake/flake_reference.rs create mode 100644 nixide/src/flake/flake_reference_parse_flags.rs create mode 100644 nixide/src/flake/flake_settings.rs create mode 100644 nixide/src/flake/flakeref.rs create mode 100644 nixide/src/flake/locked_flake.rs create mode 100644 nixide/src/flake/mod.rs create mode 100644 nixide/src/verbosity.rs diff --git a/nixide/src/context.rs b/nixide/src/context.rs index 189a246..2db8f8e 100644 --- a/nixide/src/context.rs +++ b/nixide/src/context.rs @@ -1,17 +1,56 @@ -use std::ptr::NonNull; +use std::ffi::{c_char, c_uint, CStr, CString}; +use std::ptr::{null_mut, NonNull}; -use crate::error::NixError; -use nixide_sys as sys; +use crate::error::NixErrorCode; +use crate::sys; +use crate::util::bindings::wrap_libnix_string_callback; -/// Nix context for managing library state. +// XXX: TODO: change this to a `Result` +type NixResult = Result; + +pub struct NixError { + pub code: NixErrorCode, + pub name: String, + pub msg: Option, + pub info_msg: Option, +} + +/// This object stores error state. /// -/// This is the root object for all Nix operations. It manages the lifetime -/// of the Nix C API context and provides automatic cleanup. -pub struct Context { +/// Passed as a first parameter to functions that can fail, to store error +/// information. +/// +/// # Warning +/// +/// These can be reused between different function calls, +/// but make sure not to use them for multiple calls simultaneously +/// (which can happen in callbacks). +/// +/// # `libnix` API Internals +/// +/// ```cpp +/// struct nix_c_context +/// { +/// nix_err last_err_code = NIX_OK; +/// /* WARNING: The last error message. Always check last_err_code. +/// WARNING: This may not have been cleared, so that clearing is fast. */ +/// std::optional last_err = {}; +/// std::optional info = {}; +/// std::string name = ""; +/// }; +/// ``` +/// +/// The [sys::nix_c_context] struct is laid out so that it can also be +/// cast to a [sys::nix_err] to inspect directly: +/// ```c +/// assert(*(nix_err*)ctx == NIX_OK); +/// ``` +/// +pub struct ErrorContext { inner: NonNull, } -impl Context { +impl ErrorContext { /// Create a new Nix context. /// /// This initializes the Nix C API context and the required libraries. @@ -19,30 +58,31 @@ impl Context { /// # Errors /// /// Returns an error if context creation or library initialization fails. - pub fn new() -> Result { + pub fn new() -> Result { // SAFETY: nix_c_context_create is safe to call let ctx_ptr = unsafe { sys::nix_c_context_create() }; - let ctx = Context { - inner: NonNull::new(ctx_ptr).ok_or(NixError::NullPtr { + let ctx = ErrorContext { + inner: NonNull::new(ctx_ptr).ok_or(NixErrorCode::NullPtr { location: "nix_c_context_create", })?, }; // Initialize required libraries - unsafe { - NixError::from( - sys::nix_libutil_init(ctx.inner.as_ptr()), - "nix_libutil_init", - )?; - NixError::from( - sys::nix_libstore_init(ctx.inner.as_ptr()), - "nix_libstore_init", - )?; - NixError::from( - sys::nix_libexpr_init(ctx.inner.as_ptr()), - "nix_libexpr_init", - )?; - }; + // XXX: TODO: move this to a separate init function (maybe a Nix::init() function) + // unsafe { + // NixErrorCode::from( + // sys::nix_libutil_init(ctx.inner.as_ptr()), + // "nix_libutil_init", + // )?; + // NixErrorCode::from( + // sys::nix_libstore_init(ctx.inner.as_ptr()), + // "nix_libstore_init", + // )?; + // NixErrorCode::from( + // sys::nix_libexpr_init(ctx.inner.as_ptr()), + // "nix_libexpr_init", + // )?; + // }; Ok(ctx) } @@ -51,13 +91,109 @@ impl Context { /// /// # Safety /// - /// The caller must ensure the pointer is used safely. - pub unsafe fn as_ptr(&self) -> *mut sys::nix_c_context { + /// Although this function isn't inherently `unsafe`, it is + /// marked as such intentionally to force calls to be wrapped + /// in `unsafe` blocks for clarity. + pub(crate) unsafe fn as_ptr(&self) -> *mut sys::nix_c_context { self.inner.as_ptr() } + + /// Check the error code and return an error if it's not `NIX_OK`. + /// + /// We recommend to use `check_call!` if possible. + pub fn peak(&self) -> Result<(), NixErrorCode> { + // NixError::from( unsafe { sys::nix_err_code(self.as_ptr())}, ""); + + let err = unsafe { sys::nix_err_code(self.inner.as_ptr()) }; + if err != sys::nix_err_NIX_OK { + // msgp is a borrowed pointer (pointing into the context), so we don't need to free it + let msgp = unsafe { sys::nix_err_msg(null_mut(), self.inner.as_ptr(), null_mut()) }; + // Turn the i8 pointer into a Rust string by copying + let msg: &str = unsafe { core::ffi::CStr::from_ptr(msgp).to_str()? }; + bail!("{}", msg); + } + Ok(()) + } + + pub fn pop(&mut self) -> Result<(), NixErrorCode> { + let result = self.peak(); + if result.is_err() { + self.clear(); + } + result + } + + pub fn clear(&mut self) { + unsafe { + // NOTE: previous nixops4 used the line: (maybe for compatability with old versions?) + // sys::nix_set_err_msg(self.inner.as_ptr(), sys::nix_err_NIX_OK, c"".as_ptr()); + sys::nix_clear_err(self.as_ptr()); + } + } + + /// + /// Never fails + pub(crate) fn get_code(&self) -> Result<(), NixErrorCode> { + NixErrorCode::from(unsafe { sys::nix_err_code(self.as_ptr()) }, "nix_err_code") + } + + pub(crate) fn get_name(&self, result: NixResult<()>) -> Option { + match result { + Err(code) => unsafe { + let ctx = null_mut(); + wrap_libnix_string_callback("nix_err_name", |callback, user_data| { + sys::nix_err_name(ctx, self.as_ptr(), Some(callback), user_data) + }) + }, + Ok(_) => None, + } + } + + /// # Note + /// On failure [sys::nix_err_name] does the following if the error + /// has the error code [sys::nix_err_NIX_OK]: + /// ```c + /// nix_set_err_msg(context, NIX_ERR_UNKNOWN, "No error message"); + /// return nullptr; + /// ``` + /// Hence we can just test whether the returned pointer is a `NULL` pointer, + /// and avoid passing in a [sys::nix_c_context] struct. + pub(crate) fn get_msg(&self, result: NixResult<()>) -> Option { + match result { + Err(_) => unsafe { + let ctx = null_mut(); + let msg_ptr: *const c_char = sys::nix_err_msg(ctx, self.as_ptr(), null_mut()); + + if msg_ptr.is_null() { + return None; + } + + match CStr::from_ptr(msg_ptr).to_str() { + Ok(msg_str) => Some(msg_str.to_string()), + Err(_) => None, + } + }, + Ok(_) => None, + } + } + + pub(crate) fn get_info_msg(&self) -> Option {} + + pub fn check_one_call_or_key_none(&mut self, f: F) -> Result, NixErrorCode> + where + F: FnOnce(*mut sys::nix_c_context) -> T, + { + let t = f(unsafe { self.as_ptr() }); + if unsafe { sys::nix_err_code(self.inner.as_ptr()) == sys::nix_err_NIX_ERR_KEY } { + self.clear(); + return Ok(None); + } + self.pop()?; + Ok(Some(t)) + } } -impl Drop for Context { +impl Drop for ErrorContext { fn drop(&mut self) { // SAFETY: We own the context and it's valid until drop unsafe { @@ -65,7 +201,3 @@ impl Drop for Context { } } } - -// SAFETY: Context can be shared between threads -unsafe impl Send for Context {} -unsafe impl Sync for Context {} diff --git a/nixide/src/error.rs b/nixide/src/error.rs index 0e2de14..1d0ce87 100644 --- a/nixide/src/error.rs +++ b/nixide/src/error.rs @@ -7,7 +7,7 @@ use crate::sys; /// Standard (nix_err) and some additional error codes /// produced by the libnix C API. #[derive(Debug)] -pub enum NixError { +pub enum NixErrorCode { /// A generic Nix error occurred. /// /// # Reason @@ -95,24 +95,24 @@ pub enum NixError { }, } -impl NixError { - pub fn from(err_code: sys::nix_err, location: &'static str) -> Result<(), NixError> { +impl NixErrorCode { + pub fn from(err_code: sys::nix_err, location: &'static str) -> Result<(), NixErrorCode> { #[allow(nonstandard_style)] match err_code { sys::nix_err_NIX_OK => Ok(()), - sys::nix_err_NIX_ERR_OVERFLOW => Err(NixError::Overflow { location }), - sys::nix_err_NIX_ERR_KEY => Err(NixError::KeyNotFound { + sys::nix_err_NIX_ERR_OVERFLOW => Err(NixErrorCode::Overflow { location }), + sys::nix_err_NIX_ERR_KEY => Err(NixErrorCode::KeyNotFound { location, key: None, }), - sys::nix_err_NIX_ERR_NIX_ERROR => Err(NixError::NixError { location }), + sys::nix_err_NIX_ERR_NIX_ERROR => Err(NixErrorCode::NixError { location }), - sys::nix_err_NIX_ERR_UNKNOWN => Err(NixError::Unknown { + sys::nix_err_NIX_ERR_UNKNOWN => Err(NixErrorCode::Unknown { location, reason: "Unknown error occurred".to_string(), }), - _ => Err(NixError::Undocumented { location, err_code }), + _ => Err(NixErrorCode::Undocumented { location, err_code }), } } @@ -120,29 +120,29 @@ impl NixError { result: Result, location: &'static str, ) -> Result { - result.or(Err(NixError::NulError { location })) + result.or(Err(NixErrorCode::NulError { location })) } pub fn new_nonnull(ptr: *mut T, location: &'static str) -> Result, Self> where T: Sized, { - NonNull::new(ptr).ok_or(NixError::NullPtr { location }) + NonNull::new(ptr).ok_or(NixErrorCode::NullPtr { location }) } } -impl std::error::Error for NixError {} +impl std::error::Error for NixErrorCode {} -impl Display for NixError { +impl Display for NixErrorCode { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { let msg = match self { - NixError::NixError { location } => { + NixErrorCode::NixError { location } => { format!("[libnix] Generic error (at location `{location}`)") } - NixError::Overflow { location } => { + NixErrorCode::Overflow { location } => { format!("[libnix] Overflow error (at location `{location}`)") } - NixError::KeyNotFound { location, key } => format!( + NixErrorCode::KeyNotFound { location, key } => format!( "[libnix] Key not found {} (at location `{location}`)", match key { Some(key) => format!("`{key}`"), @@ -150,26 +150,26 @@ impl Display for NixError { } ), - NixError::Unknown { location, reason } => { + NixErrorCode::Unknown { location, reason } => { format!("Unknown error \"{reason}\" (at location `{location}`)") } - NixError::Undocumented { location, err_code } => { + NixErrorCode::Undocumented { location, err_code } => { format!( "[libnix] An undocumented nix_err was returned with {err_code} (at location `{location}`)" ) } - NixError::NulError { location } => { + NixErrorCode::NulError { location } => { format!("Nul error (at location `{location}`)") } - NixError::NullPtr { location } => { + NixErrorCode::NullPtr { location } => { format!("[libnix] Null pointer (at location `{location}`)") } - NixError::InvalidArg { location, reason } => { + NixErrorCode::InvalidArg { location, reason } => { format!("Invalid argument \"{reason}\" (at location `{location}`)") } - NixError::InvalidType { + NixErrorCode::InvalidType { location, expected, got, diff --git a/nixide/src/expr/evalstate.rs b/nixide/src/expr/evalstate.rs index b0f0220..ded2a10 100644 --- a/nixide/src/expr/evalstate.rs +++ b/nixide/src/expr/evalstate.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use super::Value; use crate::sys; -use crate::{Context, NixError, Store}; +use crate::{ErrorContext, NixErrorCode, Store}; /// Nix evaluation state for evaluating expressions. /// @@ -14,7 +14,7 @@ pub struct EvalState { inner: NonNull, #[allow(dead_code)] store: Arc, - pub(super) context: Arc, + pub(super) context: Arc, } impl EvalState { @@ -22,7 +22,7 @@ impl EvalState { pub(super) fn new( inner: NonNull, store: Arc, - context: Arc, + context: Arc, ) -> Self { Self { inner, @@ -41,24 +41,24 @@ impl EvalState { /// # Errors /// /// Returns an error if evaluation fails. - pub fn eval_from_string(&self, expr: &str, path: &str) -> Result, NixError> { + pub fn eval_from_string(&self, expr: &str, path: &str) -> Result, NixErrorCode> { let expr_c = - NixError::from_nulerror(CString::new(expr), "nixide::EvalState::eval_from_string")?; + NixErrorCode::from_nulerror(CString::new(expr), "nixide::EvalState::eval_from_string")?; let path_c = - NixError::from_nulerror(CString::new(path), "nixide::EvalState::eval_from_string")?; + NixErrorCode::from_nulerror(CString::new(path), "nixide::EvalState::eval_from_string")?; // Allocate value for result // SAFETY: context and state are valid let value_ptr = unsafe { sys::nix_alloc_value(self.context.as_ptr(), self.inner.as_ptr()) }; if value_ptr.is_null() { - return Err(NixError::NullPtr { + return Err(NixErrorCode::NullPtr { location: "nix_alloc_value", }); } // Evaluate expression // SAFETY: all pointers are valid - NixError::from( + NixErrorCode::from( unsafe { sys::nix_expr_eval_from_string( self.context.as_ptr(), @@ -71,7 +71,7 @@ impl EvalState { "nix_expr_eval_from_string", )?; - let inner = NonNull::new(value_ptr).ok_or(NixError::NullPtr { + let inner = NonNull::new(value_ptr).ok_or(NixErrorCode::NullPtr { location: "nix_expr_eval_from_string", })?; @@ -83,10 +83,10 @@ impl EvalState { /// # Errors /// /// Returns an error if value allocation fails. - pub fn alloc_value(&self) -> Result, NixError> { + pub fn alloc_value(&self) -> Result, NixErrorCode> { // SAFETY: context and state are valid let value_ptr = unsafe { sys::nix_alloc_value(self.context.as_ptr(), self.inner.as_ptr()) }; - let inner = NonNull::new(value_ptr).ok_or(NixError::NullPtr { + let inner = NonNull::new(value_ptr).ok_or(NixErrorCode::NullPtr { location: "nix_alloc_value", })?; diff --git a/nixide/src/expr/evalstatebuilder.rs b/nixide/src/expr/evalstatebuilder.rs index 6ca906c..ef417db 100644 --- a/nixide/src/expr/evalstatebuilder.rs +++ b/nixide/src/expr/evalstatebuilder.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use super::EvalState; use crate::sys; -use crate::{Context, NixError, Store}; +use crate::{ErrorContext, NixErrorCode, Store}; /// Builder for Nix evaluation state. /// @@ -12,7 +12,7 @@ use crate::{Context, NixError, Store}; pub struct EvalStateBuilder { inner: NonNull, store: Arc, - context: Arc, + context: Arc, } impl EvalStateBuilder { @@ -25,12 +25,12 @@ impl EvalStateBuilder { /// # Errors /// /// Returns an error if the builder cannot be created. - pub fn new(store: &Arc) -> Result { + pub fn new(store: &Arc) -> Result { // SAFETY: store context and store are valid let builder_ptr = unsafe { sys::nix_eval_state_builder_new(store._context.as_ptr(), store.as_ptr()) }; - let inner = NonNull::new(builder_ptr).ok_or(NixError::NullPtr { + let inner = NonNull::new(builder_ptr).ok_or(NixErrorCode::NullPtr { location: "nix_eval_state_builder_new", })?; @@ -46,10 +46,10 @@ impl EvalStateBuilder { /// # Errors /// /// Returns an error if the evaluation state cannot be built. - pub fn build(self) -> Result { + pub fn build(self) -> Result { // Load configuration first // SAFETY: context and builder are valid - NixError::from( + NixErrorCode::from( unsafe { sys::nix_eval_state_builder_load(self.context.as_ptr(), self.inner.as_ptr()) }, "nix_eval_state_builder_load", )?; @@ -59,7 +59,7 @@ impl EvalStateBuilder { let state_ptr = unsafe { sys::nix_eval_state_build(self.context.as_ptr(), self.inner.as_ptr()) }; - let inner = NonNull::new(state_ptr).ok_or(NixError::NullPtr { + let inner = NonNull::new(state_ptr).ok_or(NixErrorCode::NullPtr { location: "nix_eval_state_build", })?; @@ -70,6 +70,10 @@ impl EvalStateBuilder { self.context.clone(), )) } + + pub(crate) unsafe fn as_ptr(&self) -> *mut sys::nix_eval_state_builder { + self.inner.as_ptr() + } } impl Drop for EvalStateBuilder { diff --git a/nixide/src/expr/tests.rs b/nixide/src/expr/tests.rs index 688824d..054fce9 100644 --- a/nixide/src/expr/tests.rs +++ b/nixide/src/expr/tests.rs @@ -3,19 +3,19 @@ use std::sync::Arc; use serial_test::serial; use super::{EvalStateBuilder, ValueType}; -use crate::{Context, Store}; +use crate::{ErrorContext, Store}; #[test] #[serial] fn test_context_creation() { - let _ctx = Context::new().expect("Failed to create context"); + let _ctx = ErrorContext::new().expect("Failed to create context"); // Context should be dropped automatically } #[test] #[serial] fn test_eval_state_builder() { - let ctx = Arc::new(Context::new().expect("Failed to create context")); + let ctx = Arc::new(ErrorContext::new().expect("Failed to create context")); let store = Arc::new(Store::open(&ctx, None).expect("Failed to open store")); let _state = EvalStateBuilder::new(&store) .expect("Failed to create builder") @@ -27,7 +27,7 @@ fn test_eval_state_builder() { #[test] #[serial] fn test_simple_evaluation() { - let ctx = Arc::new(Context::new().expect("Failed to create context")); + let ctx = Arc::new(ErrorContext::new().expect("Failed to create context")); let store = Arc::new(Store::open(&ctx, None).expect("Failed to open store")); let state = EvalStateBuilder::new(&store) .expect("Failed to create builder") @@ -45,7 +45,7 @@ fn test_simple_evaluation() { #[test] #[serial] fn test_value_types() { - let ctx = Arc::new(Context::new().expect("Failed to create context")); + let ctx = Arc::new(ErrorContext::new().expect("Failed to create context")); let store = Arc::new(Store::open(&ctx, None).expect("Failed to open store")); let state = EvalStateBuilder::new(&store) .expect("Failed to create builder") @@ -77,7 +77,7 @@ fn test_value_types() { #[test] #[serial] fn test_value_formatting() { - let ctx = Arc::new(Context::new().expect("Failed to create context")); + let ctx = Arc::new(ErrorContext::new().expect("Failed to create context")); let store = Arc::new(Store::open(&ctx, None).expect("Failed to open store")); let state = EvalStateBuilder::new(&store) .expect("Failed to create builder") diff --git a/nixide/src/expr/value.rs b/nixide/src/expr/value.rs index fce1b1b..32b6220 100644 --- a/nixide/src/expr/value.rs +++ b/nixide/src/expr/value.rs @@ -3,7 +3,7 @@ use std::ptr::NonNull; use super::{EvalState, ValueType}; use crate::sys; -use crate::NixError; +use crate::NixErrorCode; /// A Nix value /// @@ -22,8 +22,8 @@ impl Value<'_> { /// # Errors /// /// Returns an error if evaluation fails. - pub fn force(&mut self) -> Result<(), NixError> { - NixError::from( + pub fn force(&mut self) -> Result<(), NixErrorCode> { + NixErrorCode::from( // SAFETY: context, state, and value are valid unsafe { sys::nix_value_force( @@ -43,8 +43,8 @@ impl Value<'_> { /// # Errors /// /// Returns an error if evaluation fails. - pub fn force_deep(&mut self) -> Result<(), NixError> { - NixError::from( + pub fn force_deep(&mut self) -> Result<(), NixErrorCode> { + NixErrorCode::from( // SAFETY: context, state, and value are valid unsafe { sys::nix_value_force_deep( @@ -70,9 +70,9 @@ impl Value<'_> { /// # Errors /// /// Returns an error if the value is not an integer. - pub fn as_int(&self) -> Result { + pub fn as_int(&self) -> Result { if self.value_type() != ValueType::Int { - return Err(NixError::InvalidType { + return Err(NixErrorCode::InvalidType { location: "nixide::Value::as_int", expected: "int", got: self.value_type().to_string(), @@ -90,9 +90,9 @@ impl Value<'_> { /// # Errors /// /// Returns an error if the value is not a float. - pub fn as_float(&self) -> Result { + pub fn as_float(&self) -> Result { if self.value_type() != ValueType::Float { - return Err(NixError::InvalidType { + return Err(NixErrorCode::InvalidType { location: "nixide::Value::as_float", expected: "float", got: self.value_type().to_string(), @@ -111,9 +111,9 @@ impl Value<'_> { /// # Errors /// /// Returns an error if the value is not a boolean. - pub fn as_bool(&self) -> Result { + pub fn as_bool(&self) -> Result { if self.value_type() != ValueType::Bool { - return Err(NixError::InvalidType { + return Err(NixErrorCode::InvalidType { location: "nixide::Value::as_bool", expected: "bool", got: self.value_type().to_string(), @@ -131,9 +131,9 @@ impl Value<'_> { /// # Errors /// /// Returns an error if the value is not a string. - pub fn as_string(&self) -> Result { + pub fn as_string(&self) -> Result { if self.value_type() != ValueType::String { - return Err(NixError::InvalidType { + return Err(NixErrorCode::InvalidType { location: "nixide::Value::as_string", expected: "string", got: self.value_type().to_string(), @@ -152,7 +152,7 @@ impl Value<'_> { }; if realised_str.is_null() { - return Err(NixError::NullPtr { + return Err(NixErrorCode::NullPtr { location: "nix_string_realise", }); } @@ -165,7 +165,7 @@ impl Value<'_> { unsafe { sys::nix_realised_string_free(realised_str); } - return Err(NixError::NullPtr { + return Err(NixErrorCode::NullPtr { location: "nix_realised_string_free", }); } @@ -173,7 +173,7 @@ impl Value<'_> { // SAFETY: buffer_start is non-null and buffer_size gives us the length let bytes = unsafe { std::slice::from_raw_parts(buffer_start.cast::(), buffer_size) }; let string = std::str::from_utf8(bytes) - .map_err(|_| NixError::Unknown { + .map_err(|_| NixErrorCode::Unknown { location: "nixide::Value::as_string", reason: "Invalid UTF-8 in string".to_string(), })? @@ -206,7 +206,7 @@ impl Value<'_> { /// /// Returns an error if the value cannot be converted to a string /// representation. - pub fn to_nix_string(&self) -> Result { + pub fn to_nix_string(&self) -> Result { match self.value_type() { ValueType::Int => Ok(self.as_int()?.to_string()), ValueType::Float => Ok(self.as_float()?.to_string()), diff --git a/nixide/src/flake/eval_state_builder_ext.rs b/nixide/src/flake/eval_state_builder_ext.rs new file mode 100644 index 0000000..28d095e --- /dev/null +++ b/nixide/src/flake/eval_state_builder_ext.rs @@ -0,0 +1,17 @@ +pub trait EvalStateBuilderExt { + /// Configures the eval state to provide flakes features such as `builtins.getFlake`. + fn flakes( + self, + settings: &FlakeSettings, + ) -> Result; +} +impl EvalStateBuilderExt for nix_bindings_expr::eval_state::EvalStateBuilder { + /// Configures the eval state to provide flakes features such as `builtins.getFlake`. + fn flakes( + mut self, + settings: &FlakeSettings, + ) -> Result { + settings.add_to_eval_state_builder(&mut self)?; + Ok(self) + } +} diff --git a/nixide/src/flake/fetchers_settings.rs b/nixide/src/flake/fetchers_settings.rs new file mode 100644 index 0000000..8332695 --- /dev/null +++ b/nixide/src/flake/fetchers_settings.rs @@ -0,0 +1,42 @@ +use std::ptr::NonNull; + +use crate::sys; +use crate::{ErrorContext, NixErrorCode}; + +pub(super) struct FetchersSettings { + pub(super) ptr: NonNull, +} + +impl FetchersSettings { + pub fn new() -> Result { + let ctx = ErrorContext::new()?; + let ptr = unsafe { sys::nix_fetchers_settings_new(ctx.as_ptr()) }; + Ok(FetchersSettings { + ptr: NonNull::new(ptr).ok_or(NixErrorCode::NullPtr { + location: "fetchers_settings_new", + })?, + }) + } + + pub(crate) unsafe fn as_ptr(&self) -> *mut sys::nix_fetchers_settings { + self.ptr.as_ptr() + } +} + +impl Drop for FetchersSettings { + fn drop(&mut self) { + unsafe { + sys::nix_fetchers_settings_free(self.as_ptr()); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn fetchers_settings_new() { + let _ = FetchersSettings::new().unwrap(); + } +} diff --git a/nixide/src/flake/flake_lock_flags.rs b/nixide/src/flake/flake_lock_flags.rs new file mode 100644 index 0000000..464cc41 --- /dev/null +++ b/nixide/src/flake/flake_lock_flags.rs @@ -0,0 +1,61 @@ +/// Parameters that affect the locking of a flake. +pub struct FlakeLockFlags { + pub(crate) ptr: *mut raw::flake_lock_flags, +} +impl Drop for FlakeLockFlags { + fn drop(&mut self) { + unsafe { + raw::flake_lock_flags_free(self.ptr); + } + } +} +impl FlakeLockFlags { + pub fn new(settings: &FlakeSettings) -> Result { + let mut ctx = Context::new(); + let s = unsafe { context::check_call!(raw::flake_lock_flags_new(&mut ctx, settings.ptr)) }?; + Ok(FlakeLockFlags { ptr: s }) + } + /// Configures [LockedFlake::lock] to make incremental changes to the lock file as needed. Changes are written to file. + pub fn set_mode_write_as_needed(&mut self) -> Result<()> { + let mut ctx = Context::new(); + unsafe { + context::check_call!(raw::flake_lock_flags_set_mode_write_as_needed( + &mut ctx, self.ptr + )) + }?; + Ok(()) + } + /// Make [LockedFlake::lock] check if the lock file is up to date. If not, an error is returned. + pub fn set_mode_check(&mut self) -> Result<()> { + let mut ctx = Context::new(); + unsafe { context::check_call!(raw::flake_lock_flags_set_mode_check(&mut ctx, self.ptr)) }?; + Ok(()) + } + /// Like `set_mode_write_as_needed`, but does not write to the lock file. + pub fn set_mode_virtual(&mut self) -> Result<()> { + let mut ctx = Context::new(); + unsafe { + context::check_call!(raw::flake_lock_flags_set_mode_virtual(&mut ctx, self.ptr)) + }?; + Ok(()) + } + /// Adds an input override to the lock file that will be produced. The [LockedFlake::lock] operation will not write to the lock file. + pub fn add_input_override( + &mut self, + override_path: &str, + override_ref: &FlakeReference, + ) -> Result<()> { + let mut ctx = Context::new(); + unsafe { + context::check_call!(raw::flake_lock_flags_add_input_override( + &mut ctx, + self.ptr, + CString::new(override_path) + .context("Failed to create CString for override_path")? + .as_ptr(), + override_ref.ptr.as_ptr() + )) + }?; + Ok(()) + } +} diff --git a/nixide/src/flake/flake_reference.rs b/nixide/src/flake/flake_reference.rs new file mode 100644 index 0000000..09c31d8 --- /dev/null +++ b/nixide/src/flake/flake_reference.rs @@ -0,0 +1,43 @@ +pub struct FlakeReference { + pub(crate) ptr: NonNull, +} +impl Drop for FlakeReference { + fn drop(&mut self) { + unsafe { + raw::flake_reference_free(self.ptr.as_ptr()); + } + } +} +impl FlakeReference { + /// Parse a flake reference from a string. + /// The string must be a valid flake reference, such as `github:owner/repo`. + /// It may also be suffixed with a `#` and a fragment, such as `github:owner/repo#something`, + /// in which case, the returned string will contain the fragment. + pub fn parse_with_fragment( + fetch_settings: &FetchersSettings, + flake_settings: &FlakeSettings, + flags: &FlakeReferenceParseFlags, + reference: &str, + ) -> Result<(FlakeReference, String)> { + let mut ctx = Context::new(); + let mut r = result_string_init!(); + let mut ptr: *mut raw::flake_reference = std::ptr::null_mut(); + unsafe { + context::check_call!(raw::flake_reference_and_fragment_from_string( + &mut ctx, + fetch_settings.raw_ptr(), + flake_settings.ptr, + flags.ptr.as_ptr(), + reference.as_ptr() as *const c_char, + reference.len(), + // pointer to ptr + &mut ptr, + Some(callback_get_result_string), + callback_get_result_string_data(&mut r) + )) + }?; + let ptr = NonNull::new(ptr) + .context("flake_reference_and_fragment_from_string unexpectedly returned null")?; + Ok((FlakeReference { ptr }, r?)) + } +} diff --git a/nixide/src/flake/flake_reference_parse_flags.rs b/nixide/src/flake/flake_reference_parse_flags.rs new file mode 100644 index 0000000..2353d94 --- /dev/null +++ b/nixide/src/flake/flake_reference_parse_flags.rs @@ -0,0 +1,45 @@ +use std::ptr::NonNull; + +use super::FlakeSettings; +use crate::sys; +use crate::{ErrorContext, NixErrorCode}; + +/// Parameters for parsing a flake reference. +pub struct FlakeReferenceParseFlags { + pub(crate) ptr: NonNull, +} +impl Drop for FlakeReferenceParseFlags { + fn drop(&mut self) { + unsafe { + sys::nix_flake_reference_parse_flags_free(self.ptr.as_ptr()); + } + } +} +impl FlakeReferenceParseFlags { + pub fn new(settings: &FlakeSettings) -> Result { + let mut ctx = ErrorContext::new(); + let ptr = unsafe { + context::check_call!(sys::nix_flake_reference_parse_flags_new( + &mut ctx, + settings.ptr + )) + }?; + let ptr = NonNull::new(ptr) + .context("flake_reference_parse_flags_new unexpectedly returned null")?; + Ok(FlakeReferenceParseFlags { ptr }) + } + /// Sets the [base directory](https://nix.dev/manual/nix/latest/glossary#gloss-base-directory) + /// for resolving local flake references. + pub fn set_base_directory(&mut self, base_directory: &str) -> Result<(), NixErrorCode> { + let mut ctx = ErrorContext::new(); + unsafe { + sys::context::check_call!(sys::nix_flake_reference_parse_flags_set_base_directory( + &mut ctx, + self.ptr.as_ptr(), + base_directory.as_ptr() as *const c_char, + base_directory.len() + )) + }?; + Ok(()) + } +} diff --git a/nixide/src/flake/flake_settings.rs b/nixide/src/flake/flake_settings.rs new file mode 100644 index 0000000..3cc2082 --- /dev/null +++ b/nixide/src/flake/flake_settings.rs @@ -0,0 +1,52 @@ +use std::ptr::NonNull; + +use crate::sys; +use crate::{ErrorContext, EvalStateBuilder, NixErrorCode}; + +/// Store settings for the flakes feature. +pub struct FlakeSettings { + pub(crate) inner: NonNull, +} + +impl FlakeSettings { + pub fn new() -> Result { + let ctx = ErrorContext::new()?; + let inner = NonNull::new(unsafe { sys::nix_flake_settings_new(ctx.as_ptr()) }).ok_or( + NixErrorCode::NullPtr { + location: "nix_flake_settings_new", + }, + )?; + Ok(FlakeSettings { inner }) + } + + fn add_to_eval_state_builder( + &self, + builder: &mut EvalStateBuilder, + ) -> Result<(), NixErrorCode> { + let ctx = ErrorContext::new()?; + NixErrorCode::from( + unsafe { + sys::nix_flake_settings_add_to_eval_state_builder( + ctx.as_ptr(), + self.as_ptr(), + builder.as_ptr(), + ) + }, + "nix_flake_settings_add_to_eval_state_builder", + )?; + + Ok(()) + } + + pub(crate) unsafe fn as_ptr(&self) -> *mut sys::nix_flake_settings { + self.inner.as_ptr() + } +} + +impl Drop for FlakeSettings { + fn drop(&mut self) { + unsafe { + sys::nix_flake_settings_free(self.as_ptr()); + } + } +} diff --git a/nixide/src/flake/flakeref.rs b/nixide/src/flake/flakeref.rs new file mode 100644 index 0000000..7d6f719 --- /dev/null +++ b/nixide/src/flake/flakeref.rs @@ -0,0 +1,3 @@ +pub struct FlakeRef {} + +impl FlakeRef {} diff --git a/nixide/src/flake/locked_flake.rs b/nixide/src/flake/locked_flake.rs new file mode 100644 index 0000000..8348319 --- /dev/null +++ b/nixide/src/flake/locked_flake.rs @@ -0,0 +1,409 @@ +pub struct LockedFlake { + pub(crate) ptr: NonNull, +} +impl Drop for LockedFlake { + fn drop(&mut self) { + unsafe { + raw::locked_flake_free(self.ptr.as_ptr()); + } + } +} +impl LockedFlake { + pub fn lock( + fetch_settings: &FetchersSettings, + flake_settings: &FlakeSettings, + eval_state: &EvalState, + flags: &FlakeLockFlags, + flake_ref: &FlakeReference, + ) -> Result { + let mut ctx = Context::new(); + let ptr = unsafe { + context::check_call!(raw::flake_lock( + &mut ctx, + fetch_settings.raw_ptr(), + flake_settings.ptr, + eval_state.raw_ptr(), + flags.ptr, + flake_ref.ptr.as_ptr() + )) + }?; + let ptr = NonNull::new(ptr).context("flake_lock unexpectedly returned null")?; + Ok(LockedFlake { ptr }) + } + + /// Returns the outputs of the flake - the result of calling the `outputs` attribute. + pub fn outputs( + &self, + flake_settings: &FlakeSettings, + eval_state: &mut EvalState, + ) -> Result { + let mut ctx = Context::new(); + unsafe { + let r = context::check_call!(raw::locked_flake_get_output_attrs( + &mut ctx, + flake_settings.ptr, + eval_state.raw_ptr(), + self.ptr.as_ptr() + ))?; + Ok(nix_bindings_expr::value::__private::raw_value_new(r)) + } + } +} + +#[cfg(test)] +mod tests { + use nix_bindings_expr::eval_state::{gc_register_my_thread, EvalStateBuilder}; + use nix_bindings_store::store::Store; + + use super::*; + use std::sync::Once; + + static INIT: Once = Once::new(); + + fn init() { + // Only set experimental-features once to minimize the window where + // concurrent Nix operations might read the setting while it's being modified + INIT.call_once(|| { + nix_bindings_expr::eval_state::init().unwrap(); + nix_bindings_util::settings::set("experimental-features", "flakes").unwrap(); + }); + } + + #[test] + fn flake_settings_getflake_exists() { + init(); + let gc_registration = gc_register_my_thread(); + let store = Store::open(None, []).unwrap(); + let mut eval_state = EvalStateBuilder::new(store) + .unwrap() + .flakes(&FlakeSettings::new().unwrap()) + .unwrap() + .build() + .unwrap(); + + let v = eval_state + .eval_from_string("builtins?getFlake", "") + .unwrap(); + + let b = eval_state.require_bool(&v).unwrap(); + + assert!(b); + + drop(gc_registration); + } + + #[test] + fn flake_lock_load_flake() { + init(); + let gc_registration = gc_register_my_thread(); + let store = Store::open(None, []).unwrap(); + let fetchers_settings = FetchersSettings::new().unwrap(); + let flake_settings = FlakeSettings::new().unwrap(); + let mut eval_state = EvalStateBuilder::new(store) + .unwrap() + .flakes(&flake_settings) + .unwrap() + .build() + .unwrap(); + + let tmp_dir = tempfile::tempdir().unwrap(); + + // Create flake.nix + let flake_nix = tmp_dir.path().join("flake.nix"); + std::fs::write( + &flake_nix, + r#" +{ + outputs = { ... }: { + hello = "potato"; + }; +} + "#, + ) + .unwrap(); + + let flake_lock_flags = FlakeLockFlags::new(&flake_settings).unwrap(); + + let (flake_ref, fragment) = FlakeReference::parse_with_fragment( + &fetchers_settings, + &flake_settings, + &FlakeReferenceParseFlags::new(&flake_settings).unwrap(), + &format!("path:{}#subthing", tmp_dir.path().display()), + ) + .unwrap(); + + assert_eq!(fragment, "subthing"); + + let locked_flake = LockedFlake::lock( + &fetchers_settings, + &flake_settings, + &eval_state, + &flake_lock_flags, + &flake_ref, + ) + .unwrap(); + + let outputs = locked_flake + .outputs(&flake_settings, &mut eval_state) + .unwrap(); + + let hello = eval_state.require_attrs_select(&outputs, "hello").unwrap(); + let hello = eval_state.require_string(&hello).unwrap(); + + assert_eq!(hello, "potato"); + + drop(fetchers_settings); + drop(tmp_dir); + drop(gc_registration); + } + + #[test] + fn flake_lock_load_flake_with_flags() { + init(); + let gc_registration = gc_register_my_thread(); + let store = Store::open(None, []).unwrap(); + let fetchers_settings = FetchersSettings::new().unwrap(); + let flake_settings = FlakeSettings::new().unwrap(); + let mut eval_state = EvalStateBuilder::new(store) + .unwrap() + .flakes(&flake_settings) + .unwrap() + .build() + .unwrap(); + + let tmp_dir = tempfile::tempdir().unwrap(); + + let flake_dir_a = tmp_dir.path().join("a"); + let flake_dir_b = tmp_dir.path().join("b"); + let flake_dir_c = tmp_dir.path().join("c"); + + std::fs::create_dir_all(&flake_dir_a).unwrap(); + std::fs::create_dir_all(&flake_dir_b).unwrap(); + std::fs::create_dir_all(&flake_dir_c).unwrap(); + + let flake_dir_a_str = flake_dir_a.to_str().unwrap(); + let flake_dir_c_str = flake_dir_c.to_str().unwrap(); + assert!(!flake_dir_a_str.is_empty()); + assert!(!flake_dir_c_str.is_empty()); + + // a + std::fs::write( + tmp_dir.path().join("a/flake.nix"), + r#" + { + inputs.b.url = "@flake_dir_b@"; + outputs = { b, ... }: { + hello = b.hello; + }; + } + "# + .replace("@flake_dir_b@", flake_dir_b.to_str().unwrap()), + ) + .unwrap(); + + // b + std::fs::write( + tmp_dir.path().join("b/flake.nix"), + r#" + { + outputs = { ... }: { + hello = "BOB"; + }; + } + "#, + ) + .unwrap(); + + // c + std::fs::write( + tmp_dir.path().join("c/flake.nix"), + r#" + { + outputs = { ... }: { + hello = "Claire"; + }; + } + "#, + ) + .unwrap(); + + let mut flake_lock_flags = FlakeLockFlags::new(&flake_settings).unwrap(); + + let mut flake_reference_parse_flags = + FlakeReferenceParseFlags::new(&flake_settings).unwrap(); + + flake_reference_parse_flags + .set_base_directory(tmp_dir.path().to_str().unwrap()) + .unwrap(); + + let (flake_ref_a, fragment) = FlakeReference::parse_with_fragment( + &fetchers_settings, + &flake_settings, + &flake_reference_parse_flags, + &format!("path:{}", &flake_dir_a_str), + ) + .unwrap(); + + assert_eq!(fragment, ""); + + // Step 1: Do not update (check), fails + + flake_lock_flags.set_mode_check().unwrap(); + + let locked_flake = LockedFlake::lock( + &fetchers_settings, + &flake_settings, + &eval_state, + &flake_lock_flags, + &flake_ref_a, + ); + // Has not been locked and would need to write a lock file. + assert!(locked_flake.is_err()); + let saved_err = match locked_flake { + Ok(_) => panic!("Expected error, but got Ok"), + Err(e) => e, + }; + + // Step 2: Update but do not write, succeeds + flake_lock_flags.set_mode_virtual().unwrap(); + + let locked_flake = LockedFlake::lock( + &fetchers_settings, + &flake_settings, + &eval_state, + &flake_lock_flags, + &flake_ref_a, + ) + .unwrap(); + + let outputs = locked_flake + .outputs(&flake_settings, &mut eval_state) + .unwrap(); + + let hello = eval_state.require_attrs_select(&outputs, "hello").unwrap(); + let hello = eval_state.require_string(&hello).unwrap(); + + assert_eq!(hello, "BOB"); + + // Step 3: The lock was not written, so Step 1 would fail again + + flake_lock_flags.set_mode_check().unwrap(); + + let locked_flake = LockedFlake::lock( + &fetchers_settings, + &flake_settings, + &eval_state, + &flake_lock_flags, + &flake_ref_a, + ); + // Has not been locked and would need to write a lock file. + assert!(locked_flake.is_err()); + match locked_flake { + Ok(_) => panic!("Expected error, but got Ok"), + Err(e) => { + assert_eq!(e.to_string(), saved_err.to_string()); + } + }; + + // Step 4: Update and write, succeeds + + flake_lock_flags.set_mode_write_as_needed().unwrap(); + + let locked_flake = LockedFlake::lock( + &fetchers_settings, + &flake_settings, + &eval_state, + &flake_lock_flags, + &flake_ref_a, + ) + .unwrap(); + + let outputs = locked_flake + .outputs(&flake_settings, &mut eval_state) + .unwrap(); + let hello = eval_state.require_attrs_select(&outputs, "hello").unwrap(); + let hello = eval_state.require_string(&hello).unwrap(); + assert_eq!(hello, "BOB"); + + // Step 5: Lock was written, so Step 1 succeeds + + flake_lock_flags.set_mode_check().unwrap(); + + let locked_flake = LockedFlake::lock( + &fetchers_settings, + &flake_settings, + &eval_state, + &flake_lock_flags, + &flake_ref_a, + ) + .unwrap(); + + let outputs = locked_flake + .outputs(&flake_settings, &mut eval_state) + .unwrap(); + let hello = eval_state.require_attrs_select(&outputs, "hello").unwrap(); + let hello = eval_state.require_string(&hello).unwrap(); + assert_eq!(hello, "BOB"); + + // Step 6: Lock with override, do not write + + // This shouldn't matter; write_as_needed will be overridden + flake_lock_flags.set_mode_write_as_needed().unwrap(); + + let (flake_ref_c, fragment) = FlakeReference::parse_with_fragment( + &fetchers_settings, + &flake_settings, + &flake_reference_parse_flags, + &format!("path:{}", &flake_dir_c_str), + ) + .unwrap(); + assert_eq!(fragment, ""); + + flake_lock_flags + .add_input_override("b", &flake_ref_c) + .unwrap(); + + let locked_flake = LockedFlake::lock( + &fetchers_settings, + &flake_settings, + &eval_state, + &flake_lock_flags, + &flake_ref_a, + ) + .unwrap(); + + let outputs = locked_flake + .outputs(&flake_settings, &mut eval_state) + .unwrap(); + let hello = eval_state.require_attrs_select(&outputs, "hello").unwrap(); + let hello = eval_state.require_string(&hello).unwrap(); + assert_eq!(hello, "Claire"); + + // Can't delete overrides, so trash it + let mut flake_lock_flags = FlakeLockFlags::new(&flake_settings).unwrap(); + + // Step 7: Override was not written; lock still points to b + + flake_lock_flags.set_mode_check().unwrap(); + + let locked_flake = LockedFlake::lock( + &fetchers_settings, + &flake_settings, + &eval_state, + &flake_lock_flags, + &flake_ref_a, + ) + .unwrap(); + + let outputs = locked_flake + .outputs(&flake_settings, &mut eval_state) + .unwrap(); + let hello = eval_state.require_attrs_select(&outputs, "hello").unwrap(); + let hello = eval_state.require_string(&hello).unwrap(); + assert_eq!(hello, "BOB"); + + drop(fetchers_settings); + drop(tmp_dir); + drop(gc_registration); + } +} diff --git a/nixide/src/flake/mod.rs b/nixide/src/flake/mod.rs new file mode 100644 index 0000000..b6a4133 --- /dev/null +++ b/nixide/src/flake/mod.rs @@ -0,0 +1,15 @@ +mod eval_state_builder_ext; +mod fetchers_settings; +mod flake_lock_flags; +mod flake_reference; +mod flake_reference_parse_flags; +mod flake_settings; +mod locked_flake; + +pub(self) use eval_state_builder_ext::EvalStateBuilderExt; +pub(self) use fetchers_settings::FetchersSettings; +pub(self) use flake_lock_flags::FlakeLockFlags; +pub(self) use flake_reference::FlakeReference; +pub(self) use flake_reference_parse_flags::FlakeReferenceParseFlags; +pub(self) use flake_settings::FlakeSettings; +pub(self) use locked_flake::LockedFlake; diff --git a/nixide/src/lib.rs b/nixide/src/lib.rs index 99cabca..3bb26ed 100644 --- a/nixide/src/lib.rs +++ b/nixide/src/lib.rs @@ -3,14 +3,28 @@ mod context; mod error; mod expr; +mod flake; mod store; pub mod util; +mod verbosity; mod version; -pub use context::Context; -pub use error::NixError; +pub use context::ErrorContext; +pub use error::NixErrorCode; pub use expr::{EvalState, EvalStateBuilder, Value, ValueType}; pub use store::{Store, StorePath}; -pub use version::*; +pub use verbosity::NixVerbosity; +pub use version::NixVersion; pub use nixide_sys as sys; + +/// Sets the verbosity level +/// +/// # Arguments +/// +/// * `context` - additional error context, used as an output +/// * `level` - verbosity level +pub fn set_verbosity() { + // nix_err nix_set_verbosity(nix_c_context * context, nix_verbosity level); + // XXX: TODO: (implement Context first) +} diff --git a/nixide/src/store/mod.rs b/nixide/src/store/mod.rs index 668f6b9..bbfe92e 100644 --- a/nixide/src/store/mod.rs +++ b/nixide/src/store/mod.rs @@ -23,7 +23,7 @@ use std::ptr::NonNull; use std::result::Result; use std::sync::Arc; -use super::{Context, NixError}; +use super::{ErrorContext, NixErrorCode}; use crate::util::bindings::{wrap_libnix_pathbuf_callback, wrap_libnix_string_callback}; use nixide_sys as sys; @@ -32,7 +32,7 @@ use nixide_sys as sys; /// The store provides access to Nix packages, derivations, and store paths. pub struct Store { pub(crate) inner: NonNull, - pub(crate) _context: Arc, + pub(crate) _context: Arc, } impl Store { @@ -46,10 +46,10 @@ impl Store { /// # Errors /// /// Returns an error if the store cannot be opened. - pub fn open(context: &Arc, uri: Option<&str>) -> Result { + pub fn open(context: &Arc, uri: Option<&str>) -> Result { let uri_cstring: CString; let uri_ptr = if let Some(uri) = uri { - uri_cstring = NixError::from_nulerror(CString::new(uri), "nixide::Store::open")?; + uri_cstring = NixErrorCode::from_nulerror(CString::new(uri), "nixide::Store::open")?; uri_cstring.as_ptr() } else { std::ptr::null() @@ -59,7 +59,7 @@ impl Store { let store_ptr = unsafe { sys::nix_store_open(context.as_ptr(), uri_ptr, std::ptr::null_mut()) }; - let inner = NonNull::new(store_ptr).ok_or(NixError::NullPtr { + let inner = NonNull::new(store_ptr).ok_or(NixErrorCode::NullPtr { location: "nix_store_open", })?; @@ -99,9 +99,13 @@ impl Store { &self, path: &StorePath, callback: fn(&str, &StorePath), - ) -> Result, NixError> { + ) -> Result, NixErrorCode> { // Type alias for our userdata: (outputs vector, context) - type Userdata = (Vec<(String, StorePath)>, Arc, fn(&str, &StorePath)); + type Userdata = ( + Vec<(String, StorePath)>, + Arc, + fn(&str, &StorePath), + ); // Callback function that will be called for each realized output unsafe extern "C" fn realise_callback( @@ -157,7 +161,7 @@ impl Store { ) }; - NixError::from(err, "nix_store_realise")?; + NixErrorCode::from(err, "nix_store_realise")?; // Return the collected outputs Ok(userdata.0) @@ -187,14 +191,14 @@ impl Store { /// # Ok(()) /// # } /// ``` - pub fn store_path(&self, path: &str) -> Result { + pub fn store_path(&self, path: &str) -> Result { StorePath::parse(&self._context, self, path) } /// Get the version of a Nix store /// /// If the store doesn't have a version (like the dummy store), returns None - pub fn version(&self) -> Result { + pub fn version(&self) -> Result { wrap_libnix_string_callback("nix_store_get_version", |callback, user_data| unsafe { sys::nix_store_get_version( self._context.as_ptr(), @@ -206,7 +210,7 @@ impl Store { } /// Get the URI of a Nix store - pub fn uri(&self) -> Result { + pub fn uri(&self) -> Result { wrap_libnix_string_callback("nix_store_get_uri", |callback, user_data| unsafe { sys::nix_store_get_uri( self._context.as_ptr(), @@ -217,7 +221,7 @@ impl Store { }) } - pub fn store_dir(&self) -> Result { + pub fn store_dir(&self) -> Result { wrap_libnix_pathbuf_callback("nix_store_get_storedir", |callback, user_data| unsafe { sys::nix_store_get_storedir( self._context.as_ptr(), @@ -232,7 +236,7 @@ impl Store { &self, dst_store: &Store, store_path: &StorePath, - ) -> Result<(), NixError> { + ) -> Result<(), NixErrorCode> { let err = unsafe { sys::nix_store_copy_closure( self._context.as_ptr(), @@ -241,14 +245,14 @@ impl Store { store_path.inner.as_ptr(), ) }; - NixError::from(err, "nix_store_copy_closure") + NixErrorCode::from(err, "nix_store_copy_closure") } pub fn copy_closure_from( &self, src_store: &Store, store_path: &StorePath, - ) -> Result<(), NixError> { + ) -> Result<(), NixErrorCode> { let err = unsafe { sys::nix_store_copy_closure( self._context.as_ptr(), @@ -257,7 +261,7 @@ impl Store { store_path.inner.as_ptr(), ) }; - NixError::from(err, "nix_store_copy_closure") + NixErrorCode::from(err, "nix_store_copy_closure") } } diff --git a/nixide/src/store/path.rs b/nixide/src/store/path.rs index 8b8b6e4..aecc896 100644 --- a/nixide/src/store/path.rs +++ b/nixide/src/store/path.rs @@ -5,7 +5,7 @@ use std::sync::Arc; use super::Store; use crate::util::bindings::{wrap_libnix_pathbuf_callback, wrap_libnix_string_callback}; -use crate::{Context, NixError}; +use crate::{ErrorContext, NixErrorCode}; use nixide_sys::{self as sys, nix_err_NIX_OK}; /// A path in the Nix store. @@ -13,7 +13,7 @@ use nixide_sys::{self as sys, nix_err_NIX_OK}; /// Represents a store path that can be realized, queried, or manipulated. pub struct StorePath { pub(crate) inner: NonNull, - pub(crate) _context: Arc, + pub(crate) _context: Arc, } impl StorePath { @@ -28,8 +28,12 @@ impl StorePath { /// # Errors /// /// Returns an error if the path cannot be parsed. - pub fn parse(context: &Arc, store: &Store, path: &str) -> Result { - let path_cstring = CString::new(path).or(Err(NixError::InvalidArg { + pub fn parse( + context: &Arc, + store: &Store, + path: &str, + ) -> Result { + let path_cstring = CString::new(path).or(Err(NixErrorCode::InvalidArg { location: "nixide::StorePath::parse", reason: "`path` contains NUL char", }))?; @@ -39,7 +43,7 @@ impl StorePath { sys::nix_store_parse_path(context.as_ptr(), store.as_ptr(), path_cstring.as_ptr()) }; - let inner = NonNull::new(path_ptr).ok_or(NixError::NullPtr { + let inner = NonNull::new(path_ptr).ok_or(NixErrorCode::NullPtr { location: "nix_store_parse_path", })?; @@ -57,7 +61,7 @@ impl StorePath { /// # Errors /// /// Returns an error if the name cannot be retrieved. - pub fn name(&self) -> Result { + pub fn name(&self) -> Result { wrap_libnix_string_callback("nix_store_path_name", |callback, user_data| unsafe { sys::nix_store_path_name(self.inner.as_ptr(), Some(callback), user_data); @@ -87,7 +91,7 @@ impl StorePath { /// /// * `store` - The store containing the path /// - pub fn real_path(&self, store: &Store) -> Result { + pub fn real_path(&self, store: &Store) -> Result { wrap_libnix_pathbuf_callback("nix_store_real_path", |callback, user_data| unsafe { sys::nix_store_real_path( self._context.as_ptr(), diff --git a/nixide/src/store/tests.rs b/nixide/src/store/tests.rs index e00c3c9..4fdef06 100644 --- a/nixide/src/store/tests.rs +++ b/nixide/src/store/tests.rs @@ -5,14 +5,14 @@ use super::*; #[test] #[serial] fn test_store_opening() { - let ctx = Arc::new(Context::new().expect("Failed to create context")); + let ctx = Arc::new(ErrorContext::new().expect("Failed to create context")); let _store = Store::open(&ctx, None).expect("Failed to open store"); } #[test] #[serial] fn test_store_path_parse() { - let ctx = Arc::new(Context::new().expect("Failed to create context")); + let ctx = Arc::new(ErrorContext::new().expect("Failed to create context")); let store = Store::open(&ctx, None).expect("Failed to open store"); // Try parsing a well-formed store path @@ -38,7 +38,7 @@ fn test_store_path_parse() { #[test] #[serial] fn test_store_path_clone() { - let ctx = Arc::new(Context::new().expect("Failed to create context")); + let ctx = Arc::new(ErrorContext::new().expect("Failed to create context")); let store = Store::open(&ctx, None).expect("Failed to open store"); // Try to get a valid store path by parsing diff --git a/nixide/src/util/bindings.rs b/nixide/src/util/bindings.rs index ea9b45f..cd8b4f8 100644 --- a/nixide/src/util/bindings.rs +++ b/nixide/src/util/bindings.rs @@ -1,9 +1,12 @@ use std::os::raw::{c_char, c_uint, c_void}; use std::path::PathBuf; -use crate::NixError; +use crate::NixErrorCode; -pub fn wrap_libnix_string_callback(name: &'static str, callback: F) -> Result +pub fn wrap_libnix_string_callback( + name: &'static str, + callback: F, +) -> Result where F: FnOnce(unsafe extern "C" fn(*const c_char, c_uint, *mut c_void), *mut c_void) -> i32, { @@ -22,11 +25,14 @@ where let mut result: Option = None; let user_data = &mut result as *mut _ as *mut c_void; - NixError::from(callback(wrapper_callback, user_data), name)?; - result.ok_or(NixError::NullPtr { location: name }) + NixErrorCode::from(callback(wrapper_callback, user_data), name)?; + result.ok_or(NixErrorCode::NullPtr { location: name }) } -pub fn wrap_libnix_pathbuf_callback(name: &'static str, callback: F) -> Result +pub fn wrap_libnix_pathbuf_callback( + name: &'static str, + callback: F, +) -> Result where F: FnOnce(unsafe extern "C" fn(*const c_char, c_uint, *mut c_void), *mut c_void) -> i32, { diff --git a/nixide/src/verbosity.rs b/nixide/src/verbosity.rs new file mode 100644 index 0000000..fb1918b --- /dev/null +++ b/nixide/src/verbosity.rs @@ -0,0 +1,34 @@ +use crate::sys; + +/// Verbosity level +/// +/// # NOTE +/// +/// This should be kept in sync with the C++ implementation (nix::Verbosity) +#[derive(Debug, Clone, Copy)] +pub enum NixVerbosity { + Error, + Warn, + Notice, + Info, + Talkative, + Chatty, + Debug, + Vomit, +} + +impl From for NixVerbosity { + fn from(level: sys::nix_verbosity) -> NixVerbosity { + match level { + sys::nix_verbosity_NIX_LVL_ERROR => NixVerbosity::Error, + sys::nix_verbosity_NIX_LVL_WARN => NixVerbosity::Warn, + sys::nix_verbosity_NIX_LVL_NOTICE => NixVerbosity::Notice, + sys::nix_verbosity_NIX_LVL_INFO => NixVerbosity::Info, + sys::nix_verbosity_NIX_LVL_TALKATIVE => NixVerbosity::Talkative, + sys::nix_verbosity_NIX_LVL_CHATTY => NixVerbosity::Chatty, + sys::nix_verbosity_NIX_LVL_DEBUG => NixVerbosity::Debug, + sys::nix_verbosity_NIX_LVL_VOMIT => NixVerbosity::Vomit, + _ => panic!("nixide encountered unknown `nix_verbosity` value, please submit this as an issue at https://github.com/cry128/nixide"), + } + } +} From 6d0b5b7eeaa36cab9e8bdc4c686ab617276cf8e6 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Thu, 19 Mar 2026 00:40:22 +1000 Subject: [PATCH 290/306] add TODO.md --- TODO.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 TODO.md diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..4160a9f --- /dev/null +++ b/TODO.md @@ -0,0 +1,3 @@ +- [ ] add NixError::from_nonnull that replaces calls to NonNull::new(...).ok_or(...) +- [ ] replace all `use nixide_sys as sys;` -> `use crate::sys;` +- [ ] store NonNull pointers in structs! From 6b1ca1463fa92102253c71daa4d091e92fb09e11 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Thu, 19 Mar 2026 01:38:27 +1000 Subject: [PATCH 291/306] fix get_name return type --- nixide/src/context.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nixide/src/context.rs b/nixide/src/context.rs index 2db8f8e..ae6f7e2 100644 --- a/nixide/src/context.rs +++ b/nixide/src/context.rs @@ -139,11 +139,12 @@ impl ErrorContext { pub(crate) fn get_name(&self, result: NixResult<()>) -> Option { match result { - Err(code) => unsafe { + Err(_) => unsafe { let ctx = null_mut(); wrap_libnix_string_callback("nix_err_name", |callback, user_data| { sys::nix_err_name(ctx, self.as_ptr(), Some(callback), user_data) }) + .ok() }, Ok(_) => None, } From d10a2c8908f4629438eab08c0bc251c1d75c94a6 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Thu, 19 Mar 2026 02:46:43 +1000 Subject: [PATCH 292/306] update context.rs --- TODO.md | 1 + nixide/src/context.rs | 126 ++++++++++++++++++++++++++++-------------- nixide/src/error.rs | 2 +- 3 files changed, 86 insertions(+), 43 deletions(-) diff --git a/TODO.md b/TODO.md index 4160a9f..075a528 100644 --- a/TODO.md +++ b/TODO.md @@ -1,3 +1,4 @@ - [ ] add NixError::from_nonnull that replaces calls to NonNull::new(...).ok_or(...) - [ ] replace all `use nixide_sys as sys;` -> `use crate::sys;` - [ ] store NonNull pointers in structs! +- [ ] improve documentation situation on context.rs diff --git a/nixide/src/context.rs b/nixide/src/context.rs index ae6f7e2..6fe3011 100644 --- a/nixide/src/context.rs +++ b/nixide/src/context.rs @@ -1,4 +1,27 @@ -use std::ffi::{c_char, c_uint, CStr, CString}; +// XXX: TODO: create wrappers methods to access more than just `info->msg()` +// struct ErrorInfo +// { +// Verbosity level; +// HintFmt msg; +// std::shared_ptr pos; +// std::list traces; +// /** +// * Some messages are generated directly by expressions; notably `builtins.warn`, `abort`, `throw`. +// * These may be rendered differently, so that users can distinguish them. +// */ +// bool isFromExpr = false; + +// /** +// * Exit status. +// */ +// unsigned int status = 1; + +// Suggestions suggestions; + +// static std::optional programName; +// }; + +use std::ffi::{c_char, CStr}; use std::ptr::{null_mut, NonNull}; use crate::error::NixErrorCode; @@ -8,11 +31,12 @@ use crate::util::bindings::wrap_libnix_string_callback; // XXX: TODO: change this to a `Result` type NixResult = Result; +#[derive(Debug, Clone)] pub struct NixError { - pub code: NixErrorCode, + pub err: NixErrorCode, pub name: String, - pub msg: Option, - pub info_msg: Option, + pub msg: String, + pub info_msg: String, } /// This object stores error state. @@ -101,26 +125,37 @@ impl ErrorContext { /// Check the error code and return an error if it's not `NIX_OK`. /// /// We recommend to use `check_call!` if possible. - pub fn peak(&self) -> Result<(), NixErrorCode> { + pub fn peak(&self) -> Option { // NixError::from( unsafe { sys::nix_err_code(self.as_ptr())}, ""); - let err = unsafe { sys::nix_err_code(self.inner.as_ptr()) }; - if err != sys::nix_err_NIX_OK { - // msgp is a borrowed pointer (pointing into the context), so we don't need to free it - let msgp = unsafe { sys::nix_err_msg(null_mut(), self.inner.as_ptr(), null_mut()) }; - // Turn the i8 pointer into a Rust string by copying - let msg: &str = unsafe { core::ffi::CStr::from_ptr(msgp).to_str()? }; - bail!("{}", msg); + // let err = unsafe { sys::nix_err_code(self.inner.as_ptr()) }; + // if err != sys::nix_err_NIX_OK { + // // msgp is a borrowed pointer (pointing into the context), so we don't need to free it + // let msgp = unsafe { sys::nix_err_msg(null_mut(), self.inner.as_ptr(), null_mut()) }; + // // Turn the i8 pointer into a Rust string by copying + // let msg: &str = unsafe { core::ffi::CStr::from_ptr(msgp).to_str()? }; + // bail!("{}", msg); + // } + // Ok(()) + + let result = self.get_code(); + match result { + Ok(()) => None, + Err(err) => Some(NixError { + err, + name: self.get_name()?, + msg: self.get_msg()?, + info_msg: self.get_info_msg()?, + }), } - Ok(()) } - pub fn pop(&mut self) -> Result<(), NixErrorCode> { - let result = self.peak(); - if result.is_err() { + pub fn pop(&mut self) -> Option { + let error = self.peak(); + if error.is_some() { self.clear(); } - result + error } pub fn clear(&mut self) { @@ -137,19 +172,19 @@ impl ErrorContext { NixErrorCode::from(unsafe { sys::nix_err_code(self.as_ptr()) }, "nix_err_code") } - pub(crate) fn get_name(&self, result: NixResult<()>) -> Option { - match result { - Err(_) => unsafe { - let ctx = null_mut(); - wrap_libnix_string_callback("nix_err_name", |callback, user_data| { - sys::nix_err_name(ctx, self.as_ptr(), Some(callback), user_data) - }) - .ok() - }, - Ok(_) => None, + /// Returns None if no [self.code] is [sys::nix_err_NIX_OK]. + pub(crate) fn get_name(&self) -> Option { + unsafe { + let ctx = null_mut(); + // NOTE: an Err here only occurs when "Last error was not a nix error" + wrap_libnix_string_callback("nix_err_name", |callback, user_data| { + sys::nix_err_name(ctx, self.as_ptr(), Some(callback), user_data) + }) + .ok() } } + /// Returns None if no [self.code] is [sys::nix_err_NIX_OK]. /// # Note /// On failure [sys::nix_err_name] does the following if the error /// has the error code [sys::nix_err_NIX_OK]: @@ -159,26 +194,33 @@ impl ErrorContext { /// ``` /// Hence we can just test whether the returned pointer is a `NULL` pointer, /// and avoid passing in a [sys::nix_c_context] struct. - pub(crate) fn get_msg(&self, result: NixResult<()>) -> Option { - match result { - Err(_) => unsafe { - let ctx = null_mut(); - let msg_ptr: *const c_char = sys::nix_err_msg(ctx, self.as_ptr(), null_mut()); + pub(crate) fn get_msg(&self) -> Option { + unsafe { + let ctx = null_mut(); + let msg_ptr: *const c_char = sys::nix_err_msg(ctx, self.as_ptr(), null_mut()); - if msg_ptr.is_null() { - return None; - } + if msg_ptr.is_null() { + return None; + } - match CStr::from_ptr(msg_ptr).to_str() { - Ok(msg_str) => Some(msg_str.to_string()), - Err(_) => None, - } - }, - Ok(_) => None, + match CStr::from_ptr(msg_ptr).to_str() { + Ok(msg_str) => Some(msg_str.to_string()), + Err(_) => None, + } } } - pub(crate) fn get_info_msg(&self) -> Option {} + /// Returns None if no [self.code] is [sys::nix_err_NIX_OK]. + pub(crate) fn get_info_msg(&self) -> Option { + unsafe { + let ctx = null_mut(); + // NOTE: an Err here only occurs when "Last error was not a nix error" + wrap_libnix_string_callback("nix_err_name", |callback, user_data| { + sys::nix_err_info_msg(ctx, self.as_ptr(), Some(callback), user_data) + }) + .ok() + } + } pub fn check_one_call_or_key_none(&mut self, f: F) -> Result, NixErrorCode> where diff --git a/nixide/src/error.rs b/nixide/src/error.rs index 1d0ce87..b870591 100644 --- a/nixide/src/error.rs +++ b/nixide/src/error.rs @@ -6,7 +6,7 @@ use crate::sys; /// Standard (nix_err) and some additional error codes /// produced by the libnix C API. -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum NixErrorCode { /// A generic Nix error occurred. /// From 3ebb96db630f5b171d0639b7101a800e904a42ef Mon Sep 17 00:00:00 2001 From: _cry64 Date: Thu, 19 Mar 2026 02:51:14 +1000 Subject: [PATCH 293/306] remove check_one_call_or_key_none --- nixide/src/context.rs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/nixide/src/context.rs b/nixide/src/context.rs index 6fe3011..94ab5f6 100644 --- a/nixide/src/context.rs +++ b/nixide/src/context.rs @@ -221,19 +221,6 @@ impl ErrorContext { .ok() } } - - pub fn check_one_call_or_key_none(&mut self, f: F) -> Result, NixErrorCode> - where - F: FnOnce(*mut sys::nix_c_context) -> T, - { - let t = f(unsafe { self.as_ptr() }); - if unsafe { sys::nix_err_code(self.inner.as_ptr()) == sys::nix_err_NIX_ERR_KEY } { - self.clear(); - return Ok(None); - } - self.pop()?; - Ok(Some(t)) - } } impl Drop for ErrorContext { From 3cdfae01b950c60b951e98a10ca501123a5122ff Mon Sep 17 00:00:00 2001 From: _cry64 Date: Thu, 19 Mar 2026 03:53:01 +1000 Subject: [PATCH 294/306] fix EvalStateBuilderExt --- nixide/src/flake/eval_state_builder_ext.rs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/nixide/src/flake/eval_state_builder_ext.rs b/nixide/src/flake/eval_state_builder_ext.rs index 28d095e..3c3acac 100644 --- a/nixide/src/flake/eval_state_builder_ext.rs +++ b/nixide/src/flake/eval_state_builder_ext.rs @@ -1,17 +1,14 @@ +use super::FlakeSettings; +use crate::{EvalStateBuilder, NixErrorCode}; + pub trait EvalStateBuilderExt { /// Configures the eval state to provide flakes features such as `builtins.getFlake`. - fn flakes( - self, - settings: &FlakeSettings, - ) -> Result; + fn flakes(self, settings: &FlakeSettings) -> Result; } -impl EvalStateBuilderExt for nix_bindings_expr::eval_state::EvalStateBuilder { + +impl EvalStateBuilderExt for EvalStateBuilder { /// Configures the eval state to provide flakes features such as `builtins.getFlake`. - fn flakes( - mut self, - settings: &FlakeSettings, - ) -> Result { - settings.add_to_eval_state_builder(&mut self)?; - Ok(self) + fn flakes(mut self, settings: &FlakeSettings) -> Result { + settings.add_to_eval_state_builder(&mut self).map(|_| self) } } From 802aebf606571874fafe811d29a6b4b93af9e609 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Thu, 19 Mar 2026 03:53:09 +1000 Subject: [PATCH 295/306] fix FlakeLockFlags + FlakeSettings --- nixide/src/flake/flake_lock_flags.rs | 164 ++++++++++++++++++++------- nixide/src/flake/flake_settings.rs | 2 +- 2 files changed, 121 insertions(+), 45 deletions(-) diff --git a/nixide/src/flake/flake_lock_flags.rs b/nixide/src/flake/flake_lock_flags.rs index 464cc41..d006fc5 100644 --- a/nixide/src/flake/flake_lock_flags.rs +++ b/nixide/src/flake/flake_lock_flags.rs @@ -1,61 +1,137 @@ +use std::ffi::CString; +use std::ptr::NonNull; + +use super::{FlakeReference, FlakeSettings}; +use crate::sys; +use crate::{ErrorContext, NixErrorCode}; + +#[derive(Debug, Clone)] +pub enum FlakeLockMode { + /// Configures [LockedFlake::lock] to make incremental changes to the lock file as needed. Changes are written to file. + WriteAsNeeded, + + /// Like [FlakeLockMode::WriteAsNeeded], but does not write to the lock file. + Virtual, + + /// Make [LockedFlake::lock] check if the lock file is up to date. If not, an error is returned. + Check, +} + /// Parameters that affect the locking of a flake. pub struct FlakeLockFlags { - pub(crate) ptr: *mut raw::flake_lock_flags, + pub(crate) inner: NonNull, } impl Drop for FlakeLockFlags { fn drop(&mut self) { unsafe { - raw::flake_lock_flags_free(self.ptr); + sys::nix_flake_lock_flags_free(self.as_ptr()); } } } impl FlakeLockFlags { - pub fn new(settings: &FlakeSettings) -> Result { - let mut ctx = Context::new(); - let s = unsafe { context::check_call!(raw::flake_lock_flags_new(&mut ctx, settings.ptr)) }?; - Ok(FlakeLockFlags { ptr: s }) + // XXX: TODO: what is the default FlakeLockMode? + pub fn new(settings: &FlakeSettings) -> Result { + ErrorContext::new().and_then(|ctx| { + NonNull::new(unsafe { sys::nix_flake_lock_flags_new(ctx.as_ptr(), settings.as_ptr()) }) + .ok_or(NixErrorCode::NulError { + location: "nix_flake_lock_flags_new", + }) + .map(|inner| FlakeLockFlags { inner }) + }) } + + pub(crate) fn as_ptr(&self) -> *mut sys::nix_flake_lock_flags { + self.inner.as_ptr() + } + + pub fn set_lock_mode(&mut self, mode: &FlakeLockMode) -> Result<(), NixErrorCode> { + ErrorContext::new().and_then(|ctx| unsafe { + NixErrorCode::from( + match mode { + FlakeLockMode::WriteAsNeeded => { + sys::nix_flake_lock_flags_set_mode_write_as_needed( + ctx.as_ptr(), + self.as_ptr(), + ) + } + FlakeLockMode::Virtual => { + sys::nix_flake_lock_flags_set_mode_virtual(ctx.as_ptr(), self.as_ptr()) + } + FlakeLockMode::Check => { + sys::nix_flake_lock_flags_set_mode_check(ctx.as_ptr(), self.as_ptr()) + } + }, + "nix_flake_lock_flags_set_mode_check", + ) + }) + } + /// Configures [LockedFlake::lock] to make incremental changes to the lock file as needed. Changes are written to file. - pub fn set_mode_write_as_needed(&mut self) -> Result<()> { - let mut ctx = Context::new(); - unsafe { - context::check_call!(raw::flake_lock_flags_set_mode_write_as_needed( - &mut ctx, self.ptr - )) - }?; - Ok(()) - } + // pub fn set_mode_write_as_needed(&mut self) -> Result<(), NixErrorCode> { + // ErrorContext::new().and_then(|ctx| { + // NixErrorCode::from( + // unsafe { + // sys::nix_flake_lock_flags_set_mode_write_as_needed(ctx.as_ptr(), self.as_ptr()) + // }, + // "nix_flake_lock_flags_set_mode_write_as_needed", + // ) + // }) + // } + /// Make [LockedFlake::lock] check if the lock file is up to date. If not, an error is returned. - pub fn set_mode_check(&mut self) -> Result<()> { - let mut ctx = Context::new(); - unsafe { context::check_call!(raw::flake_lock_flags_set_mode_check(&mut ctx, self.ptr)) }?; - Ok(()) - } + // pub fn set_mode_check(&mut self) -> Result<(), NixErrorCode> { + // ErrorContext::new().and_then(|ctx| { + // NixErrorCode::from( + // unsafe { sys::nix_flake_lock_flags_set_mode_check(ctx.as_ptr(), self.as_ptr()) }, + // "nix_flake_lock_flags_set_mode_check", + // ) + // }) + // } + /// Like `set_mode_write_as_needed`, but does not write to the lock file. - pub fn set_mode_virtual(&mut self) -> Result<()> { - let mut ctx = Context::new(); - unsafe { - context::check_call!(raw::flake_lock_flags_set_mode_virtual(&mut ctx, self.ptr)) - }?; - Ok(()) - } - /// Adds an input override to the lock file that will be produced. The [LockedFlake::lock] operation will not write to the lock file. - pub fn add_input_override( + // pub fn set_mode_virtual(&mut self) -> Result<(), NixErrorCode> { + // ErrorContext::new().and_then(|ctx| { + // NixErrorCode::from( + // unsafe { sys::nix_flake_lock_flags_set_mode_virtual(ctx.as_ptr(), self.as_ptr()) }, + // "nix_flake_lock_flags_set_mode_virtual", + // ) + // }) + // } + + /// Adds an input override to the lock file that will be produced. + /// The [LockedFlake::lock] operation will not write to the lock file. + /// + /// # Warning + /// + /// Calling this function will implicitly set the [FlakeLockMode] to + /// [FlakeLockMode::Virtual] if `self.mode` is not [FlakeLockMode::Check]. + /// + /// # Arguments + /// + /// * `path` - The input name/path to override (must not be empty) + /// * `flake_ref` - The flake reference to use as the override + pub fn override_input( &mut self, - override_path: &str, - override_ref: &FlakeReference, - ) -> Result<()> { - let mut ctx = Context::new(); - unsafe { - context::check_call!(raw::flake_lock_flags_add_input_override( - &mut ctx, - self.ptr, - CString::new(override_path) - .context("Failed to create CString for override_path")? - .as_ptr(), - override_ref.ptr.as_ptr() - )) - }?; - Ok(()) + path: &str, + flakeref: &FlakeReference, + ) -> Result<(), NixErrorCode> { + let input_path = NixErrorCode::from_nulerror( + CString::new(path), + "nixide::FlakeLockArgs::override_input", + )?; + + ErrorContext::new().and_then(|ctx| unsafe { + NixErrorCode::from( + unsafe { + sys::nix_flake_lock_flags_add_input_override( + ctx.as_ptr(), + self.as_ptr(), + input_path.as_ptr(), + flakeref.as_ptr(), + ) + }, + "nix_flake_lock_flags_add_input_override", + ) + }) } } diff --git a/nixide/src/flake/flake_settings.rs b/nixide/src/flake/flake_settings.rs index 3cc2082..687b56d 100644 --- a/nixide/src/flake/flake_settings.rs +++ b/nixide/src/flake/flake_settings.rs @@ -19,7 +19,7 @@ impl FlakeSettings { Ok(FlakeSettings { inner }) } - fn add_to_eval_state_builder( + pub(super) fn add_to_eval_state_builder( &self, builder: &mut EvalStateBuilder, ) -> Result<(), NixErrorCode> { From e437571af1f51feb1936ce1cfe964bf17db742bc Mon Sep 17 00:00:00 2001 From: _cry64 Date: Fri, 20 Mar 2026 11:36:12 +1000 Subject: [PATCH 296/306] update flake.nix description --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index c8c63ec..f52bd4c 100644 --- a/flake.nix +++ b/flake.nix @@ -1,5 +1,5 @@ { - description = "Wire on your TTYs just feels better!"; + description = "rust wrapper for libnix"; inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11"; From fcc8fede1130cb7d9cee58ef82bf8d7bb656f36c Mon Sep 17 00:00:00 2001 From: _cry64 Date: Sat, 21 Mar 2026 10:26:27 +1000 Subject: [PATCH 297/306] this is how i stash right? /s --- Cargo.lock | 7 + TODO.md | 18 ++ nixide/Cargo.toml | 1 + nixide/src/context.rs | 233 -------------- nixide/src/error.rs | 183 ----------- nixide/src/errors/context.rs | 300 ++++++++++++++++++ nixide/src/errors/error.rs | 175 ++++++++++ nixide/src/errors/mod.rs | 9 + nixide/src/errors/nix_error.rs | 145 +++++++++ nixide/src/expr/evalstate.rs | 111 +++---- nixide/src/expr/evalstatebuilder.rs | 41 +-- nixide/src/expr/tests.rs | 18 +- nixide/src/expr/value.rs | 196 ++++++------ nixide/src/expr/valuetype.rs | 8 +- nixide/src/flake/eval_state_builder_ext.rs | 6 +- nixide/src/flake/fetchers_settings.rs | 12 +- nixide/src/flake/flake_lock_flags.rs | 98 +++--- nixide/src/flake/flake_reference.rs | 55 ++-- .../src/flake/flake_reference_parse_flags.rs | 55 ++-- nixide/src/flake/flake_settings.rs | 58 ++-- nixide/src/flake/locked_flake.rs | 105 +++--- nixide/src/flake/mod.rs | 2 +- nixide/src/lib.rs | 8 +- nixide/src/store/mod.rs | 6 +- nixide/src/store/path.rs | 73 +++-- nixide/src/util/bindings.rs | 53 ++-- nixide/src/util/cchar_nix_ext.rs | 37 +++ nixide/src/util/mod.rs | 24 +- nixide/src/util/panic.rs | 17 + nixide/src/util/wrappers.rs | 16 + rustfmt.toml | 5 + 31 files changed, 1217 insertions(+), 858 deletions(-) delete mode 100644 nixide/src/context.rs delete mode 100644 nixide/src/error.rs create mode 100644 nixide/src/errors/context.rs create mode 100644 nixide/src/errors/error.rs create mode 100644 nixide/src/errors/mod.rs create mode 100644 nixide/src/errors/nix_error.rs create mode 100644 nixide/src/util/cchar_nix_ext.rs create mode 100644 nixide/src/util/panic.rs create mode 100644 nixide/src/util/wrappers.rs create mode 100644 rustfmt.toml diff --git a/Cargo.lock b/Cargo.lock index 5346be7..ff32633 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -192,6 +192,7 @@ version = "0.1.0" dependencies = [ "nixide-sys", "serial_test", + "stdext", ] [[package]] @@ -383,6 +384,12 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +[[package]] +name = "stdext" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4af28eeb7c18ac2dbdb255d40bee63f203120e1db6b0024b177746ebec7049c1" + [[package]] name = "syn" version = "2.0.117" diff --git a/TODO.md b/TODO.md index 075a528..e8fdabf 100644 --- a/TODO.md +++ b/TODO.md @@ -2,3 +2,21 @@ - [ ] replace all `use nixide_sys as sys;` -> `use crate::sys;` - [ ] store NonNull pointers in structs! - [ ] improve documentation situation on context.rs + +- [ ] rename `as_ptr()` to `as_inner_ptr()` or `inner_ptr()`? +- [ ] ^^^ this fn should be added to a trait (maybe just `trait NixStructWrapper : AsPtr { ... }`) +- [ ] ^^^ also make `as_ptr()` public + +- [ ] add mutexs and make the library thread safe!! + +- [ ] grep all `self.inner.as_ptr()` calls and replace them with `self.as_ptr()` + + +- [ ] `ErrorContext::peak` should return `Result<(), NixideError>` **not** `Option` +- [ ] `self.expect_type` should instead be a macro to preserve the trace macro location + +- [ ] make `Value` an enum instead because like duhh + +- [ ] ensure we're always calling `ctx.peak()` unless it's ACTUALLY not necessary + +- [ ] replace *most* calls to `ErrorContext::peak()` with `ErrorContext::pop()` diff --git a/nixide/Cargo.toml b/nixide/Cargo.toml index fc0fa79..e97d5fe 100644 --- a/nixide/Cargo.toml +++ b/nixide/Cargo.toml @@ -25,6 +25,7 @@ gc = [] [dependencies] nixide-sys = { path = "../nixide-sys", version = "0.1.0" } +stdext = "0.3.3" [dev-dependencies] serial_test = "3.4.0" diff --git a/nixide/src/context.rs b/nixide/src/context.rs deleted file mode 100644 index 94ab5f6..0000000 --- a/nixide/src/context.rs +++ /dev/null @@ -1,233 +0,0 @@ -// XXX: TODO: create wrappers methods to access more than just `info->msg()` -// struct ErrorInfo -// { -// Verbosity level; -// HintFmt msg; -// std::shared_ptr pos; -// std::list traces; -// /** -// * Some messages are generated directly by expressions; notably `builtins.warn`, `abort`, `throw`. -// * These may be rendered differently, so that users can distinguish them. -// */ -// bool isFromExpr = false; - -// /** -// * Exit status. -// */ -// unsigned int status = 1; - -// Suggestions suggestions; - -// static std::optional programName; -// }; - -use std::ffi::{c_char, CStr}; -use std::ptr::{null_mut, NonNull}; - -use crate::error::NixErrorCode; -use crate::sys; -use crate::util::bindings::wrap_libnix_string_callback; - -// XXX: TODO: change this to a `Result` -type NixResult = Result; - -#[derive(Debug, Clone)] -pub struct NixError { - pub err: NixErrorCode, - pub name: String, - pub msg: String, - pub info_msg: String, -} - -/// This object stores error state. -/// -/// Passed as a first parameter to functions that can fail, to store error -/// information. -/// -/// # Warning -/// -/// These can be reused between different function calls, -/// but make sure not to use them for multiple calls simultaneously -/// (which can happen in callbacks). -/// -/// # `libnix` API Internals -/// -/// ```cpp -/// struct nix_c_context -/// { -/// nix_err last_err_code = NIX_OK; -/// /* WARNING: The last error message. Always check last_err_code. -/// WARNING: This may not have been cleared, so that clearing is fast. */ -/// std::optional last_err = {}; -/// std::optional info = {}; -/// std::string name = ""; -/// }; -/// ``` -/// -/// The [sys::nix_c_context] struct is laid out so that it can also be -/// cast to a [sys::nix_err] to inspect directly: -/// ```c -/// assert(*(nix_err*)ctx == NIX_OK); -/// ``` -/// -pub struct ErrorContext { - inner: NonNull, -} - -impl ErrorContext { - /// Create a new Nix context. - /// - /// This initializes the Nix C API context and the required libraries. - /// - /// # Errors - /// - /// Returns an error if context creation or library initialization fails. - pub fn new() -> Result { - // SAFETY: nix_c_context_create is safe to call - let ctx_ptr = unsafe { sys::nix_c_context_create() }; - let ctx = ErrorContext { - inner: NonNull::new(ctx_ptr).ok_or(NixErrorCode::NullPtr { - location: "nix_c_context_create", - })?, - }; - - // Initialize required libraries - // XXX: TODO: move this to a separate init function (maybe a Nix::init() function) - // unsafe { - // NixErrorCode::from( - // sys::nix_libutil_init(ctx.inner.as_ptr()), - // "nix_libutil_init", - // )?; - // NixErrorCode::from( - // sys::nix_libstore_init(ctx.inner.as_ptr()), - // "nix_libstore_init", - // )?; - // NixErrorCode::from( - // sys::nix_libexpr_init(ctx.inner.as_ptr()), - // "nix_libexpr_init", - // )?; - // }; - - Ok(ctx) - } - - /// Get the raw context pointer. - /// - /// # Safety - /// - /// Although this function isn't inherently `unsafe`, it is - /// marked as such intentionally to force calls to be wrapped - /// in `unsafe` blocks for clarity. - pub(crate) unsafe fn as_ptr(&self) -> *mut sys::nix_c_context { - self.inner.as_ptr() - } - - /// Check the error code and return an error if it's not `NIX_OK`. - /// - /// We recommend to use `check_call!` if possible. - pub fn peak(&self) -> Option { - // NixError::from( unsafe { sys::nix_err_code(self.as_ptr())}, ""); - - // let err = unsafe { sys::nix_err_code(self.inner.as_ptr()) }; - // if err != sys::nix_err_NIX_OK { - // // msgp is a borrowed pointer (pointing into the context), so we don't need to free it - // let msgp = unsafe { sys::nix_err_msg(null_mut(), self.inner.as_ptr(), null_mut()) }; - // // Turn the i8 pointer into a Rust string by copying - // let msg: &str = unsafe { core::ffi::CStr::from_ptr(msgp).to_str()? }; - // bail!("{}", msg); - // } - // Ok(()) - - let result = self.get_code(); - match result { - Ok(()) => None, - Err(err) => Some(NixError { - err, - name: self.get_name()?, - msg: self.get_msg()?, - info_msg: self.get_info_msg()?, - }), - } - } - - pub fn pop(&mut self) -> Option { - let error = self.peak(); - if error.is_some() { - self.clear(); - } - error - } - - pub fn clear(&mut self) { - unsafe { - // NOTE: previous nixops4 used the line: (maybe for compatability with old versions?) - // sys::nix_set_err_msg(self.inner.as_ptr(), sys::nix_err_NIX_OK, c"".as_ptr()); - sys::nix_clear_err(self.as_ptr()); - } - } - - /// - /// Never fails - pub(crate) fn get_code(&self) -> Result<(), NixErrorCode> { - NixErrorCode::from(unsafe { sys::nix_err_code(self.as_ptr()) }, "nix_err_code") - } - - /// Returns None if no [self.code] is [sys::nix_err_NIX_OK]. - pub(crate) fn get_name(&self) -> Option { - unsafe { - let ctx = null_mut(); - // NOTE: an Err here only occurs when "Last error was not a nix error" - wrap_libnix_string_callback("nix_err_name", |callback, user_data| { - sys::nix_err_name(ctx, self.as_ptr(), Some(callback), user_data) - }) - .ok() - } - } - - /// Returns None if no [self.code] is [sys::nix_err_NIX_OK]. - /// # Note - /// On failure [sys::nix_err_name] does the following if the error - /// has the error code [sys::nix_err_NIX_OK]: - /// ```c - /// nix_set_err_msg(context, NIX_ERR_UNKNOWN, "No error message"); - /// return nullptr; - /// ``` - /// Hence we can just test whether the returned pointer is a `NULL` pointer, - /// and avoid passing in a [sys::nix_c_context] struct. - pub(crate) fn get_msg(&self) -> Option { - unsafe { - let ctx = null_mut(); - let msg_ptr: *const c_char = sys::nix_err_msg(ctx, self.as_ptr(), null_mut()); - - if msg_ptr.is_null() { - return None; - } - - match CStr::from_ptr(msg_ptr).to_str() { - Ok(msg_str) => Some(msg_str.to_string()), - Err(_) => None, - } - } - } - - /// Returns None if no [self.code] is [sys::nix_err_NIX_OK]. - pub(crate) fn get_info_msg(&self) -> Option { - unsafe { - let ctx = null_mut(); - // NOTE: an Err here only occurs when "Last error was not a nix error" - wrap_libnix_string_callback("nix_err_name", |callback, user_data| { - sys::nix_err_info_msg(ctx, self.as_ptr(), Some(callback), user_data) - }) - .ok() - } - } -} - -impl Drop for ErrorContext { - fn drop(&mut self) { - // SAFETY: We own the context and it's valid until drop - unsafe { - sys::nix_c_context_free(self.inner.as_ptr()); - } - } -} diff --git a/nixide/src/error.rs b/nixide/src/error.rs deleted file mode 100644 index b870591..0000000 --- a/nixide/src/error.rs +++ /dev/null @@ -1,183 +0,0 @@ -use std::ffi::NulError; -use std::fmt::{Display, Formatter, Result as FmtResult}; -use std::ptr::NonNull; - -use crate::sys; - -/// Standard (nix_err) and some additional error codes -/// produced by the libnix C API. -#[derive(Debug, Clone)] -pub enum NixErrorCode { - /// A generic Nix error occurred. - /// - /// # Reason - /// - /// This error code is returned when a generic Nix error occurred during the - /// function execution. - NixError { location: &'static str }, - - /// An overflow error occurred. - /// - /// # Reason - /// - /// This error code is returned when an overflow error occurred during the - /// function execution. - Overflow { location: &'static str }, - - /// A key/index access error occurred in C API functions. - /// - /// # Reason - /// - /// This error code is returned when accessing a key, index, or identifier that - /// does not exist in C API functions. Common scenarios include: - /// - Setting keys that don't exist (nix_setting_get, nix_setting_set) - /// - List indices that are out of bounds (nix_get_list_byidx*) - /// - Attribute names that don't exist (nix_get_attr_byname*) - /// - Attribute indices that are out of bounds (nix_get_attr_byidx*, nix_get_attr_name_byidx) - /// - /// This error typically indicates incorrect usage or assumptions about data structure - /// contents, rather than internal Nix evaluation errors. - /// - /// # Note - /// - /// This error code should ONLY be returned by C API functions themselves, - /// not by underlying Nix evaluation. For example, evaluating `{}.foo` in Nix - /// will throw a normal error (NIX_ERR_NIX_ERROR), not NIX_ERR_KEY. - KeyNotFound { - location: &'static str, - key: Option, - }, - - /// An unknown error occurred. - /// - /// # Reason - /// - /// This error code is returned when an unknown error occurred during the - /// function execution. - Unknown { - location: &'static str, - reason: String, - }, - - /// An undocumented error occurred. - /// - /// # Reason - /// - /// The libnix C API defines `enum nix_err` as a signed integer value. - /// In the (unexpected) event libnix returns an error code with an - /// invalid enum value, or one I new addition I didn't know existed, - /// then an [NixError::Undocumented] is considered to have occurred. - Undocumented { - location: &'static str, - err_code: sys::nix_err, - }, - - ////////////////////// - // NON-STANDARD ERRORS - ////////////////////// - /// NulError - NulError { location: &'static str }, - - /// Non-standard - NullPtr { location: &'static str }, - - /// Invalid Argument - InvalidArg { - location: &'static str, - reason: &'static str, // XXX: TODO: make this a String - }, - - /// Invalid Type - InvalidType { - location: &'static str, - expected: &'static str, - got: String, - }, -} - -impl NixErrorCode { - pub fn from(err_code: sys::nix_err, location: &'static str) -> Result<(), NixErrorCode> { - #[allow(nonstandard_style)] - match err_code { - sys::nix_err_NIX_OK => Ok(()), - - sys::nix_err_NIX_ERR_OVERFLOW => Err(NixErrorCode::Overflow { location }), - sys::nix_err_NIX_ERR_KEY => Err(NixErrorCode::KeyNotFound { - location, - key: None, - }), - sys::nix_err_NIX_ERR_NIX_ERROR => Err(NixErrorCode::NixError { location }), - - sys::nix_err_NIX_ERR_UNKNOWN => Err(NixErrorCode::Unknown { - location, - reason: "Unknown error occurred".to_string(), - }), - _ => Err(NixErrorCode::Undocumented { location, err_code }), - } - } - - pub fn from_nulerror( - result: Result, - location: &'static str, - ) -> Result { - result.or(Err(NixErrorCode::NulError { location })) - } - - pub fn new_nonnull(ptr: *mut T, location: &'static str) -> Result, Self> - where - T: Sized, - { - NonNull::new(ptr).ok_or(NixErrorCode::NullPtr { location }) - } -} - -impl std::error::Error for NixErrorCode {} - -impl Display for NixErrorCode { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - let msg = match self { - NixErrorCode::NixError { location } => { - format!("[libnix] Generic error (at location `{location}`)") - } - NixErrorCode::Overflow { location } => { - format!("[libnix] Overflow error (at location `{location}`)") - } - NixErrorCode::KeyNotFound { location, key } => format!( - "[libnix] Key not found {} (at location `{location}`)", - match key { - Some(key) => format!("`{key}`"), - None => "".to_owned(), - } - ), - - NixErrorCode::Unknown { location, reason } => { - format!("Unknown error \"{reason}\" (at location `{location}`)") - } - NixErrorCode::Undocumented { location, err_code } => { - format!( - "[libnix] An undocumented nix_err was returned with {err_code} (at location `{location}`)" - ) - } - - NixErrorCode::NulError { location } => { - format!("Nul error (at location `{location}`)") - } - NixErrorCode::NullPtr { location } => { - format!("[libnix] Null pointer (at location `{location}`)") - } - - NixErrorCode::InvalidArg { location, reason } => { - format!("Invalid argument \"{reason}\" (at location `{location}`)") - } - NixErrorCode::InvalidType { - location, - expected, - got, - } => { - format!("Invalid type, expected \"{expected}\" ${got} (at location `{location}`)") - } - }; - - write!(f, "{msg}") - } -} diff --git a/nixide/src/errors/context.rs b/nixide/src/errors/context.rs new file mode 100644 index 0000000..91c5cec --- /dev/null +++ b/nixide/src/errors/context.rs @@ -0,0 +1,300 @@ +// XXX: TODO: create wrappers methods to access more than just `info->msg()` +// struct ErrorInfo +// { +// Verbosity level; +// HintFmt msg; +// std::shared_ptr pos; +// std::list traces; +// /** +// * Some messages are generated directly by expressions; notably `builtins.warn`, `abort`, `throw`. +// * These may be rendered differently, so that users can distinguish them. +// */ +// bool isFromExpr = false; + +// /** +// * Exit status. +// */ +// unsigned int status = 1; + +// Suggestions suggestions; + +// static std::optional programName; +// }; + +use std::ffi::c_uint; +use std::ptr::NonNull; + +use super::NixideError; +use crate::sys; +use crate::util::bindings::wrap_libnix_string_callback; +use crate::util::wrappers::AsInnerPtr; +use crate::util::CCharPtrNixExt; + +/// This object stores error state. +/// +/// Passed as a first parameter to functions that can fail, to store error +/// information. +/// +/// # Warning +/// +/// These can be reused between different function calls, +/// but make sure not to use them for multiple calls simultaneously +/// (which can happen in callbacks). +/// +/// # Nix C++ API Internals +/// +/// ```cpp +/// struct nix_c_context +/// { +/// nix_err last_err_code = NIX_OK; +/// /* WARNING: The last error message. Always check last_err_code. +/// WARNING: This may not have been cleared, so that clearing is fast. */ +/// std::optional last_err = {}; +/// std::optional info = {}; +/// std::string name = ""; +/// }; +/// ``` +/// +/// The [sys::nix_c_context] struct is laid out so that it can also be +/// cast to a [sys::nix_err] to inspect directly: +/// ```c +/// assert(*(nix_err*)ctx == NIX_OK); +/// ``` +pub(crate) struct ErrorContext { + // XXX: TODO: add a RwLock to this (maybe Arc? or is that excessive?) + inner: NonNull, +} + +impl AsInnerPtr for ErrorContext { + unsafe fn as_ptr(&self) -> *mut sys::nix_c_context { + self.inner.as_ptr() + } +} + +impl ErrorContext { + /// Create a new error context. + /// + /// # Errors + /// + /// Returns an error if no memory can be allocated for + /// the underlying [sys::nix_c_context] struct. + pub fn new() -> Self { + match NonNull::new(unsafe { sys::nix_c_context_create() }) { + Some(inner) => ErrorContext { inner }, + None => panic!("[nixide] CRITICAL FAILURE: Out-Of-Memory condition reached - `sys::nix_c_context_create` allocation failed!"), + } + + // Initialize required libraries + // XXX: TODO: move this to a separate init function (maybe a Nix::init() function) + // unsafe { + // NixErrorCode::from( + // sys::nix_libutil_init(ctx.inner.as_ptr()), + // "nix_libutil_init", + // )?; + // NixErrorCode::from( + // sys::nix_libstore_init(ctx.inner.as_ptr()), + // "nix_libstore_init", + // )?; + // NixErrorCode::from( + // sys::nix_libexpr_init(ctx.inner.as_ptr()), + // "nix_libexpr_init", + // )?; + // }; + } + + /// Check the error code and return an error if it's not `NIX_OK`. + pub fn peak(&self) -> Option { + NixideError::from_error_context(self) + } + + /// + /// Equivalent to running `self.peak()` then `self.clear()` + pub fn pop(&mut self) -> Option { + self.get_err().and_then(|_| { + let error = self.peak(); + self.clear(); + error + }) + } + + /// # Nix C++ API Internals + /// + /// ```cpp + /// void nix_clear_err(nix_c_context * context) + /// { + /// if (context) + /// context->last_err_code = NIX_OK; + /// } + /// ``` + /// + /// `nix_clear_err` only modifies the `last_err_code`, it does not + /// clear all attributes of a `nix_c_context` struct. Hence all uses + /// of `nix_c_context` must be careful to check the `last_err_code` regularly. + pub fn clear(&mut self) { + unsafe { + // NOTE: previous nixops4 used the line: (maybe for compatability with old versions?) + // sys::nix_set_err_msg(self.inner.as_ptr(), sys::nix_err_NIX_OK, c"".as_ptr()); + sys::nix_clear_err(self.as_ptr()); + } + } + + /// Returns [None] if [self.code] is [sys::nix_err_NIX_OK], and [Some] otherwise. + /// + /// # Nix C++ API Internals + /// ```cpp + /// nix_err nix_err_code(const nix_c_context * read_context) + /// { + /// return read_context->last_err_code; + /// } + /// ``` + /// This function **never fails**. + pub(super) fn get_err(&self) -> Option { + let err = unsafe { sys::nix_err_code(self.as_ptr()) }; + + if err == sys::nix_err_NIX_OK { + None + } else { + Some(err) + } + } + + /// Returns [None] if [self.code] is [sys::nix_err_NIX_OK], and [Some] otherwise. + /// + /// # Nix C++ API Internals + /// ```cpp + /// const char * nix_err_msg(nix_c_context * context, const nix_c_context * read_context, unsigned int * n) + /// { + /// if (context) + /// context->last_err_code = NIX_OK; + /// if (read_context->last_err && read_context->last_err_code != NIX_OK) { + /// if (n) + /// *n = read_context->last_err->size(); + /// return read_context->last_err->c_str(); + /// } + /// nix_set_err_msg(context, NIX_ERR_UNKNOWN, "No error message"); + /// return nullptr; + /// } + /// ``` + /// + /// # Note + /// On failure [sys::nix_err_name] does the following if the error + /// has the error code [sys::nix_err_NIX_OK]: + /// ```cpp + /// nix_set_err_msg(context, NIX_ERR_UNKNOWN, "No error message"); + /// return nullptr; + /// ``` + /// Hence we can just test whether the returned pointer is a `NULL` pointer, + /// and avoid passing in a [sys::nix_c_context] struct. + pub(super) fn get_msg(&self) -> Option { + // XXX: TODO: what happens if i DO actually use `null_mut` instead of ErrorContext::new? does rust just panic? + let ctx = ErrorContext::new(); + unsafe { + // NOTE: an Err here only occurs when `self.get_code() == Ok(())` + let mut n: c_uint = 0; + sys::nix_err_msg(ctx.as_ptr(), self.as_ptr(), &mut n) + .to_utf8_string() + .ok() + } + } + + /// Returns [None] if [self.code] is [sys::nix_err_NIX_OK], and [Some] otherwise. + /// + /// # Nix C++ API Internals + /// + /// ```cpp + /// // NOTE(nixide): the implementation of `nix_err_info_msg` is identical to `nix_err_name` + /// nix_err nix_err_info_msg( + /// nix_c_context * context, + /// const nix_c_context * read_context, + /// nix_get_string_callback callback, + /// void * user_data) + /// { + /// if (context) + /// context->last_err_code = NIX_OK; + /// if (read_context->last_err_code != NIX_ERR_NIX_ERROR) { + /// // NOTE(nixide): `nix_set_err_msg` throws a `nix::Error` exception if `context == nullptr` + /// return nix_set_err_msg(context, NIX_ERR_UNKNOWN, "Last error was not a nix error"); + /// } + /// // NOTE(nixide): `call_nix_get_string_callback` always returns `NIX_OK` + /// return call_nix_get_string_callback(read_context->info->msg.str(), callback, user_data); + /// } + /// ``` + /// + /// `nix_err_info_msg` accepts two `nix_c_context*`: + /// * `nix_c_context* context` - errors from the function call are logged here + /// * `const nix_c_context* read_context` - the context to read `info_msg` from + /// + /// `nix_set_err_msg` will cause undefined behaviour if `context` is a null pointer (see below) + /// due to [https://github.com/rust-lang/rust-bindgen/issues/1208]. + /// So we should never assigned it [std::ptr::null_mut]. + /// ```cpp + /// if (context == nullptr) { + /// throw nix::Error("Nix C api error: %s", msg); + /// } + /// ``` + pub(super) fn get_nix_err_name(&self) -> Option { + // XXX: TODO: what happens if i DO actually use `null_mut` instead of ErrorContext::new? does rust just panic? + unsafe { + // NOTE: an Err here only occurs when "Last error was not a nix error" + wrap_libnix_string_callback(|ctx, callback, user_data| { + sys::nix_err_name(ctx.as_ptr(), self.as_ptr(), Some(callback), user_data) + }) + .ok() + } + } + + /// Returns [None] if [self.code] is [sys::nix_err_NIX_OK], and [Some] otherwise. + /// + /// # Nix C++ API Internals + /// + /// ```cpp + /// // NOTE(nixide): the implementation of `nix_err_info_msg` is identical to `nix_err_name` + /// nix_err nix_err_info_msg( + /// nix_c_context * context, + /// const nix_c_context * read_context, + /// nix_get_string_callback callback, + /// void * user_data) + /// { + /// if (context) + /// context->last_err_code = NIX_OK; + /// if (read_context->last_err_code != NIX_ERR_NIX_ERROR) { + /// // NOTE(nixide): `nix_set_err_msg` throws a `nix::Error` exception if `context == nullptr` + /// return nix_set_err_msg(context, NIX_ERR_UNKNOWN, "Last error was not a nix error"); + /// } + /// // NOTE(nixide): `call_nix_get_string_callback` always returns `NIX_OK` + /// return call_nix_get_string_callback(read_context->info->msg.str(), callback, user_data); + /// } + /// ``` + /// + /// `nix_err_info_msg` accepts two `nix_c_context*`: + /// * `nix_c_context* context` - errors from the function call are logged here + /// * `const nix_c_context* read_context` - the context to read `info_msg` from + /// + /// `nix_set_err_msg` will cause undefined behaviour if `context` is a null pointer (see below) + /// due to [https://github.com/rust-lang/rust-bindgen/issues/1208]. + /// So we should never assigned it [std::ptr::null_mut]. + /// ```cpp + /// if (context == nullptr) { + /// throw nix::Error("Nix C api error: %s", msg); + /// } + /// ``` + pub(super) fn get_nix_err_info_msg(&self) -> Option { + // XXX: TODO: what happens if i DO actually use `null_mut` instead of ErrorContext::new? does rust just panic? + unsafe { + // NOTE: an Err here only occurs when "Last error was not a nix error" + wrap_libnix_string_callback(|ctx, callback, user_data| { + sys::nix_err_info_msg(ctx.as_ptr(), self.as_ptr(), Some(callback), user_data) + }) + .ok() + } + } +} + +impl Drop for ErrorContext { + fn drop(&mut self) { + // SAFETY: We own the context and it's valid until drop + unsafe { + sys::nix_c_context_free(self.inner.as_ptr()); + } + } +} diff --git a/nixide/src/errors/error.rs b/nixide/src/errors/error.rs new file mode 100644 index 0000000..b47694e --- /dev/null +++ b/nixide/src/errors/error.rs @@ -0,0 +1,175 @@ +use std::fmt::{Display, Formatter, Result as FmtResult}; + +use super::{ErrorContext, NixError}; +use crate::sys; +use crate::util::panic_issue_call_failed; + +pub type NixideResult = Result; + +#[derive(Debug, Clone)] +pub enum NixideError { + /// # Warning + /// [NixideErrorVariant::NixError] is **not the same** as [sys::nix_err_NIX_ERR_NIX_ERROR], + /// that is instead mapped to [NixError::ExprEval] + NixError { + trace: String, + inner: sys::nix_err, + err: NixError, + msg: String, + }, + + /// Returned if a C string `*const c_char` contained a `\0` byte prematurely. + StringNulByte { trace: String }, + + /// Returned if a C string is not encoded in UTF-8. + StringNotUtf8 { trace: String }, + + /// Equivalent to the standard [std::ffi::NulError] type. + NullPtr { trace: String }, + + /// Invalid Argument + InvalidArg { + trace: String, + name: &'static str, + reason: String, + }, + + /// Invalid Type + InvalidType { + trace: String, + expected: &'static str, + got: String, + }, +} + +macro_rules! new_nixide_error { + (NixError, $inner:expr, $err:expr, $msg:expr) => {{ + NixideError::NixError { + trace: stdext::debug_name!(), + inner: $inner, + err: $err, + msg: $msg, + } + }}; + (StringNulByte) => {{ + NixideError::StringNulByte { + trace: stdext::debug_name!(), + } + }}; + (StringNotUtf8) => {{ + NixideError::StringNotUtf8 { + trace: stdext::debug_name!(), + } + }}; + (NullPtr) => {{ + NixideError::NullPtr { + trace: stdext::debug_name!(), + } + }}; + (InvalidArg, $name:expr, $reason:expr) => {{ + NixideError::InvalidArg { + trace: stdext::debug_name!(), + name: $name, + reason: $reason, + } + }}; + (InvalidType, $expected:expr, $got:expr) => {{ + NixideError::InvalidType { + trace: stdext::debug_name!(), + expected: $expected, + got: $got, + } + }}; +} +pub(crate) use new_nixide_error; + +macro_rules! retrace_nixide_error { + ($x:expr) => {{ + new_nixide_error!($x.err) + }}; +} +pub(crate) use retrace_nixide_error; + +impl NixideError { + /// # Panics + /// + /// This function will panic in the event that `context.get_err() == Some(err) && err == sys::nix_err_NIX_OK` + /// since `nixide::ErrorContext::get_err` is expected to return `None` to indicate `sys::ni_err_NIX_OK`. + /// + /// + /// This function will panic in the event that `value != sys::nix_err_NIX_OK` + /// but that `context.get_code() == sys::nix_err_NIX_OK` + pub(super) fn from_error_context(context: &ErrorContext) -> Option { + let inner = context.get_err()?; + let msg = context.get_msg()?; + + #[allow(nonstandard_style)] + let err = match inner { + sys::nix_err_NIX_OK => unreachable!(), + + sys::nix_err_NIX_ERR_OVERFLOW => NixError::Overflow, + sys::nix_err_NIX_ERR_KEY => NixError::KeyNotFound(None), + sys::nix_err_NIX_ERR_NIX_ERROR => NixError::ExprEval { + name: context + .get_nix_err_name() + .unwrap_or_else(|| panic_issue_call_failed!()), + + info_msg: context + .get_nix_err_info_msg() + .unwrap_or_else(|| panic_issue_call_failed!()), + }, + + sys::nix_err_NIX_ERR_UNKNOWN => NixError::Unknown, + err => NixError::Undocumented(err), + }; + + Some(new_nixide_error!(NixError, inner, err, msg)) + } +} + +impl std::error::Error for NixideError {} + +impl Display for NixideError { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + match self { + NixideError::NixError { + trace, + inner, + err, + msg, + } => { + write!(f, "[nixide ~ {trace}]{err} (nix_err={inner}): {msg}") + } + + NixideError::StringNulByte { trace } => { + write!(f, "[nixide ~ {trace}] Got premature `\\0` (NUL) byte") + } + + NixideError::StringNotUtf8 { trace } => { + write!(f, "[nixide ~ {trace}] Expected UTF-8 encoded string") + } + + NixideError::NullPtr { trace } => write!(f, "[nixide ~ {trace}] Got null pointer"), + + NixideError::InvalidArg { + trace, + name, + reason, + } => { + write!( + f, + "[nixide ~ {trace}] Invalid argument `{name}`: reason \"{reason}\"" + ) + } + + NixideError::InvalidType { + trace, + expected, + got, + } => write!( + f, + "[nixide ~ {trace}] Got invalid type: expected `{expected}` but got `{got}`" + ), + } + } +} diff --git a/nixide/src/errors/mod.rs b/nixide/src/errors/mod.rs new file mode 100644 index 0000000..4ab5ca5 --- /dev/null +++ b/nixide/src/errors/mod.rs @@ -0,0 +1,9 @@ +#[macro_use] +mod error; +mod context; +mod nix_error; + +pub(crate) use context::ErrorContext; +pub(crate) use error::{new_nixide_error, retrace_nixide_error}; +pub use error::{NixideError, NixideResult}; +pub use nix_error::NixError; diff --git a/nixide/src/errors/nix_error.rs b/nixide/src/errors/nix_error.rs new file mode 100644 index 0000000..39e6825 --- /dev/null +++ b/nixide/src/errors/nix_error.rs @@ -0,0 +1,145 @@ +use std::fmt::{Display, Formatter, Result as FmtResult}; + +use crate::sys; + +/// Standard (nix_err) and some additional error codes +/// produced by the libnix C API. +#[derive(Debug, Clone)] +pub enum NixError { + /// A generic Nix error occurred. + /// + /// # Reason + /// + /// This error code is returned when a generic Nix error occurs + /// during nixexpr evaluation. + /// + /// # Nix C++ API Internals + /// + /// ```cpp + /// // `NIX_ERR_NIX_ERROR` variant of the `nix_err` enum type + /// NIX_ERR_NIX_ERROR = -4 + /// ``` + ExprEval { name: String, info_msg: String }, + + /// A key/index access error occurred in C API functions. + /// + /// # Reason + /// + /// This error code is returned when accessing a key, index, or identifier that + /// does not exist in C API functions. Common scenarios include: + /// - Setting keys that don't exist (nix_setting_get, nix_setting_set) + /// - List indices that are out of bounds (nix_get_list_byidx*) + /// - Attribute names that don't exist (nix_get_attr_byname*) + /// - Attribute indices that are out of bounds (nix_get_attr_byidx*, nix_get_attr_name_byidx) + /// + /// This error typically indicates incorrect usage or assumptions about data structure + /// contents, rather than internal Nix evaluation errors. + /// + /// # Note + /// + /// This error code should ONLY be returned by C API functions themselves, + /// not by underlying Nix evaluation. For example, evaluating `{}.foo` in Nix + /// will throw a normal error (NIX_ERR_NIX_ERROR), not NIX_ERR_KEY. + /// + /// # Nix C++ API Internals + /// + /// ```cpp + /// // `NIX_ERR_KEY` variant of the `nix_err` enum type + /// NIX_ERR_KEY = -3 + /// ``` + KeyNotFound(Option), + + /// An overflow error occurred. + /// + /// # Reason + /// + /// This error code is returned when an overflow error occurred during the + /// function execution. + /// + /// # Nix C++ API Internals + /// + /// ```cpp + /// // `NIX_ERR_OVERFLOW` variant of the `nix_err` enum type + /// NIX_ERR_OVERFLOW = -2 + /// ``` + Overflow, + + /// An unknown error occurred. + /// + /// # Reason + /// + /// This error code is returned when an unknown error occurred during the + /// function execution. + /// + /// # Nix C++ API Internals + /// + /// ```cpp + /// // `NIX_ERR_OVERFLOW` variant of the `nix_err` enum type + /// NIX_ERR_UNKNOWN = -1 + /// ``` + Unknown, + + /// + /// An undocumented error occurred. + /// + /// # Reason + /// + /// The libnix C API defines `enum nix_err` as a signed integer value. + /// In the (unexpected) event libnix returns an error code with an + /// invalid enum value, or one I new addition I didn't know existed, + /// then an [NixError::Undocumented] is considered to have occurred. + /// + /// # Nix C++ API Internals + /// + /// [NixError::Undocumented] has no equivalent in the `libnix` api. + /// This is solely a language difference between C++ and Rust, since + /// [sys::nix_err] is defined over the *"continuous" (not realy)* + /// type [std::os::raw::c_int]. + Undocumented(sys::nix_err), +} + +// impl NixError { +// /// # Panics +// /// +// /// This function will panic in the event that `context.get_err() == Some(err) && err == sys::nix_err_NIX_OK` +// /// since `nixide::ErrorContext::get_err` is expected to return `None` to indicate `sys::ni_err_NIX_OK`. +// /// +// /// +// /// This function will panic in the event that `value != sys::nix_err_NIX_OK` +// /// but that `context.get_code() == sys::nix_err_NIX_OK` +// pub(super) fn from_error_context(context: &ErrorContext) -> Option { +// #[allow(nonstandard_style)] +// match context.get_err()? { +// sys::nix_err_NIX_OK => unreachable!("call to `nixide::ErrorContext::get_err@nixide::NixError::from_context` failed: please open an issue on https://github.com/cry128/nixide"), + +// sys::nix_err_NIX_ERR_OVERFLOW => Some(NixError::Overflow), +// sys::nix_err_NIX_ERR_KEY => Some(NixError::KeyNotFound(None)), +// sys::nix_err_NIX_ERR_NIX_ERROR => Some(NixError::ExprEval { +// name: context +// .get_nix_err_name() +// .expect("call to `nixide::ErrorContext::get_nix_err_name@nixide::NixError::from_context` failed: please open an issue on https://github.com/cry128/nixide"), +// info_msg: context.get_nix_err_info_msg() +// .expect("call to `nixide::ErrorContext::get_nix_err_info_msg@nixide::NixError::from_context` failed: please open an issue on https://github.com/cry128/nixide"), +// }), + +// sys::nix_err_NIX_ERR_UNKNOWN => Some(NixError::Unknown), +// err => Some(NixError::Undocumented(err)), +// } +// } +// } + +impl Display for NixError { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + match self { + NixError::ExprEval { name, info_msg } => write!(f, "[libnix] NixExpr evaluation failed [name=\"{name}\", info_msg=\"{info_msg}\"]"), + NixError::KeyNotFound(Some(key)) => write!(f, "[libnix] Key not found \"{key}\""), + NixError::KeyNotFound(None) => write!(f, "[libnix] Key not found"), + NixError::Overflow => write!(f, "[libnix] Overflow error"), + NixError::Unknown => write!(f, "[libnix] Unknown error"), + NixError::Undocumented(err) => write!( + f, + "[libnix] An undocumented nix_err({err}) [please open an issue on https://github.com/cry128/nixide]" + ), + } + } +} diff --git a/nixide/src/expr/evalstate.rs b/nixide/src/expr/evalstate.rs index ded2a10..bdb7884 100644 --- a/nixide/src/expr/evalstate.rs +++ b/nixide/src/expr/evalstate.rs @@ -2,9 +2,13 @@ use std::ffi::CString; use std::ptr::NonNull; use std::sync::Arc; +use crate::errors::new_nixide_error; + use super::Value; +use crate::errors::ErrorContext; use crate::sys; -use crate::{ErrorContext, NixErrorCode, Store}; +use crate::util::wrappers::AsInnerPtr; +use crate::{NixideError, Store}; /// Nix evaluation state for evaluating expressions. /// @@ -12,23 +16,22 @@ use crate::{ErrorContext, NixErrorCode, Store}; /// and creating values. pub struct EvalState { inner: NonNull, + + // XXX: TODO: is an `Arc` necessary or just a `Store` #[allow(dead_code)] store: Arc, - pub(super) context: Arc, +} + +impl AsInnerPtr for EvalState { + unsafe fn as_ptr(&self) -> *mut sys::EvalState { + self.inner.as_ptr() + } } impl EvalState { /// Construct a new EvalState directly from its attributes - pub(super) fn new( - inner: NonNull, - store: Arc, - context: Arc, - ) -> Self { - Self { - inner, - store, - context, - } + pub(super) fn new(inner: NonNull, store: Arc) -> Self { + Self { inner, store } } /// Evaluate a Nix expression from a string. @@ -41,41 +44,36 @@ impl EvalState { /// # Errors /// /// Returns an error if evaluation fails. - pub fn eval_from_string(&self, expr: &str, path: &str) -> Result, NixErrorCode> { - let expr_c = - NixErrorCode::from_nulerror(CString::new(expr), "nixide::EvalState::eval_from_string")?; - let path_c = - NixErrorCode::from_nulerror(CString::new(path), "nixide::EvalState::eval_from_string")?; + pub fn eval_from_string(&self, expr: &str, path: &str) -> Result { + let expr_c = CString::new(expr).or(Err(new_nixide_error!(StringNulByte)))?; + let path_c = CString::new(path).or(Err(new_nixide_error!(StringNulByte)))?; + let ctx = ErrorContext::new(); // Allocate value for result - // SAFETY: context and state are valid - let value_ptr = unsafe { sys::nix_alloc_value(self.context.as_ptr(), self.inner.as_ptr()) }; - if value_ptr.is_null() { - return Err(NixErrorCode::NullPtr { - location: "nix_alloc_value", - }); - } + // XXX: TODO: refactor this code to use `nixide::Value` + let value_ptr = unsafe { sys::nix_alloc_value(ctx.as_ptr(), self.as_ptr()) }; + let value = match ctx.peak() { + Some(err) => Err(err), + None => match NonNull::new(value_ptr) { + Some(inner) => Ok(Value { inner }), + None => Err(new_nixide_error!(NullPtr)), + }, + }?; // Evaluate expression - // SAFETY: all pointers are valid - NixErrorCode::from( - unsafe { - sys::nix_expr_eval_from_string( - self.context.as_ptr(), - self.inner.as_ptr(), - expr_c.as_ptr(), - path_c.as_ptr(), - value_ptr, - ) - }, - "nix_expr_eval_from_string", - )?; - - let inner = NonNull::new(value_ptr).ok_or(NixErrorCode::NullPtr { - location: "nix_expr_eval_from_string", - })?; - - Ok(Value { inner, state: self }) + unsafe { + sys::nix_expr_eval_from_string( + ctx.as_ptr(), + self.as_ptr(), + expr_c.as_ptr(), + path_c.as_ptr(), + value.as_ptr(), + ); + }; + match ctx.peak() { + Some(err) => Err(err), + None => Ok(value), + } } /// Allocate a new value. @@ -83,23 +81,16 @@ impl EvalState { /// # Errors /// /// Returns an error if value allocation fails. - pub fn alloc_value(&self) -> Result, NixErrorCode> { - // SAFETY: context and state are valid - let value_ptr = unsafe { sys::nix_alloc_value(self.context.as_ptr(), self.inner.as_ptr()) }; - let inner = NonNull::new(value_ptr).ok_or(NixErrorCode::NullPtr { - location: "nix_alloc_value", - })?; - - Ok(Value { inner, state: self }) - } - - /// Get the raw state pointer. - /// - /// # Safety - /// - /// The caller must ensure the pointer is used safely. - pub(super) unsafe fn as_ptr(&self) -> *mut sys::EvalState { - self.inner.as_ptr() + pub fn alloc_value(&self) -> Result { + let ctx = ErrorContext::new(); + let value_ptr = unsafe { sys::nix_alloc_value(ctx.as_ptr(), self.as_ptr()) }; + match ctx.peak() { + Some(err) => Err(err), + None => match NonNull::new(value_ptr) { + Some(inner) => Ok(Value { inner }), + None => Err(new_nixide_error!(NullPtr)), + }, + } } } diff --git a/nixide/src/expr/evalstatebuilder.rs b/nixide/src/expr/evalstatebuilder.rs index ef417db..25cf117 100644 --- a/nixide/src/expr/evalstatebuilder.rs +++ b/nixide/src/expr/evalstatebuilder.rs @@ -2,8 +2,10 @@ use std::ptr::NonNull; use std::sync::Arc; use super::EvalState; +use crate::errors::{new_nixide_error, ErrorContext, NixideError}; use crate::sys; -use crate::{ErrorContext, NixErrorCode, Store}; +use crate::util::wrappers::AsInnerPtr; +use crate::Store; /// Builder for Nix evaluation state. /// @@ -12,7 +14,6 @@ use crate::{ErrorContext, NixErrorCode, Store}; pub struct EvalStateBuilder { inner: NonNull, store: Arc, - context: Arc, } impl EvalStateBuilder { @@ -25,19 +26,16 @@ impl EvalStateBuilder { /// # Errors /// /// Returns an error if the builder cannot be created. - pub fn new(store: &Arc) -> Result { + pub fn new(store: &Arc) -> Result { // SAFETY: store context and store are valid let builder_ptr = unsafe { sys::nix_eval_state_builder_new(store._context.as_ptr(), store.as_ptr()) }; - let inner = NonNull::new(builder_ptr).ok_or(NixErrorCode::NullPtr { - location: "nix_eval_state_builder_new", - })?; + let inner = NonNull::new(builder_ptr).ok_or(new_nixide_error!(NullPtr))?; Ok(EvalStateBuilder { inner, store: Arc::clone(store), - context: Arc::clone(&store._context), }) } @@ -46,29 +44,24 @@ impl EvalStateBuilder { /// # Errors /// /// Returns an error if the evaluation state cannot be built. - pub fn build(self) -> Result { + pub fn build(self) -> Result { + let ctx = ErrorContext::new(); // Load configuration first - // SAFETY: context and builder are valid - NixErrorCode::from( - unsafe { sys::nix_eval_state_builder_load(self.context.as_ptr(), self.inner.as_ptr()) }, - "nix_eval_state_builder_load", - )?; + unsafe { sys::nix_eval_state_builder_load(ctx.as_ptr(), self.as_ptr()) }; + if let Some(err) = ctx.peak() { + return Err(err); + } // Build the state - // SAFETY: context and builder are valid - let state_ptr = - unsafe { sys::nix_eval_state_build(self.context.as_ptr(), self.inner.as_ptr()) }; + let state_ptr = unsafe { sys::nix_eval_state_build(ctx.as_ptr(), self.as_ptr()) }; + if let Some(err) = ctx.peak() { + return Err(err); + } - let inner = NonNull::new(state_ptr).ok_or(NixErrorCode::NullPtr { - location: "nix_eval_state_build", - })?; + let inner = NonNull::new(state_ptr).ok_or(new_nixide_error!(NullPtr))?; // The builder is consumed here - its Drop will clean up - Ok(EvalState::new( - inner, - self.store.clone(), - self.context.clone(), - )) + Ok(EvalState::new(inner, self.store.clone())) } pub(crate) unsafe fn as_ptr(&self) -> *mut sys::nix_eval_state_builder { diff --git a/nixide/src/expr/tests.rs b/nixide/src/expr/tests.rs index 054fce9..1bcc04a 100644 --- a/nixide/src/expr/tests.rs +++ b/nixide/src/expr/tests.rs @@ -3,19 +3,13 @@ use std::sync::Arc; use serial_test::serial; use super::{EvalStateBuilder, ValueType}; -use crate::{ErrorContext, Store}; - -#[test] -#[serial] -fn test_context_creation() { - let _ctx = ErrorContext::new().expect("Failed to create context"); - // Context should be dropped automatically -} +use crate::errors::ErrorContext; +use crate::Store; #[test] #[serial] fn test_eval_state_builder() { - let ctx = Arc::new(ErrorContext::new().expect("Failed to create context")); + let ctx = Arc::new(ErrorContext::new()); let store = Arc::new(Store::open(&ctx, None).expect("Failed to open store")); let _state = EvalStateBuilder::new(&store) .expect("Failed to create builder") @@ -27,7 +21,7 @@ fn test_eval_state_builder() { #[test] #[serial] fn test_simple_evaluation() { - let ctx = Arc::new(ErrorContext::new().expect("Failed to create context")); + let ctx = Arc::new(ErrorContext::new()); let store = Arc::new(Store::open(&ctx, None).expect("Failed to open store")); let state = EvalStateBuilder::new(&store) .expect("Failed to create builder") @@ -45,7 +39,7 @@ fn test_simple_evaluation() { #[test] #[serial] fn test_value_types() { - let ctx = Arc::new(ErrorContext::new().expect("Failed to create context")); + let ctx = Arc::new(ErrorContext::new()); let store = Arc::new(Store::open(&ctx, None).expect("Failed to open store")); let state = EvalStateBuilder::new(&store) .expect("Failed to create builder") @@ -77,7 +71,7 @@ fn test_value_types() { #[test] #[serial] fn test_value_formatting() { - let ctx = Arc::new(ErrorContext::new().expect("Failed to create context")); + let ctx = Arc::new(ErrorContext::new()); let store = Arc::new(Store::open(&ctx, None).expect("Failed to open store")); let state = EvalStateBuilder::new(&store) .expect("Failed to create builder") diff --git a/nixide/src/expr/value.rs b/nixide/src/expr/value.rs index 32b6220..48a10e4 100644 --- a/nixide/src/expr/value.rs +++ b/nixide/src/expr/value.rs @@ -2,19 +2,32 @@ use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; use std::ptr::NonNull; use super::{EvalState, ValueType}; +use crate::errors::{new_nixide_error, ErrorContext, NixideError}; use crate::sys; -use crate::NixErrorCode; +use crate::util::wrappers::{AsInnerPtr, FromC as _}; +use crate::util::AsErr; /// A Nix value /// /// This represents any value in the Nix language, including primitives, /// collections, and functions. -pub struct Value<'a> { +pub struct Value { pub(crate) inner: NonNull, - pub(crate) state: &'a EvalState, } -impl Value<'_> { +impl AsInnerPtr for Value { + unsafe fn as_ptr(&self) -> *mut sys::nix_value { + self.inner.as_ptr() + } +} + +impl Value { + pub(crate) unsafe fn new(inner: *mut sys::Value) -> Self { + Value { + inner: NonNull::new(inner).unwrap(), + } + } + /// Force evaluation of this value. /// /// If the value is a thunk, this will evaluate it to its final form. @@ -22,18 +35,12 @@ impl Value<'_> { /// # Errors /// /// Returns an error if evaluation fails. - pub fn force(&mut self) -> Result<(), NixErrorCode> { - NixErrorCode::from( - // SAFETY: context, state, and value are valid - unsafe { - sys::nix_value_force( - self.state.context.as_ptr(), - self.state.as_ptr(), - self.inner.as_ptr(), - ) - }, - "nix_value_force", - ) + pub fn force(&mut self, state: &EvalState) -> Result<(), NixideError> { + // XXX: TODO: move force and force_deep to the EvalState + let ctx = ErrorContext::new(); + + unsafe { sys::nix_value_force(ctx.as_ptr(), state.as_ptr(), self.as_ptr()) }; + ctx.peak().as_err() } /// Force deep evaluation of this value. @@ -43,26 +50,38 @@ impl Value<'_> { /// # Errors /// /// Returns an error if evaluation fails. - pub fn force_deep(&mut self) -> Result<(), NixErrorCode> { - NixErrorCode::from( - // SAFETY: context, state, and value are valid - unsafe { - sys::nix_value_force_deep( - self.state.context.as_ptr(), - self.state.as_ptr(), - self.inner.as_ptr(), - ) - }, - "nix_value_force_deep", - ) + pub fn force_deep(&mut self, state: &EvalState) -> Result<(), NixideError> { + let ctx = ErrorContext::new(); + + unsafe { sys::nix_value_force_deep(ctx.as_ptr(), state.as_ptr(), self.as_ptr()) }; + ctx.peak().as_err() } /// Get the type of this value. #[must_use] pub fn value_type(&self) -> ValueType { - // SAFETY: context and value are valid - let c_type = unsafe { sys::nix_get_type(self.state.context.as_ptr(), self.inner.as_ptr()) }; - ValueType::from_c(c_type) + let ctx = ErrorContext::new(); + let value_type = + unsafe { ValueType::from_c(sys::nix_get_type(ctx.as_ptr(), self.as_ptr())) }; + // NOTE: an error here only occurs if `nix_get_type` catches an error, + // NOTE: which in turn only happens if the `sys::nix_value*` is a null pointer + // NOTE: or points to an uninitialised `nix_value` struct. + ctx.peak() + .as_err() + .unwrap_or_else(|_| panic!("TODO im sleepy rn")); + value_type + } + + fn expect_type(&self, expected: ValueType) -> Result<(), NixideError> { + let got = self.value_type(); + if got != expected { + return Err(new_nixide_error!( + InvalidType, + expected.to_string(), + got.to_string() + )); + } + Ok(()) } /// Convert this value to an integer. @@ -70,19 +89,15 @@ impl Value<'_> { /// # Errors /// /// Returns an error if the value is not an integer. - pub fn as_int(&self) -> Result { - if self.value_type() != ValueType::Int { - return Err(NixErrorCode::InvalidType { - location: "nixide::Value::as_int", - expected: "int", - got: self.value_type().to_string(), - }); + pub fn as_int(&self) -> Result { + self.expect_type(ValueType::Int)?; + + let ctx = ErrorContext::new(); + let result = unsafe { sys::nix_get_int(ctx.as_ptr(), self.as_ptr()) }; + match ctx.peak() { + Some(err) => Err(err), + None => Ok(result), } - - // SAFETY: context and value are valid, type is checked - let result = unsafe { sys::nix_get_int(self.state.context.as_ptr(), self.inner.as_ptr()) }; - - Ok(result) } /// Convert this value to a float. @@ -90,20 +105,15 @@ impl Value<'_> { /// # Errors /// /// Returns an error if the value is not a float. - pub fn as_float(&self) -> Result { - if self.value_type() != ValueType::Float { - return Err(NixErrorCode::InvalidType { - location: "nixide::Value::as_float", - expected: "float", - got: self.value_type().to_string(), - }); + pub fn as_float(&self) -> Result { + self.expect_type(ValueType::Float)?; + + let ctx = ErrorContext::new(); + let result = unsafe { sys::nix_get_float(ctx.as_ptr(), self.as_ptr()) }; + match ctx.peak() { + Some(err) => Err(err), + None => Ok(result), } - - // SAFETY: context and value are valid, type is checked - let result = - unsafe { sys::nix_get_float(self.state.context.as_ptr(), self.inner.as_ptr()) }; - - Ok(result) } /// Convert this value to a boolean. @@ -111,19 +121,15 @@ impl Value<'_> { /// # Errors /// /// Returns an error if the value is not a boolean. - pub fn as_bool(&self) -> Result { - if self.value_type() != ValueType::Bool { - return Err(NixErrorCode::InvalidType { - location: "nixide::Value::as_bool", - expected: "bool", - got: self.value_type().to_string(), - }); + pub fn as_bool(&self) -> Result { + self.expect_type(ValueType::Bool)?; + + let ctx = ErrorContext::new(); + let result = unsafe { sys::nix_get_bool(ctx.as_ptr(), self.as_ptr()) }; + match ctx.peak() { + Some(err) => Err(err), + None => Ok(result), } - - // SAFETY: context and value are valid, type is checked - let result = unsafe { sys::nix_get_bool(self.state.context.as_ptr(), self.inner.as_ptr()) }; - - Ok(result) } /// Convert this value to a string. @@ -131,33 +137,25 @@ impl Value<'_> { /// # Errors /// /// Returns an error if the value is not a string. - pub fn as_string(&self) -> Result { - if self.value_type() != ValueType::String { - return Err(NixErrorCode::InvalidType { - location: "nixide::Value::as_string", - expected: "string", - got: self.value_type().to_string(), - }); - } + pub fn as_string(&self) -> Result { + self.expect_type(ValueType::String)?; + + let ctx = ErrorContext::new(); // For string values, we need to use realised string API - // SAFETY: context and value are valid, type is checked let realised_str = unsafe { sys::nix_string_realise( - self.state.context.as_ptr(), + ctx.as_ptr(), self.state.as_ptr(), - self.inner.as_ptr(), + self.as_ptr(), false, // don't copy more ) }; if realised_str.is_null() { - return Err(NixErrorCode::NullPtr { - location: "nix_string_realise", - }); + return Err(new_nixide_error!(NullPtr)); } - // SAFETY: realised_str is non-null and points to valid RealizedString let buffer_start = unsafe { sys::nix_realised_string_get_buffer_start(realised_str) }; let buffer_size = unsafe { sys::nix_realised_string_get_buffer_size(realised_str) }; if buffer_start.is_null() { @@ -165,18 +163,12 @@ impl Value<'_> { unsafe { sys::nix_realised_string_free(realised_str); } - return Err(NixErrorCode::NullPtr { - location: "nix_realised_string_free", - }); + return Err(new_nixide_error!(NullPtr)); } - // SAFETY: buffer_start is non-null and buffer_size gives us the length let bytes = unsafe { std::slice::from_raw_parts(buffer_start.cast::(), buffer_size) }; let string = std::str::from_utf8(bytes) - .map_err(|_| NixErrorCode::Unknown { - location: "nixide::Value::as_string", - reason: "Invalid UTF-8 in string".to_string(), - })? + .map_err(|_| new_nixide_error!(StringNotUtf8))? .to_owned(); // Clean up realised string @@ -187,16 +179,6 @@ impl Value<'_> { Ok(string) } - /// Get the raw value pointer. - /// - /// # Safety - /// - /// The caller must ensure the pointer is used safely. - #[allow(dead_code)] - unsafe fn as_ptr(&self) -> *mut sys::nix_value { - self.inner.as_ptr() - } - /// Format this value as Nix syntax. /// /// This provides a string representation that matches Nix's own syntax, @@ -206,7 +188,7 @@ impl Value<'_> { /// /// Returns an error if the value cannot be converted to a string /// representation. - pub fn to_nix_string(&self) -> Result { + pub fn to_nix_string(&self) -> Result { match self.value_type() { ValueType::Int => Ok(self.as_int()?.to_string()), ValueType::Float => Ok(self.as_float()?.to_string()), @@ -227,16 +209,16 @@ impl Value<'_> { } } -impl Drop for Value<'_> { +impl Drop for Value { fn drop(&mut self) { - // SAFETY: We own the value and it's valid until drop + let ctx = ErrorContext::new(); unsafe { - sys::nix_value_decref(self.state.context.as_ptr(), self.inner.as_ptr()); + sys::nix_value_decref(ctx.as_ptr(), self.as_ptr()); } } } -impl Display for Value<'_> { +impl Display for Value { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { match self.value_type() { ValueType::Int => { @@ -278,7 +260,7 @@ impl Display for Value<'_> { } } -impl Debug for Value<'_> { +impl Debug for Value { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { let value_type = self.value_type(); match value_type { diff --git a/nixide/src/expr/valuetype.rs b/nixide/src/expr/valuetype.rs index 408bd65..62ed8cd 100644 --- a/nixide/src/expr/valuetype.rs +++ b/nixide/src/expr/valuetype.rs @@ -1,6 +1,6 @@ use std::fmt::{Display, Formatter, Result as FmtResult}; -use crate::sys; +use crate::{sys, util::wrappers::FromC}; /// Nix value types. #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -29,8 +29,8 @@ pub enum ValueType { External, } -impl ValueType { - pub(super) fn from_c(value_type: sys::ValueType) -> Self { +impl FromC for ValueType { + unsafe fn from_c(value_type: sys::ValueType) -> Self { match value_type { sys::ValueType_NIX_TYPE_THUNK => ValueType::Thunk, sys::ValueType_NIX_TYPE_INT => ValueType::Int, @@ -43,7 +43,7 @@ impl ValueType { sys::ValueType_NIX_TYPE_LIST => ValueType::List, sys::ValueType_NIX_TYPE_FUNCTION => ValueType::Function, sys::ValueType_NIX_TYPE_EXTERNAL => ValueType::External, - _ => ValueType::Thunk, // fallback (TODO: is this ok?) + _ => unreachable!("call to `nixide::ValueType::from_c` failed: please open an issue on https://github.com/cry128/nixide"), } } } diff --git a/nixide/src/flake/eval_state_builder_ext.rs b/nixide/src/flake/eval_state_builder_ext.rs index 3c3acac..ff8598f 100644 --- a/nixide/src/flake/eval_state_builder_ext.rs +++ b/nixide/src/flake/eval_state_builder_ext.rs @@ -1,14 +1,14 @@ use super::FlakeSettings; -use crate::{EvalStateBuilder, NixErrorCode}; +use crate::{EvalStateBuilder, NixideError}; pub trait EvalStateBuilderExt { /// Configures the eval state to provide flakes features such as `builtins.getFlake`. - fn flakes(self, settings: &FlakeSettings) -> Result; + fn flakes(self, settings: &FlakeSettings) -> Result; } impl EvalStateBuilderExt for EvalStateBuilder { /// Configures the eval state to provide flakes features such as `builtins.getFlake`. - fn flakes(mut self, settings: &FlakeSettings) -> Result { + fn flakes(mut self, settings: &FlakeSettings) -> Result { settings.add_to_eval_state_builder(&mut self).map(|_| self) } } diff --git a/nixide/src/flake/fetchers_settings.rs b/nixide/src/flake/fetchers_settings.rs index 8332695..ac4f5ca 100644 --- a/nixide/src/flake/fetchers_settings.rs +++ b/nixide/src/flake/fetchers_settings.rs @@ -1,20 +1,20 @@ use std::ptr::NonNull; +use crate::errors::{new_nixide_error, ErrorContext}; use crate::sys; -use crate::{ErrorContext, NixErrorCode}; +use crate::util::wrappers::AsInnerPtr; +use crate::NixideError; pub(super) struct FetchersSettings { pub(super) ptr: NonNull, } impl FetchersSettings { - pub fn new() -> Result { - let ctx = ErrorContext::new()?; + pub fn new() -> Result { + let ctx = ErrorContext::new(); let ptr = unsafe { sys::nix_fetchers_settings_new(ctx.as_ptr()) }; Ok(FetchersSettings { - ptr: NonNull::new(ptr).ok_or(NixErrorCode::NullPtr { - location: "fetchers_settings_new", - })?, + ptr: NonNull::new(ptr).ok_or(new_nixide_error!(NullPtr))?, }) } diff --git a/nixide/src/flake/flake_lock_flags.rs b/nixide/src/flake/flake_lock_flags.rs index d006fc5..bb066ec 100644 --- a/nixide/src/flake/flake_lock_flags.rs +++ b/nixide/src/flake/flake_lock_flags.rs @@ -2,8 +2,11 @@ use std::ffi::CString; use std::ptr::NonNull; use super::{FlakeReference, FlakeSettings}; +use crate::errors::new_nixide_error; +use crate::errors::ErrorContext; use crate::sys; -use crate::{ErrorContext, NixErrorCode}; +use crate::util::wrappers::AsInnerPtr; +use crate::NixideError; #[derive(Debug, Clone)] pub enum FlakeLockMode { @@ -30,46 +33,40 @@ impl Drop for FlakeLockFlags { } impl FlakeLockFlags { // XXX: TODO: what is the default FlakeLockMode? - pub fn new(settings: &FlakeSettings) -> Result { - ErrorContext::new().and_then(|ctx| { - NonNull::new(unsafe { sys::nix_flake_lock_flags_new(ctx.as_ptr(), settings.as_ptr()) }) - .ok_or(NixErrorCode::NulError { - location: "nix_flake_lock_flags_new", - }) - .map(|inner| FlakeLockFlags { inner }) - }) + pub fn new(settings: &FlakeSettings) -> Result { + let ctx = ErrorContext::new(); + NonNull::new(unsafe { sys::nix_flake_lock_flags_new(ctx.as_ptr(), settings.as_ptr()) }) + .ok_or(new_nixide_error!(NullPtr)) + .map(|inner| FlakeLockFlags { inner }) } pub(crate) fn as_ptr(&self) -> *mut sys::nix_flake_lock_flags { self.inner.as_ptr() } - pub fn set_lock_mode(&mut self, mode: &FlakeLockMode) -> Result<(), NixErrorCode> { - ErrorContext::new().and_then(|ctx| unsafe { - NixErrorCode::from( - match mode { - FlakeLockMode::WriteAsNeeded => { - sys::nix_flake_lock_flags_set_mode_write_as_needed( - ctx.as_ptr(), - self.as_ptr(), - ) - } - FlakeLockMode::Virtual => { - sys::nix_flake_lock_flags_set_mode_virtual(ctx.as_ptr(), self.as_ptr()) - } - FlakeLockMode::Check => { - sys::nix_flake_lock_flags_set_mode_check(ctx.as_ptr(), self.as_ptr()) - } - }, - "nix_flake_lock_flags_set_mode_check", - ) - }) + pub fn set_lock_mode(&mut self, mode: &FlakeLockMode) -> Result<(), NixideError> { + let ctx = ErrorContext::new(); + match mode { + FlakeLockMode::WriteAsNeeded => unsafe { + sys::nix_flake_lock_flags_set_mode_write_as_needed(ctx.as_ptr(), self.as_ptr()) + }, + FlakeLockMode::Virtual => unsafe { + sys::nix_flake_lock_flags_set_mode_virtual(ctx.as_ptr(), self.as_ptr()) + }, + FlakeLockMode::Check => unsafe { + sys::nix_flake_lock_flags_set_mode_check(ctx.as_ptr(), self.as_ptr()) + }, + }; + match ctx.peak() { + Some(err) => Err(err), + None => Ok(()), + } } /// Configures [LockedFlake::lock] to make incremental changes to the lock file as needed. Changes are written to file. - // pub fn set_mode_write_as_needed(&mut self) -> Result<(), NixErrorCode> { + // pub fn set_mode_write_as_needed(&mut self) -> Result<(), NixideError> { // ErrorContext::new().and_then(|ctx| { - // NixErrorCode::from( + // NixideError::from( // unsafe { // sys::nix_flake_lock_flags_set_mode_write_as_needed(ctx.as_ptr(), self.as_ptr()) // }, @@ -79,9 +76,9 @@ impl FlakeLockFlags { // } /// Make [LockedFlake::lock] check if the lock file is up to date. If not, an error is returned. - // pub fn set_mode_check(&mut self) -> Result<(), NixErrorCode> { + // pub fn set_mode_check(&mut self) -> Result<(), NixideError> { // ErrorContext::new().and_then(|ctx| { - // NixErrorCode::from( + // NixideError::from( // unsafe { sys::nix_flake_lock_flags_set_mode_check(ctx.as_ptr(), self.as_ptr()) }, // "nix_flake_lock_flags_set_mode_check", // ) @@ -89,9 +86,9 @@ impl FlakeLockFlags { // } /// Like `set_mode_write_as_needed`, but does not write to the lock file. - // pub fn set_mode_virtual(&mut self) -> Result<(), NixErrorCode> { + // pub fn set_mode_virtual(&mut self) -> Result<(), NixideError> { // ErrorContext::new().and_then(|ctx| { - // NixErrorCode::from( + // NixideError::from( // unsafe { sys::nix_flake_lock_flags_set_mode_virtual(ctx.as_ptr(), self.as_ptr()) }, // "nix_flake_lock_flags_set_mode_virtual", // ) @@ -114,24 +111,21 @@ impl FlakeLockFlags { &mut self, path: &str, flakeref: &FlakeReference, - ) -> Result<(), NixErrorCode> { - let input_path = NixErrorCode::from_nulerror( - CString::new(path), - "nixide::FlakeLockArgs::override_input", - )?; + ) -> Result<(), NixideError> { + let input_path = CString::new(path).or_else(|_| Err(new_nixide_error!(StringNulByte))); - ErrorContext::new().and_then(|ctx| unsafe { - NixErrorCode::from( - unsafe { - sys::nix_flake_lock_flags_add_input_override( - ctx.as_ptr(), - self.as_ptr(), - input_path.as_ptr(), - flakeref.as_ptr(), - ) - }, - "nix_flake_lock_flags_add_input_override", + let ctx = ErrorContext::new(); + unsafe { + sys::nix_flake_lock_flags_add_input_override( + ctx.as_ptr(), + self.as_ptr(), + input_path.as_ptr(), + flakeref.as_ptr(), ) - }) + }; + match ctx.peak() { + Some(err) => Err(err), + None => Ok(()), + } } } diff --git a/nixide/src/flake/flake_reference.rs b/nixide/src/flake/flake_reference.rs index 09c31d8..3979e2a 100644 --- a/nixide/src/flake/flake_reference.rs +++ b/nixide/src/flake/flake_reference.rs @@ -1,14 +1,30 @@ +use std::os::raw::c_char; +use std::ptr::{null_mut, NonNull}; + +use super::{FetchersSettings, FlakeReferenceParseFlags, FlakeSettings}; +use crate::errors::new_nixide_error; +use crate::sys; +use crate::util::bindings::wrap_libnix_string_callback; +use crate::util::wrappers::AsInnerPtr; +use crate::NixideError; + pub struct FlakeReference { - pub(crate) ptr: NonNull, + pub(crate) ptr: NonNull, } + impl Drop for FlakeReference { fn drop(&mut self) { unsafe { - raw::flake_reference_free(self.ptr.as_ptr()); + sys::nix_flake_reference_free(self.ptr.as_ptr()); } } } + impl FlakeReference { + pub fn as_ptr(&self) -> *mut sys::nix_flake_reference { + self.ptr.as_ptr() + } + /// Parse a flake reference from a string. /// The string must be a valid flake reference, such as `github:owner/repo`. /// It may also be suffixed with a `#` and a fragment, such as `github:owner/repo#something`, @@ -18,26 +34,25 @@ impl FlakeReference { flake_settings: &FlakeSettings, flags: &FlakeReferenceParseFlags, reference: &str, - ) -> Result<(FlakeReference, String)> { - let mut ctx = Context::new(); - let mut r = result_string_init!(); - let mut ptr: *mut raw::flake_reference = std::ptr::null_mut(); - unsafe { - context::check_call!(raw::flake_reference_and_fragment_from_string( - &mut ctx, - fetch_settings.raw_ptr(), - flake_settings.ptr, - flags.ptr.as_ptr(), + ) -> Result<(FlakeReference, String), NixideError> { + let mut ptr: *mut sys::nix_flake_reference = null_mut(); + let result = wrap_libnix_string_callback(|ctx, callback, user_data| unsafe { + sys::nix_flake_reference_and_fragment_from_string( + ctx.as_ptr(), + fetch_settings.as_ptr(), + flake_settings.as_ptr(), + flags.as_ptr(), reference.as_ptr() as *const c_char, reference.len(), - // pointer to ptr &mut ptr, - Some(callback_get_result_string), - callback_get_result_string_data(&mut r) - )) - }?; - let ptr = NonNull::new(ptr) - .context("flake_reference_and_fragment_from_string unexpectedly returned null")?; - Ok((FlakeReference { ptr }, r?)) + Some(callback), + user_data, + ) + }); + + match NonNull::new(ptr) { + Some(ptr) => result.map(|s| (FlakeReference { ptr }, s)), + None => Err(new_nixide_error!(NullPtr)), + } } } diff --git a/nixide/src/flake/flake_reference_parse_flags.rs b/nixide/src/flake/flake_reference_parse_flags.rs index 2353d94..39bbf78 100644 --- a/nixide/src/flake/flake_reference_parse_flags.rs +++ b/nixide/src/flake/flake_reference_parse_flags.rs @@ -1,13 +1,18 @@ +use std::os::raw::c_char; use std::ptr::NonNull; use super::FlakeSettings; +use crate::errors::{new_nixide_error, ErrorContext}; use crate::sys; -use crate::{ErrorContext, NixErrorCode}; +use crate::util::wrappers::AsInnerPtr; +use crate::NixideError; /// Parameters for parsing a flake reference. +#[derive(Debug)] pub struct FlakeReferenceParseFlags { pub(crate) ptr: NonNull, } + impl Drop for FlakeReferenceParseFlags { fn drop(&mut self) { unsafe { @@ -15,31 +20,39 @@ impl Drop for FlakeReferenceParseFlags { } } } + impl FlakeReferenceParseFlags { - pub fn new(settings: &FlakeSettings) -> Result { - let mut ctx = ErrorContext::new(); - let ptr = unsafe { - context::check_call!(sys::nix_flake_reference_parse_flags_new( - &mut ctx, - settings.ptr - )) - }?; - let ptr = NonNull::new(ptr) - .context("flake_reference_parse_flags_new unexpectedly returned null")?; - Ok(FlakeReferenceParseFlags { ptr }) + pub fn new(settings: &FlakeSettings) -> Result { + let ctx = ErrorContext::new(); + let ptr = + unsafe { sys::nix_flake_reference_parse_flags_new(ctx.as_ptr(), settings.as_ptr()) }; + match ctx.peak() { + Some(err) => Err(err), + None => NonNull::new(ptr).map_or(Err(new_nixide_error!(NullPtr)), |ptr| { + Ok(FlakeReferenceParseFlags { ptr }) + }), + } } + /// Sets the [base directory](https://nix.dev/manual/nix/latest/glossary#gloss-base-directory) /// for resolving local flake references. - pub fn set_base_directory(&mut self, base_directory: &str) -> Result<(), NixErrorCode> { - let mut ctx = ErrorContext::new(); + pub fn set_base_directory(&mut self, base_directory: &str) -> Result<(), NixideError> { + let ctx = ErrorContext::new(); unsafe { - sys::context::check_call!(sys::nix_flake_reference_parse_flags_set_base_directory( - &mut ctx, - self.ptr.as_ptr(), + sys::nix_flake_reference_parse_flags_set_base_directory( + ctx.as_ptr(), + self.as_ptr(), base_directory.as_ptr() as *const c_char, - base_directory.len() - )) - }?; - Ok(()) + base_directory.len(), + ) + }; + match ctx.peak() { + Some(err) => Err(err), + None => Ok(()), + } + } + + pub fn as_ptr(&self) -> *mut sys::nix_flake_reference_parse_flags { + self.ptr.as_ptr() } } diff --git a/nixide/src/flake/flake_settings.rs b/nixide/src/flake/flake_settings.rs index 687b56d..a16d68b 100644 --- a/nixide/src/flake/flake_settings.rs +++ b/nixide/src/flake/flake_settings.rs @@ -1,45 +1,51 @@ use std::ptr::NonNull; +use crate::errors::{new_nixide_error, ErrorContext}; use crate::sys; -use crate::{ErrorContext, EvalStateBuilder, NixErrorCode}; +use crate::util::wrappers::AsInnerPtr; +use crate::{EvalStateBuilder, NixideError}; /// Store settings for the flakes feature. pub struct FlakeSettings { pub(crate) inner: NonNull, } +impl AsInnerPtr for FlakeSettings { + unsafe fn as_ptr(&self) -> *mut sys::nix_flake_settings { + self.inner.as_ptr() + } +} + impl FlakeSettings { - pub fn new() -> Result { - let ctx = ErrorContext::new()?; - let inner = NonNull::new(unsafe { sys::nix_flake_settings_new(ctx.as_ptr()) }).ok_or( - NixErrorCode::NullPtr { - location: "nix_flake_settings_new", + pub fn new() -> Result { + let ctx = ErrorContext::new(); + let opt = NonNull::new(unsafe { sys::nix_flake_settings_new(ctx.as_ptr()) }); + + match ctx.peak() { + Some(err) => Err(err), + None => match opt { + Some(inner) => Ok(FlakeSettings { inner }), + None => Err(new_nixide_error!(NullPtr)), }, - )?; - Ok(FlakeSettings { inner }) + } } pub(super) fn add_to_eval_state_builder( &self, builder: &mut EvalStateBuilder, - ) -> Result<(), NixErrorCode> { - let ctx = ErrorContext::new()?; - NixErrorCode::from( - unsafe { - sys::nix_flake_settings_add_to_eval_state_builder( - ctx.as_ptr(), - self.as_ptr(), - builder.as_ptr(), - ) - }, - "nix_flake_settings_add_to_eval_state_builder", - )?; - - Ok(()) - } - - pub(crate) unsafe fn as_ptr(&self) -> *mut sys::nix_flake_settings { - self.inner.as_ptr() + ) -> Result<(), NixideError> { + let ctx = ErrorContext::new(); + unsafe { + sys::nix_flake_settings_add_to_eval_state_builder( + ctx.as_ptr(), + self.as_ptr(), + builder.as_ptr(), + ) + }; + match ctx.peak() { + Some(err) => Err(err), + None => Ok(()), + } } } diff --git a/nixide/src/flake/locked_flake.rs b/nixide/src/flake/locked_flake.rs index 8348319..f11732c 100644 --- a/nixide/src/flake/locked_flake.rs +++ b/nixide/src/flake/locked_flake.rs @@ -1,10 +1,18 @@ +use std::ptr::NonNull; + +use super::{FetchersSettings, FlakeLockFlags, FlakeReference, FlakeSettings}; +use crate::errors::{new_nixide_error, ErrorContext}; +use crate::sys; +use crate::util::wrappers::AsInnerPtr; +use crate::{EvalState, NixideError, Value}; + pub struct LockedFlake { - pub(crate) ptr: NonNull, + pub(crate) ptr: NonNull, } impl Drop for LockedFlake { fn drop(&mut self) { unsafe { - raw::locked_flake_free(self.ptr.as_ptr()); + sys::nix_locked_flake_free(self.ptr.as_ptr()); } } } @@ -15,20 +23,24 @@ impl LockedFlake { eval_state: &EvalState, flags: &FlakeLockFlags, flake_ref: &FlakeReference, - ) -> Result { - let mut ctx = Context::new(); - let ptr = unsafe { - context::check_call!(raw::flake_lock( - &mut ctx, - fetch_settings.raw_ptr(), - flake_settings.ptr, - eval_state.raw_ptr(), - flags.ptr, - flake_ref.ptr.as_ptr() - )) - }?; - let ptr = NonNull::new(ptr).context("flake_lock unexpectedly returned null")?; - Ok(LockedFlake { ptr }) + ) -> Result { + let ctx = ErrorContext::new(); + + let ptr = NonNull::new(unsafe { + sys::nix_flake_lock( + ctx.as_ptr(), + fetch_settings.as_ptr(), + flake_settings.as_ptr(), + eval_state.as_ptr(), + flags.as_ptr(), + flake_ref.as_ptr(), + ) + }); + + match ptr { + Some(ptr) => Ok(LockedFlake { ptr }), + None => Err(new_nixide_error!(NullPtr)), + } } /// Returns the outputs of the flake - the result of calling the `outputs` attribute. @@ -36,24 +48,27 @@ impl LockedFlake { &self, flake_settings: &FlakeSettings, eval_state: &mut EvalState, - ) -> Result { - let mut ctx = Context::new(); - unsafe { - let r = context::check_call!(raw::locked_flake_get_output_attrs( - &mut ctx, - flake_settings.ptr, - eval_state.raw_ptr(), - self.ptr.as_ptr() - ))?; - Ok(nix_bindings_expr::value::__private::raw_value_new(r)) - } + ) -> Result { + let ctx = ErrorContext::new(); + + let r = unsafe { + sys::nix_locked_flake_get_output_attrs( + ctx.as_ptr(), + flake_settings.as_ptr(), + eval_state.as_ptr(), + self.ptr.as_ptr(), + ) + }; + Ok(nix_bindings_expr::value::__private::raw_value_new(r)) } } #[cfg(test)] mod tests { - use nix_bindings_expr::eval_state::{gc_register_my_thread, EvalStateBuilder}; - use nix_bindings_store::store::Store; + // use nix_bindings_expr::eval_state::{gc_register_my_thread, EvalStateBuilder}; + + use crate::flake::{FlakeLockMode, FlakeReferenceParseFlags}; + use crate::{EvalStateBuilder, Store}; use super::*; use std::sync::Once; @@ -248,7 +263,9 @@ mod tests { // Step 1: Do not update (check), fails - flake_lock_flags.set_mode_check().unwrap(); + flake_lock_flags + .set_lock_mode(&FlakeLockMode::Check) + .unwrap(); let locked_flake = LockedFlake::lock( &fetchers_settings, @@ -265,7 +282,9 @@ mod tests { }; // Step 2: Update but do not write, succeeds - flake_lock_flags.set_mode_virtual().unwrap(); + flake_lock_flags + .set_lock_mode(&FlakeLockMode::Virtual) + .unwrap(); let locked_flake = LockedFlake::lock( &fetchers_settings, @@ -287,7 +306,9 @@ mod tests { // Step 3: The lock was not written, so Step 1 would fail again - flake_lock_flags.set_mode_check().unwrap(); + flake_lock_flags + .set_lock_mode(&FlakeLockMode::Check) + .unwrap(); let locked_flake = LockedFlake::lock( &fetchers_settings, @@ -307,7 +328,9 @@ mod tests { // Step 4: Update and write, succeeds - flake_lock_flags.set_mode_write_as_needed().unwrap(); + flake_lock_flags + .set_lock_mode(&FlakeLockMode::WriteAsNeeded) + .unwrap(); let locked_flake = LockedFlake::lock( &fetchers_settings, @@ -327,7 +350,9 @@ mod tests { // Step 5: Lock was written, so Step 1 succeeds - flake_lock_flags.set_mode_check().unwrap(); + flake_lock_flags + .set_lock_mode(&FlakeLockMode::Check) + .unwrap(); let locked_flake = LockedFlake::lock( &fetchers_settings, @@ -348,7 +373,9 @@ mod tests { // Step 6: Lock with override, do not write // This shouldn't matter; write_as_needed will be overridden - flake_lock_flags.set_mode_write_as_needed().unwrap(); + flake_lock_flags + .set_lock_mode(&FlakeLockMode::WriteAsNeeded) + .unwrap(); let (flake_ref_c, fragment) = FlakeReference::parse_with_fragment( &fetchers_settings, @@ -359,9 +386,7 @@ mod tests { .unwrap(); assert_eq!(fragment, ""); - flake_lock_flags - .add_input_override("b", &flake_ref_c) - .unwrap(); + flake_lock_flags.override_input("b", &flake_ref_c).unwrap(); let locked_flake = LockedFlake::lock( &fetchers_settings, @@ -384,7 +409,9 @@ mod tests { // Step 7: Override was not written; lock still points to b - flake_lock_flags.set_mode_check().unwrap(); + flake_lock_flags + .set_lock_mode(&FlakeLockMode::Check) + .unwrap(); let locked_flake = LockedFlake::lock( &fetchers_settings, diff --git a/nixide/src/flake/mod.rs b/nixide/src/flake/mod.rs index b6a4133..51ae925 100644 --- a/nixide/src/flake/mod.rs +++ b/nixide/src/flake/mod.rs @@ -8,7 +8,7 @@ mod locked_flake; pub(self) use eval_state_builder_ext::EvalStateBuilderExt; pub(self) use fetchers_settings::FetchersSettings; -pub(self) use flake_lock_flags::FlakeLockFlags; +pub(self) use flake_lock_flags::{FlakeLockFlags, FlakeLockMode}; pub(self) use flake_reference::FlakeReference; pub(self) use flake_reference_parse_flags::FlakeReferenceParseFlags; pub(self) use flake_settings::FlakeSettings; diff --git a/nixide/src/lib.rs b/nixide/src/lib.rs index 3bb26ed..90ffab7 100644 --- a/nixide/src/lib.rs +++ b/nixide/src/lib.rs @@ -1,16 +1,14 @@ // #![warn(missing_docs)] -mod context; -mod error; +pub(crate) mod errors; mod expr; mod flake; mod store; -pub mod util; +pub(crate) mod util; mod verbosity; mod version; -pub use context::ErrorContext; -pub use error::NixErrorCode; +pub use errors::{NixError, NixideError, NixideResult}; pub use expr::{EvalState, EvalStateBuilder, Value, ValueType}; pub use store::{Store, StorePath}; pub use verbosity::NixVerbosity; diff --git a/nixide/src/store/mod.rs b/nixide/src/store/mod.rs index bbfe92e..9aa7f44 100644 --- a/nixide/src/store/mod.rs +++ b/nixide/src/store/mod.rs @@ -161,7 +161,7 @@ impl Store { ) }; - NixErrorCode::from(err, "nix_store_realise")?; + NixErrorCode::result_from(err, "nix_store_realise")?; // Return the collected outputs Ok(userdata.0) @@ -245,7 +245,7 @@ impl Store { store_path.inner.as_ptr(), ) }; - NixErrorCode::from(err, "nix_store_copy_closure") + NixErrorCode::result_from(err, "nix_store_copy_closure") } pub fn copy_closure_from( @@ -261,7 +261,7 @@ impl Store { store_path.inner.as_ptr(), ) }; - NixErrorCode::from(err, "nix_store_copy_closure") + NixErrorCode::result_from(err, "nix_store_copy_closure") } } diff --git a/nixide/src/store/path.rs b/nixide/src/store/path.rs index aecc896..c298eb4 100644 --- a/nixide/src/store/path.rs +++ b/nixide/src/store/path.rs @@ -4,8 +4,11 @@ use std::ptr::NonNull; use std::sync::Arc; use super::Store; +use crate::errors::{new_nixide_error, ErrorContext}; use crate::util::bindings::{wrap_libnix_pathbuf_callback, wrap_libnix_string_callback}; -use crate::{ErrorContext, NixErrorCode}; +use crate::util::wrappers::AsInnerPtr; +use crate::NixideError; + use nixide_sys::{self as sys, nix_err_NIX_OK}; /// A path in the Nix store. @@ -13,7 +16,6 @@ use nixide_sys::{self as sys, nix_err_NIX_OK}; /// Represents a store path that can be realized, queried, or manipulated. pub struct StorePath { pub(crate) inner: NonNull, - pub(crate) _context: Arc, } impl StorePath { @@ -21,36 +23,31 @@ impl StorePath { /// /// # Arguments /// - /// * `context` - The Nix context /// * `store` - The store containing the path /// * `path` - The store path string (e.g., "/nix/store/...") /// /// # Errors /// /// Returns an error if the path cannot be parsed. - pub fn parse( - context: &Arc, - store: &Store, - path: &str, - ) -> Result { - let path_cstring = CString::new(path).or(Err(NixErrorCode::InvalidArg { - location: "nixide::StorePath::parse", - reason: "`path` contains NUL char", - }))?; + pub fn parse(store: &Store, path: &str) -> Result { + let path_cstring = CString::new(path).or(Err(new_nixide_error!( + InvalidArg, + "path", + "contains a `\\0` (NUL) byte".to_owned() + )))?; - // SAFETY: context, store, and path_cstring are valid + let ctx = ErrorContext::new(); let path_ptr = unsafe { - sys::nix_store_parse_path(context.as_ptr(), store.as_ptr(), path_cstring.as_ptr()) + sys::nix_store_parse_path(ctx.as_ptr(), store.as_ptr(), path_cstring.as_ptr()) }; - let inner = NonNull::new(path_ptr).ok_or(NixErrorCode::NullPtr { - location: "nix_store_parse_path", - })?; - - Ok(Self { - inner, - _context: Arc::clone(context), - }) + match ctx.peak() { + Some(err) => Err(err), + None => match NonNull::new(path_ptr) { + Some(inner) => Ok(Self { inner }), + None => Err(new_nixide_error!(NullPtr)), + }, + } } /// Get the name component of the store path. @@ -61,8 +58,9 @@ impl StorePath { /// # Errors /// /// Returns an error if the name cannot be retrieved. - pub fn name(&self) -> Result { - wrap_libnix_string_callback("nix_store_path_name", |callback, user_data| unsafe { + /// + pub fn name(&self) -> Result { + wrap_libnix_string_callback(|_, callback, user_data| unsafe { sys::nix_store_path_name(self.inner.as_ptr(), Some(callback), user_data); // NOTE: nix_store_path_name doesn't return nix_err, so we force it to return successfully @@ -91,15 +89,21 @@ impl StorePath { /// /// * `store` - The store containing the path /// - pub fn real_path(&self, store: &Store) -> Result { - wrap_libnix_pathbuf_callback("nix_store_real_path", |callback, user_data| unsafe { - sys::nix_store_real_path( - self._context.as_ptr(), + pub fn real_path(&self, store: &Store) -> Result { + wrap_libnix_pathbuf_callback(|ctx, callback, user_data| unsafe { + let err_code = sys::nix_store_real_path( + ctx.as_ptr(), store.inner.as_ptr(), - self.inner.as_ptr(), + self.as_ptr(), Some(callback), user_data, - ) + ); + match ctx.pop() { + Some(err) => Err(err), + None => Ok(()), + } + + err_code }) } @@ -111,13 +115,12 @@ impl StorePath { /// * `store` - The store containing the path /// pub fn is_valid(&self, store: &Store) -> bool { + let ctx = ErrorContext::new(); unsafe { - sys::nix_store_is_valid_path( - self._context.as_ptr(), - store.inner.as_ptr(), - self.inner.as_ptr(), - ) + sys::nix_store_is_valid_path(ctx.as_ptr(), store.inner.as_ptr(), self.inner.as_ptr()) } + + ctx } /// Get the raw store path pointer. diff --git a/nixide/src/util/bindings.rs b/nixide/src/util/bindings.rs index cd8b4f8..50408e6 100644 --- a/nixide/src/util/bindings.rs +++ b/nixide/src/util/bindings.rs @@ -1,40 +1,47 @@ +use std::mem::MaybeUninit; use std::os::raw::{c_char, c_uint, c_void}; use std::path::PathBuf; -use crate::NixErrorCode; +use crate::errors::{ErrorContext, NixideError}; +use crate::util::CCharPtrNixExt; -pub fn wrap_libnix_string_callback( - name: &'static str, - callback: F, -) -> Result +pub fn wrap_libnix_string_callback(callback: F) -> Result where - F: FnOnce(unsafe extern "C" fn(*const c_char, c_uint, *mut c_void), *mut c_void) -> i32, + F: FnOnce( + &ErrorContext, + unsafe extern "C" fn(*const c_char, c_uint, *mut c_void), + *mut c_void, + ) -> i32, { // Callback to receive the string unsafe extern "C" fn wrapper_callback(start: *const c_char, n: c_uint, user_data: *mut c_void) { - let result = unsafe { &mut *(user_data as *mut Option) }; + let result = unsafe { &mut *(user_data as *mut Result) }; - if !start.is_null() && n > 0 { - let bytes = unsafe { std::slice::from_raw_parts(start.cast::(), n as usize) }; - if let Ok(s) = std::str::from_utf8(bytes) { - *result = Some(s.to_string()); - } - } + *result = start.to_utf8_string_sized(n as usize); } - let mut result: Option = None; - let user_data = &mut result as *mut _ as *mut c_void; + let ctx = ErrorContext::new(); + let mut user_data: MaybeUninit> = MaybeUninit::uninit(); - NixErrorCode::from(callback(wrapper_callback, user_data), name)?; - result.ok_or(NixErrorCode::NullPtr { location: name }) + callback( + &ctx, + wrapper_callback, + user_data.as_mut_ptr() as *mut c_void, + ); + if let Some(err) = ctx.peak() { + return Err(err); + } + + unsafe { user_data.assume_init() } } -pub fn wrap_libnix_pathbuf_callback( - name: &'static str, - callback: F, -) -> Result +pub fn wrap_libnix_pathbuf_callback(callback: F) -> Result where - F: FnOnce(unsafe extern "C" fn(*const c_char, c_uint, *mut c_void), *mut c_void) -> i32, + F: FnOnce( + &ErrorContext, + unsafe extern "C" fn(*const c_char, c_uint, *mut c_void), + *mut c_void, + ) -> i32, { - wrap_libnix_string_callback(name, callback).map(PathBuf::from) + wrap_libnix_string_callback(callback).map(PathBuf::from) } diff --git a/nixide/src/util/cchar_nix_ext.rs b/nixide/src/util/cchar_nix_ext.rs new file mode 100644 index 0000000..82b9fc8 --- /dev/null +++ b/nixide/src/util/cchar_nix_ext.rs @@ -0,0 +1,37 @@ +use std::ffi::{c_char, CStr}; +use std::slice::from_raw_parts; +use std::str::from_utf8; + +use crate::errors::{new_nixide_error, NixideError}; + +pub trait CCharPtrNixExt { + fn to_utf8_string(self) -> Result; + + fn to_utf8_string_sized(self, n: usize) -> Result; +} + +impl CCharPtrNixExt for *const c_char { + fn to_utf8_string(self) -> Result { + if self.is_null() { + return Err(new_nixide_error!(NullPtr)); + } + + let result = unsafe { CStr::from_ptr(self).to_str() }; + match result { + Ok(msg_str) => Ok(msg_str.to_string()), + Err(_) => Err(new_nixide_error!(StringNulByte)), + } + } + + fn to_utf8_string_sized(self, n: usize) -> Result { + if !self.is_null() && n > 0 { + let bytes = unsafe { from_raw_parts(self.cast::(), n as usize) }; + from_utf8(bytes) + .ok() + .map(|s| s.to_string()) + .ok_or(new_nixide_error!(NullPtr)) + } else { + Err(new_nixide_error!(NullPtr)) + } + } +} diff --git a/nixide/src/util/mod.rs b/nixide/src/util/mod.rs index 90c70dc..5635219 100644 --- a/nixide/src/util/mod.rs +++ b/nixide/src/util/mod.rs @@ -1 +1,23 @@ -pub mod bindings; +#[macro_use] +pub mod panic; +pub(crate) mod bindings; +mod cchar_nix_ext; +pub mod wrappers; + +pub use cchar_nix_ext::CCharPtrNixExt; +pub(crate) use panic::*; + +use crate::NixideError; + +pub trait AsErr { + fn as_err(self) -> Result<(), T>; +} + +impl AsErr for Option { + fn as_err(self) -> Result<(), NixideError> { + match self { + Some(err) => Err(err), + None => Ok(()), + } + } +} diff --git a/nixide/src/util/panic.rs b/nixide/src/util/panic.rs new file mode 100644 index 0000000..7a22501 --- /dev/null +++ b/nixide/src/util/panic.rs @@ -0,0 +1,17 @@ +macro_rules! panic_issue { + ($($arg:expr),*) => {{ + panic!( + "{}: please open an issue on https://github.com/cry128/nixide", + format!($($arg),*) + ) + }}; +} + +macro_rules! panic_issue_call_failed { + () => {{ + crate::util::panic_issue!("[nixide] call to `{}` failed", stdext::debug_name!()) + }}; +} + +pub(crate) use panic_issue; +pub(crate) use panic_issue_call_failed; diff --git a/nixide/src/util/wrappers.rs b/nixide/src/util/wrappers.rs new file mode 100644 index 0000000..1f6af3b --- /dev/null +++ b/nixide/src/util/wrappers.rs @@ -0,0 +1,16 @@ +pub trait AsInnerPtr { + /// Get a pointer to the underlying (`inner`) `libnix` C struct. + /// + /// # Safety + /// + /// Although this function isn't inherently `unsafe`, it is + /// marked as such intentionally to force calls to be wrapped + /// in `unsafe` blocks for clarity. + unsafe fn as_ptr(&self) -> *mut T; +} + +pub trait FromC { + /// Creates a new instance of [Self] from the underlying + /// libnix C type [T]. + unsafe fn from_c(value: T) -> Self; +} diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..3a45084 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,5 @@ +format_strings = true +group_imports = "Preserve" +imports_granularity = "Module" +reorder_impl_items = true +wrap_comments = true From 4f7743432047ca765603f2d529c8e290ed551590 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Sat, 21 Mar 2026 17:25:03 +1000 Subject: [PATCH 298/306] wow i got to pet some cute dogs today --- nixide/src/errors/context.rs | 56 ++++++++++++++++++++++-------- nixide/src/errors/error.rs | 3 +- nixide/src/lib.rs | 1 + nixide/src/stdext/cchar_ptr_ext.rs | 30 ++++++++++++++++ nixide/src/stdext/mod.rs | 4 +++ nixide/src/util/cchar_nix_ext.rs | 37 -------------------- 6 files changed, 78 insertions(+), 53 deletions(-) create mode 100644 nixide/src/stdext/cchar_ptr_ext.rs create mode 100644 nixide/src/stdext/mod.rs delete mode 100644 nixide/src/util/cchar_nix_ext.rs diff --git a/nixide/src/errors/context.rs b/nixide/src/errors/context.rs index 91c5cec..1b4c4a5 100644 --- a/nixide/src/errors/context.rs +++ b/nixide/src/errors/context.rs @@ -24,11 +24,11 @@ use std::ffi::c_uint; use std::ptr::NonNull; -use super::NixideError; +use super::{NixError, NixideError, NixideResult}; use crate::sys; use crate::util::bindings::wrap_libnix_string_callback; use crate::util::wrappers::AsInnerPtr; -use crate::util::CCharPtrNixExt; +use crate::util::{panic_issue_call_failed, CCharPtrNixExt}; /// This object stores error state. /// @@ -71,6 +71,34 @@ impl AsInnerPtr for ErrorContext { } } +impl Into> for &ErrorContext { + fn into(self) -> NixideResult<()> { + let inner = self.get_err().ok_?; + let msg = self.get_msg()?; + + let err = match inner { + sys::nix_err_NIX_OK => unreachable!(), + + sys::nix_err_NIX_ERR_OVERFLOW => NixError::Overflow, + sys::nix_err_NIX_ERR_KEY => NixError::KeyNotFound(None), + sys::nix_err_NIX_ERR_NIX_ERROR => NixError::ExprEval { + name: self + .get_nix_err_name() + .unwrap_or_else(|| panic_issue_call_failed!()), + + info_msg: self + .get_nix_err_info_msg() + .unwrap_or_else(|| panic_issue_call_failed!()), + }, + + sys::nix_err_NIX_ERR_UNKNOWN => NixError::Unknown, + err => NixError::Undocumented(err), + }; + + Some(new_nixide_error!(NixError, inner, err, msg)) + } +} + impl ErrorContext { /// Create a new error context. /// @@ -103,18 +131,19 @@ impl ErrorContext { } /// Check the error code and return an error if it's not `NIX_OK`. - pub fn peak(&self) -> Option { - NixideError::from_error_context(self) + pub fn peak(&self) -> NixideResult<()> { + match self.into() { + Some(err) => Err(err), + None => Ok(()), + } } /// /// Equivalent to running `self.peak()` then `self.clear()` - pub fn pop(&mut self) -> Option { - self.get_err().and_then(|_| { - let error = self.peak(); - self.clear(); - error - }) + pub fn pop(&mut self) -> NixideResult<()> { + let error = self.peak(); + self.clear(); + error } /// # Nix C++ API Internals @@ -151,10 +180,9 @@ impl ErrorContext { pub(super) fn get_err(&self) -> Option { let err = unsafe { sys::nix_err_code(self.as_ptr()) }; - if err == sys::nix_err_NIX_OK { - None - } else { - Some(err) + match err { + sys::nix_err_NIX_OK => None, + _ => Some(err), } } diff --git a/nixide/src/errors/error.rs b/nixide/src/errors/error.rs index b47694e..986527f 100644 --- a/nixide/src/errors/error.rs +++ b/nixide/src/errors/error.rs @@ -94,7 +94,7 @@ impl NixideError { /// # Panics /// /// This function will panic in the event that `context.get_err() == Some(err) && err == sys::nix_err_NIX_OK` - /// since `nixide::ErrorContext::get_err` is expected to return `None` to indicate `sys::ni_err_NIX_OK`. + /// since `nixide::ErrorContext::get_err` is expected to return `None` to indicate `sys::nix_err_NIX_OK`. /// /// /// This function will panic in the event that `value != sys::nix_err_NIX_OK` @@ -103,7 +103,6 @@ impl NixideError { let inner = context.get_err()?; let msg = context.get_msg()?; - #[allow(nonstandard_style)] let err = match inner { sys::nix_err_NIX_OK => unreachable!(), diff --git a/nixide/src/lib.rs b/nixide/src/lib.rs index 90ffab7..0ef0b16 100644 --- a/nixide/src/lib.rs +++ b/nixide/src/lib.rs @@ -3,6 +3,7 @@ pub(crate) mod errors; mod expr; mod flake; +mod stdext; mod store; pub(crate) mod util; mod verbosity; diff --git a/nixide/src/stdext/cchar_ptr_ext.rs b/nixide/src/stdext/cchar_ptr_ext.rs new file mode 100644 index 0000000..cd8bbfb --- /dev/null +++ b/nixide/src/stdext/cchar_ptr_ext.rs @@ -0,0 +1,30 @@ +use std::ffi::{c_char, CStr}; +use std::slice::from_raw_parts; +use std::str::{from_utf8, Utf8Error}; + +pub trait CCharPtrExt { + fn to_utf8_string(self) -> Result>; + + fn to_utf8_string_n(self, n: usize) -> Result>; +} + +impl CCharPtrExt for *const c_char { + fn to_utf8_string(self) -> Result> { + if self.is_null() { + return Err(None); + } + let cstr = unsafe { CStr::from_ptr(self) }; + match cstr.to_str() { + Ok(s) => Ok(s.to_owned()), + Err(err) => Err(Some(err)), + } + } + + fn to_utf8_string_n(self, n: usize) -> Result> { + if self.is_null() || n == 0 { + return Err(None); + } + let bytes = unsafe { from_raw_parts(self.cast::(), n as usize) }; + from_utf8(bytes).map(str::to_string).map_err(Some) + } +} diff --git a/nixide/src/stdext/mod.rs b/nixide/src/stdext/mod.rs new file mode 100644 index 0000000..fe17913 --- /dev/null +++ b/nixide/src/stdext/mod.rs @@ -0,0 +1,4 @@ +mod cchar_ptr_ext; + +pub(crate) use cchar_ptr_ext::CCharPtrExt; +pub(crate) use stdext::*; diff --git a/nixide/src/util/cchar_nix_ext.rs b/nixide/src/util/cchar_nix_ext.rs deleted file mode 100644 index 82b9fc8..0000000 --- a/nixide/src/util/cchar_nix_ext.rs +++ /dev/null @@ -1,37 +0,0 @@ -use std::ffi::{c_char, CStr}; -use std::slice::from_raw_parts; -use std::str::from_utf8; - -use crate::errors::{new_nixide_error, NixideError}; - -pub trait CCharPtrNixExt { - fn to_utf8_string(self) -> Result; - - fn to_utf8_string_sized(self, n: usize) -> Result; -} - -impl CCharPtrNixExt for *const c_char { - fn to_utf8_string(self) -> Result { - if self.is_null() { - return Err(new_nixide_error!(NullPtr)); - } - - let result = unsafe { CStr::from_ptr(self).to_str() }; - match result { - Ok(msg_str) => Ok(msg_str.to_string()), - Err(_) => Err(new_nixide_error!(StringNulByte)), - } - } - - fn to_utf8_string_sized(self, n: usize) -> Result { - if !self.is_null() && n > 0 { - let bytes = unsafe { from_raw_parts(self.cast::(), n as usize) }; - from_utf8(bytes) - .ok() - .map(|s| s.to_string()) - .ok_or(new_nixide_error!(NullPtr)) - } else { - Err(new_nixide_error!(NullPtr)) - } - } -} From a186b2297bce28b81686bee0ec35fa0595bf3197 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Tue, 24 Mar 2026 14:31:39 +1000 Subject: [PATCH 299/306] major changes to internal wrapper utilities --- nixide/src/errors/context.rs | 45 ++--- nixide/src/errors/error.rs | 79 +++----- nixide/src/errors/nix_error.rs | 30 ---- nixide/src/expr/value.rs | 8 +- nixide/src/flake/flake_reference.rs | 4 +- nixide/src/lib.rs | 6 +- nixide/src/stdext/cchar_ptr_ext.rs | 52 +++++- nixide/src/store/mod.rs | 267 ++++++++++++++-------------- nixide/src/store/path.rs | 84 +++------ nixide/src/store/tests.rs | 21 +-- nixide/src/util/bindings.rs | 47 ----- nixide/src/util/mod.rs | 45 +++-- nixide/src/util/panic.rs | 3 + nixide/src/util/wrap.rs | 147 +++++++++++++++ nixide/src/util/wrappers.rs | 15 ++ 15 files changed, 468 insertions(+), 385 deletions(-) delete mode 100644 nixide/src/util/bindings.rs create mode 100644 nixide/src/util/wrap.rs diff --git a/nixide/src/errors/context.rs b/nixide/src/errors/context.rs index 1b4c4a5..87039f5 100644 --- a/nixide/src/errors/context.rs +++ b/nixide/src/errors/context.rs @@ -24,11 +24,12 @@ use std::ffi::c_uint; use std::ptr::NonNull; -use super::{NixError, NixideError, NixideResult}; +use super::{NixError, NixideResult}; +use crate::stdext::CCharPtrExt as _; use crate::sys; -use crate::util::bindings::wrap_libnix_string_callback; +use crate::util::panic_issue_call_failed; +use crate::util::wrap; use crate::util::wrappers::AsInnerPtr; -use crate::util::{panic_issue_call_failed, CCharPtrNixExt}; /// This object stores error state. /// @@ -72,9 +73,22 @@ impl AsInnerPtr for ErrorContext { } impl Into> for &ErrorContext { + /// # Panics + /// + /// This function will panic in the event that `context.get_err() == Some(err) && err == sys::nix_err_NIX_OK` + /// since `nixide::ErrorContext::get_err` is expected to return `None` to indicate `sys::nix_err_NIX_OK`. + /// + /// This function will panic in the event that `value != sys::nix_err_NIX_OK` + /// but that `context.get_code() == sys::nix_err_NIX_OK` fn into(self) -> NixideResult<()> { - let inner = self.get_err().ok_?; - let msg = self.get_msg()?; + let inner = match self.get_err() { + Some(err) => err, + None => return Ok(()), + }; + let msg = match self.get_msg() { + Some(msg) => msg, + None => return Ok(()), + }; let err = match inner { sys::nix_err_NIX_OK => unreachable!(), @@ -95,7 +109,7 @@ impl Into> for &ErrorContext { err => NixError::Undocumented(err), }; - Some(new_nixide_error!(NixError, inner, err, msg)) + Err(new_nixide_error!(NixError, inner, err, msg)) } } @@ -132,18 +146,13 @@ impl ErrorContext { /// Check the error code and return an error if it's not `NIX_OK`. pub fn peak(&self) -> NixideResult<()> { - match self.into() { - Some(err) => Err(err), - None => Ok(()), - } + self.into() } /// /// Equivalent to running `self.peak()` then `self.clear()` pub fn pop(&mut self) -> NixideResult<()> { - let error = self.peak(); - self.clear(); - error + self.peak().and_then(|_| Ok(self.clear())) } /// # Nix C++ API Internals @@ -214,13 +223,12 @@ impl ErrorContext { /// Hence we can just test whether the returned pointer is a `NULL` pointer, /// and avoid passing in a [sys::nix_c_context] struct. pub(super) fn get_msg(&self) -> Option { - // XXX: TODO: what happens if i DO actually use `null_mut` instead of ErrorContext::new? does rust just panic? let ctx = ErrorContext::new(); unsafe { // NOTE: an Err here only occurs when `self.get_code() == Ok(())` let mut n: c_uint = 0; sys::nix_err_msg(ctx.as_ptr(), self.as_ptr(), &mut n) - .to_utf8_string() + .to_utf8_string_n(n as usize) .ok() } } @@ -261,10 +269,9 @@ impl ErrorContext { /// } /// ``` pub(super) fn get_nix_err_name(&self) -> Option { - // XXX: TODO: what happens if i DO actually use `null_mut` instead of ErrorContext::new? does rust just panic? unsafe { // NOTE: an Err here only occurs when "Last error was not a nix error" - wrap_libnix_string_callback(|ctx, callback, user_data| { + wrap::nix_string_callback(|callback, user_data, ctx| { sys::nix_err_name(ctx.as_ptr(), self.as_ptr(), Some(callback), user_data) }) .ok() @@ -307,10 +314,9 @@ impl ErrorContext { /// } /// ``` pub(super) fn get_nix_err_info_msg(&self) -> Option { - // XXX: TODO: what happens if i DO actually use `null_mut` instead of ErrorContext::new? does rust just panic? unsafe { // NOTE: an Err here only occurs when "Last error was not a nix error" - wrap_libnix_string_callback(|ctx, callback, user_data| { + wrap::nix_string_callback(|callback, user_data, ctx| { sys::nix_err_info_msg(ctx.as_ptr(), self.as_ptr(), Some(callback), user_data) }) .ok() @@ -320,7 +326,6 @@ impl ErrorContext { impl Drop for ErrorContext { fn drop(&mut self) { - // SAFETY: We own the context and it's valid until drop unsafe { sys::nix_c_context_free(self.inner.as_ptr()); } diff --git a/nixide/src/errors/error.rs b/nixide/src/errors/error.rs index 986527f..bcebcf8 100644 --- a/nixide/src/errors/error.rs +++ b/nixide/src/errors/error.rs @@ -1,8 +1,7 @@ use std::fmt::{Display, Formatter, Result as FmtResult}; -use super::{ErrorContext, NixError}; +use super::NixError; use crate::sys; -use crate::util::panic_issue_call_failed; pub type NixideResult = Result; @@ -44,38 +43,38 @@ pub enum NixideError { macro_rules! new_nixide_error { (NixError, $inner:expr, $err:expr, $msg:expr) => {{ - NixideError::NixError { - trace: stdext::debug_name!(), + crate::NixideError::NixError { + trace: ::stdext::debug_name!(), inner: $inner, err: $err, msg: $msg, } }}; (StringNulByte) => {{ - NixideError::StringNulByte { - trace: stdext::debug_name!(), + crate::NixideError::StringNulByte { + trace: ::stdext::debug_name!(), } }}; (StringNotUtf8) => {{ - NixideError::StringNotUtf8 { - trace: stdext::debug_name!(), + crate::NixideError::StringNotUtf8 { + trace: ::stdext::debug_name!(), } }}; (NullPtr) => {{ - NixideError::NullPtr { - trace: stdext::debug_name!(), + crate::NixideError::NullPtr { + trace: ::stdext::debug_name!(), } }}; (InvalidArg, $name:expr, $reason:expr) => {{ - NixideError::InvalidArg { - trace: stdext::debug_name!(), + crate::NixideError::InvalidArg { + trace: ::stdext::debug_name!(), name: $name, reason: $reason, } }}; (InvalidType, $expected:expr, $got:expr) => {{ - NixideError::InvalidType { - trace: stdext::debug_name!(), + crate::NixideError::InvalidType { + trace: ::stdext::debug_name!(), expected: $expected, got: $got, } @@ -83,49 +82,14 @@ macro_rules! new_nixide_error { } pub(crate) use new_nixide_error; +#[allow(unused_macros)] macro_rules! retrace_nixide_error { ($x:expr) => {{ - new_nixide_error!($x.err) + crate::errors::new_nixide_error!($x.err) }}; } pub(crate) use retrace_nixide_error; -impl NixideError { - /// # Panics - /// - /// This function will panic in the event that `context.get_err() == Some(err) && err == sys::nix_err_NIX_OK` - /// since `nixide::ErrorContext::get_err` is expected to return `None` to indicate `sys::nix_err_NIX_OK`. - /// - /// - /// This function will panic in the event that `value != sys::nix_err_NIX_OK` - /// but that `context.get_code() == sys::nix_err_NIX_OK` - pub(super) fn from_error_context(context: &ErrorContext) -> Option { - let inner = context.get_err()?; - let msg = context.get_msg()?; - - let err = match inner { - sys::nix_err_NIX_OK => unreachable!(), - - sys::nix_err_NIX_ERR_OVERFLOW => NixError::Overflow, - sys::nix_err_NIX_ERR_KEY => NixError::KeyNotFound(None), - sys::nix_err_NIX_ERR_NIX_ERROR => NixError::ExprEval { - name: context - .get_nix_err_name() - .unwrap_or_else(|| panic_issue_call_failed!()), - - info_msg: context - .get_nix_err_info_msg() - .unwrap_or_else(|| panic_issue_call_failed!()), - }, - - sys::nix_err_NIX_ERR_UNKNOWN => NixError::Unknown, - err => NixError::Undocumented(err), - }; - - Some(new_nixide_error!(NixError, inner, err, msg)) - } -} - impl std::error::Error for NixideError {} impl Display for NixideError { @@ -172,3 +136,16 @@ impl Display for NixideError { } } } + +pub trait AsErr { + fn as_err(self) -> Result<(), T>; +} + +impl AsErr for Option { + fn as_err(self) -> Result<(), NixideError> { + match self { + Some(err) => Err(err), + None => Ok(()), + } + } +} diff --git a/nixide/src/errors/nix_error.rs b/nixide/src/errors/nix_error.rs index 39e6825..5e3938e 100644 --- a/nixide/src/errors/nix_error.rs +++ b/nixide/src/errors/nix_error.rs @@ -98,36 +98,6 @@ pub enum NixError { Undocumented(sys::nix_err), } -// impl NixError { -// /// # Panics -// /// -// /// This function will panic in the event that `context.get_err() == Some(err) && err == sys::nix_err_NIX_OK` -// /// since `nixide::ErrorContext::get_err` is expected to return `None` to indicate `sys::ni_err_NIX_OK`. -// /// -// /// -// /// This function will panic in the event that `value != sys::nix_err_NIX_OK` -// /// but that `context.get_code() == sys::nix_err_NIX_OK` -// pub(super) fn from_error_context(context: &ErrorContext) -> Option { -// #[allow(nonstandard_style)] -// match context.get_err()? { -// sys::nix_err_NIX_OK => unreachable!("call to `nixide::ErrorContext::get_err@nixide::NixError::from_context` failed: please open an issue on https://github.com/cry128/nixide"), - -// sys::nix_err_NIX_ERR_OVERFLOW => Some(NixError::Overflow), -// sys::nix_err_NIX_ERR_KEY => Some(NixError::KeyNotFound(None)), -// sys::nix_err_NIX_ERR_NIX_ERROR => Some(NixError::ExprEval { -// name: context -// .get_nix_err_name() -// .expect("call to `nixide::ErrorContext::get_nix_err_name@nixide::NixError::from_context` failed: please open an issue on https://github.com/cry128/nixide"), -// info_msg: context.get_nix_err_info_msg() -// .expect("call to `nixide::ErrorContext::get_nix_err_info_msg@nixide::NixError::from_context` failed: please open an issue on https://github.com/cry128/nixide"), -// }), - -// sys::nix_err_NIX_ERR_UNKNOWN => Some(NixError::Unknown), -// err => Some(NixError::Undocumented(err)), -// } -// } -// } - impl Display for NixError { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { match self { diff --git a/nixide/src/expr/value.rs b/nixide/src/expr/value.rs index 48a10e4..93f3787 100644 --- a/nixide/src/expr/value.rs +++ b/nixide/src/expr/value.rs @@ -40,7 +40,7 @@ impl Value { let ctx = ErrorContext::new(); unsafe { sys::nix_value_force(ctx.as_ptr(), state.as_ptr(), self.as_ptr()) }; - ctx.peak().as_err() + ctx.peak() } /// Force deep evaluation of this value. @@ -54,7 +54,7 @@ impl Value { let ctx = ErrorContext::new(); unsafe { sys::nix_value_force_deep(ctx.as_ptr(), state.as_ptr(), self.as_ptr()) }; - ctx.peak().as_err() + ctx.peak() } /// Get the type of this value. @@ -66,9 +66,7 @@ impl Value { // NOTE: an error here only occurs if `nix_get_type` catches an error, // NOTE: which in turn only happens if the `sys::nix_value*` is a null pointer // NOTE: or points to an uninitialised `nix_value` struct. - ctx.peak() - .as_err() - .unwrap_or_else(|_| panic!("TODO im sleepy rn")); + ctx.peak().unwrap_or_else(|_| panic!("TODO im sleepy rn")); value_type } diff --git a/nixide/src/flake/flake_reference.rs b/nixide/src/flake/flake_reference.rs index 3979e2a..c60a950 100644 --- a/nixide/src/flake/flake_reference.rs +++ b/nixide/src/flake/flake_reference.rs @@ -4,7 +4,7 @@ use std::ptr::{null_mut, NonNull}; use super::{FetchersSettings, FlakeReferenceParseFlags, FlakeSettings}; use crate::errors::new_nixide_error; use crate::sys; -use crate::util::bindings::wrap_libnix_string_callback; +use crate::util::bindings::wrap_nix_string_callback; use crate::util::wrappers::AsInnerPtr; use crate::NixideError; @@ -36,7 +36,7 @@ impl FlakeReference { reference: &str, ) -> Result<(FlakeReference, String), NixideError> { let mut ptr: *mut sys::nix_flake_reference = null_mut(); - let result = wrap_libnix_string_callback(|ctx, callback, user_data| unsafe { + let result = wrap_nix_string_callback(|ctx, callback, user_data| unsafe { sys::nix_flake_reference_and_fragment_from_string( ctx.as_ptr(), fetch_settings.as_ptr(), diff --git a/nixide/src/lib.rs b/nixide/src/lib.rs index 0ef0b16..05a1738 100644 --- a/nixide/src/lib.rs +++ b/nixide/src/lib.rs @@ -1,8 +1,8 @@ // #![warn(missing_docs)] pub(crate) mod errors; -mod expr; -mod flake; +// mod expr; +// mod flake; mod stdext; mod store; pub(crate) mod util; @@ -10,7 +10,7 @@ mod verbosity; mod version; pub use errors::{NixError, NixideError, NixideResult}; -pub use expr::{EvalState, EvalStateBuilder, Value, ValueType}; +// pub use expr::{EvalState, EvalStateBuilder, Value, ValueType}; pub use store::{Store, StorePath}; pub use verbosity::NixVerbosity; pub use version::NixVersion; diff --git a/nixide/src/stdext/cchar_ptr_ext.rs b/nixide/src/stdext/cchar_ptr_ext.rs index cd8bbfb..a4af4fe 100644 --- a/nixide/src/stdext/cchar_ptr_ext.rs +++ b/nixide/src/stdext/cchar_ptr_ext.rs @@ -1,30 +1,64 @@ use std::ffi::{c_char, CStr}; use std::slice::from_raw_parts; -use std::str::{from_utf8, Utf8Error}; +use std::str::from_utf8; + +use crate::errors::new_nixide_error; +use crate::NixideResult; pub trait CCharPtrExt { - fn to_utf8_string(self) -> Result>; + fn to_utf8_string(self) -> NixideResult; - fn to_utf8_string_n(self, n: usize) -> Result>; + fn to_utf8_string_n(self, n: usize) -> NixideResult; } impl CCharPtrExt for *const c_char { - fn to_utf8_string(self) -> Result> { + fn to_utf8_string(self) -> NixideResult { if self.is_null() { - return Err(None); + return Err(new_nixide_error!(NullPtr)); } let cstr = unsafe { CStr::from_ptr(self) }; match cstr.to_str() { Ok(s) => Ok(s.to_owned()), - Err(err) => Err(Some(err)), + Err(_) => Err(new_nixide_error!(StringNotUtf8)), } } - fn to_utf8_string_n(self, n: usize) -> Result> { + fn to_utf8_string_n(self, n: usize) -> NixideResult { if self.is_null() || n == 0 { - return Err(None); + return Err(new_nixide_error!(NullPtr)); } let bytes = unsafe { from_raw_parts(self.cast::(), n as usize) }; - from_utf8(bytes).map(str::to_string).map_err(Some) + match from_utf8(bytes) { + Ok(s) => Ok(s.to_string()), + Err(_) => Err(new_nixide_error!(StringNotUtf8)), + } } } + +// XXX: TODO: remove if unused +// pub trait CCharPtrExt { +// fn to_utf8_string(self) -> Result>; +// +// fn to_utf8_string_n(self, n: usize) -> Result>; +// } +// +// impl CCharPtrExt for *const c_char { +// fn to_utf8_string(self) -> Result> { +// if self.is_null() { +// return Err(None); +// } +// let cstr = unsafe { CStr::from_ptr(self) }; +// match cstr.to_str() { +// Ok(s) => Ok(s.to_owned()), +// Err(err) => Err(Some(err)), +// } +// } +// +// fn to_utf8_string_n(self, n: usize) -> Result> { +// if self.is_null() || n == 0 { +// return Err(None); +// } +// let bytes = unsafe { from_raw_parts(self.cast::(), n as usize) }; +// from_utf8(bytes).map(str::to_string).map_err(Some) +// } +// } diff --git a/nixide/src/store/mod.rs b/nixide/src/store/mod.rs index 9aa7f44..e98595d 100644 --- a/nixide/src/store/mod.rs +++ b/nixide/src/store/mod.rs @@ -16,15 +16,17 @@ mod tests; mod path; pub use path::*; -use std::ffi::{CStr, CString, NulError}; -use std::os::raw::{c_char, c_void}; +use std::ffi::CString; +use std::os::raw::c_char; use std::path::PathBuf; -use std::ptr::NonNull; +use std::ptr::{null, null_mut, NonNull}; use std::result::Result; -use std::sync::Arc; -use super::{ErrorContext, NixErrorCode}; -use crate::util::bindings::{wrap_libnix_pathbuf_callback, wrap_libnix_string_callback}; +use crate::errors::{new_nixide_error, ErrorContext}; +use crate::stdext::CCharPtrExt; +use crate::util::wrap; +use crate::util::wrappers::AsInnerPtr; +use crate::{NixideError, NixideResult}; use nixide_sys as sys; /// Nix store for managing packages and derivations. @@ -32,7 +34,12 @@ use nixide_sys as sys; /// The store provides access to Nix packages, derivations, and store paths. pub struct Store { pub(crate) inner: NonNull, - pub(crate) _context: Arc, +} + +impl AsInnerPtr for Store { + unsafe fn as_ptr(&self) -> *mut sys::Store { + self.inner.as_ptr() + } } impl Store { @@ -46,36 +53,19 @@ impl Store { /// # Errors /// /// Returns an error if the store cannot be opened. - pub fn open(context: &Arc, uri: Option<&str>) -> Result { - let uri_cstring: CString; - let uri_ptr = if let Some(uri) = uri { - uri_cstring = NixErrorCode::from_nulerror(CString::new(uri), "nixide::Store::open")?; - uri_cstring.as_ptr() - } else { - std::ptr::null() + pub fn open(uri: Option<&str>) -> Result { + let uri_ptr = match uri.map(CString::new) { + Some(Ok(c_uri)) => c_uri.as_ptr(), + Some(Err(_)) => Err(new_nixide_error!(StringNulByte))?, + None => null(), }; - // SAFETY: context is valid, uri_ptr is either null or valid CString - let store_ptr = - unsafe { sys::nix_store_open(context.as_ptr(), uri_ptr, std::ptr::null_mut()) }; - - let inner = NonNull::new(store_ptr).ok_or(NixErrorCode::NullPtr { - location: "nix_store_open", + let inner = wrap::nix_ptr_fn!(|ctx: &ErrorContext| unsafe { + // XXX: TODO: allow args to be parsed instead of just `null_mut` + sys::nix_store_open(ctx.as_ptr(), uri_ptr, null_mut()) })?; - Ok(Store { - inner, - _context: Arc::clone(context), - }) - } - - /// Get the raw store pointer. - /// - /// # Safety - /// - /// The caller must ensure the pointer is used safely. - pub(crate) unsafe fn as_ptr(&self) -> *mut sys::Store { - self.inner.as_ptr() + Ok(Store { inner }) } /// Realize a store path. @@ -99,72 +89,100 @@ impl Store { &self, path: &StorePath, callback: fn(&str, &StorePath), - ) -> Result, NixErrorCode> { - // Type alias for our userdata: (outputs vector, context) - type Userdata = ( - Vec<(String, StorePath)>, - Arc, - fn(&str, &StorePath), - ); + ) -> NixideResult<(String, StorePath)> { + // // Type alias for our userdata: (outputs vector, context) + // type Userdata = ( + // Vec<(String, StorePath)>, + // Arc, + // fn(&str, &StorePath), + // ); + // + // // Callback function that will be called for each realized output + // unsafe extern "C" fn realise_callback( + // userdata: *mut c_void, + // out_name_ptr: *const c_char, + // out_path_ptr: *const sys::StorePath, + // ) { + // // SAFETY: userdata is a valid pointer to our (Vec, Arc) tuple + // let (outputs, ctx, callback) = unsafe { &mut *(userdata as *mut Userdata) }; + // + // // SAFETY: outname is a valid C string from Nix + // let output_name = if !out_name_ptr.is_null() { + // unsafe { CStr::from_ptr(out_name_ptr).to_string_lossy().into_owned() } + // } else { + // String::from("out") // Default output name + // }; + // + // // SAFETY: out is a valid StorePath pointer from Nix, we need to clone it + // // because Nix owns the original and may free it after the callback + // if !out_path_ptr.is_null() { + // let cloned_path_ptr = + // unsafe { sys::nix_store_path_clone(out_path_ptr as *mut sys::StorePath) }; + // if let Some(inner) = NonNull::new(cloned_path_ptr) { + // let store_path = StorePath { inner }; + // + // callback(output_name.as_ref(), &store_path); + // + // outputs.push((output_name, store_path)); + // } + // } + // } + // + // // Create userdata with empty outputs vector and context + // let mut userdata: Userdata = (Vec::new(), Arc::new(ErrorContext::new()), callback); + // let userdata_ptr = &mut userdata as *mut Userdata as *mut std::os::raw::c_void; + // + // // SAFETY: All pointers are valid, callback is compatible with the FFI signature + // // - self._context is valid for the duration of this call + // // - self.inner is valid (checked in Store::open) + // // - path.inner is valid (checked in StorePath::parse) + // // - userdata_ptr points to valid stack memory + // // - realize_callback matches the expected C function signature + // let err = unsafe { + // sys::nix_store_realise( + // ctx.as_ptr(), + // self.inner.as_ptr(), + // path.as_ptr(), + // userdata_ptr, + // Some(realise_callback), + // ) + // }; - // Callback function that will be called for each realized output - unsafe extern "C" fn realise_callback( - userdata: *mut c_void, - out_name_ptr: *const c_char, - out_path_ptr: *const sys::StorePath, - ) { - // SAFETY: userdata is a valid pointer to our (Vec, Arc) tuple - let (outputs, context, callback) = unsafe { &mut *(userdata as *mut Userdata) }; + wrap::nix_callback!( + |userdata ; + output_name_ptr: *const c_char, + output_path_ptr: *const sys::StorePath| + -> NixideResult<(String, StorePath)> { + // XXX: TODO: test to see if this is ever null ("out" as a default feels unsafe...) + // let output_name = output_name_ptr.to_utf8_string().unwrap_or("out".to_owned()); + let output_name = output_name_ptr.to_utf8_string()?; - // SAFETY: outname is a valid C string from Nix - let output_name = if !out_name_ptr.is_null() { - unsafe { CStr::from_ptr(out_name_ptr).to_string_lossy().into_owned() } - } else { - String::from("out") // Default output name - }; - - // SAFETY: out is a valid StorePath pointer from Nix, we need to clone it - // because Nix owns the original and may free it after the callback - if !out_path_ptr.is_null() { - let cloned_path_ptr = - unsafe { sys::nix_store_path_clone(out_path_ptr as *mut sys::StorePath) }; - if let Some(inner) = NonNull::new(cloned_path_ptr) { - let store_path = StorePath { - inner, - _context: Arc::clone(context), - }; + // SAFETY: out is a valid StorePath pointer from Nix, we need to clone it + // because Nix owns the original and may free it after the callback + if !output_path_ptr.is_null() { + let inner = wrap::nix_ptr_fn!(|ctx| unsafe { + sys::nix_store_path_clone(output_path_ptr as *mut sys::StorePath) + })?; + let store_path = StorePath { inner }; callback(output_name.as_ref(), &store_path); - outputs.push((output_name, store_path)); + Ok((output_name, store_path)) + } else { + panic!("IDK YET"); + // XXX: TODO: how should `output_path_ptr.is_null()` be handled? } + }, + |callback, userdata, ctx: &ErrorContext| unsafe { + sys::nix_store_realise( + ctx.as_ptr(), + self.inner.as_ptr(), + path.as_ptr(), + userdata, + Some(callback), + ) } - } - - // Create userdata with empty outputs vector and context - let mut userdata: Userdata = (Vec::new(), Arc::clone(&self._context), callback); - let userdata_ptr = &mut userdata as *mut Userdata as *mut std::os::raw::c_void; - - // SAFETY: All pointers are valid, callback is compatible with the FFI signature - // - self._context is valid for the duration of this call - // - self.inner is valid (checked in Store::open) - // - path.inner is valid (checked in StorePath::parse) - // - userdata_ptr points to valid stack memory - // - realize_callback matches the expected C function signature - let err = unsafe { - sys::nix_store_realise( - self._context.as_ptr(), - self.inner.as_ptr(), - path.as_ptr(), - userdata_ptr, - Some(realise_callback), - ) - }; - - NixErrorCode::result_from(err, "nix_store_realise")?; - - // Return the collected outputs - Ok(userdata.0) + ) } /// Parse a store path string into a StorePath. @@ -191,40 +209,30 @@ impl Store { /// # Ok(()) /// # } /// ``` - pub fn store_path(&self, path: &str) -> Result { - StorePath::parse(&self._context, self, path) + pub fn store_path(&self, path: &str) -> Result { + StorePath::parse(self, path) } /// Get the version of a Nix store /// /// If the store doesn't have a version (like the dummy store), returns None - pub fn version(&self) -> Result { - wrap_libnix_string_callback("nix_store_get_version", |callback, user_data| unsafe { - sys::nix_store_get_version( - self._context.as_ptr(), - self.inner.as_ptr(), - Some(callback), - user_data, - ) + pub fn version(&self) -> Result { + wrap::nix_string_callback(|callback, user_data, ctx| unsafe { + sys::nix_store_get_version(ctx.as_ptr(), self.inner.as_ptr(), Some(callback), user_data) }) } /// Get the URI of a Nix store - pub fn uri(&self) -> Result { - wrap_libnix_string_callback("nix_store_get_uri", |callback, user_data| unsafe { - sys::nix_store_get_uri( - self._context.as_ptr(), - self.inner.as_ptr(), - Some(callback), - user_data, - ) + pub fn uri(&self) -> Result { + wrap::nix_string_callback(|callback, user_data, ctx| unsafe { + sys::nix_store_get_uri(ctx.as_ptr(), self.inner.as_ptr(), Some(callback), user_data) }) } - pub fn store_dir(&self) -> Result { - wrap_libnix_pathbuf_callback("nix_store_get_storedir", |callback, user_data| unsafe { + pub fn store_dir(&self) -> Result { + wrap::nix_pathbuf_callback(|callback, user_data, ctx| unsafe { sys::nix_store_get_storedir( - self._context.as_ptr(), + ctx.as_ptr(), self.inner.as_ptr(), Some(callback), user_data, @@ -236,38 +244,35 @@ impl Store { &self, dst_store: &Store, store_path: &StorePath, - ) -> Result<(), NixErrorCode> { - let err = unsafe { + ) -> Result<(), NixideError> { + wrap::nix_fn!(|ctx: &ErrorContext| unsafe { sys::nix_store_copy_closure( - self._context.as_ptr(), - self.inner.as_ptr(), - dst_store.inner.as_ptr(), - store_path.inner.as_ptr(), - ) - }; - NixErrorCode::result_from(err, "nix_store_copy_closure") + ctx.as_ptr(), + self.as_ptr(), + dst_store.as_ptr(), + store_path.as_ptr(), + ); // semi-colon to return () and not i32 + }) } pub fn copy_closure_from( &self, src_store: &Store, store_path: &StorePath, - ) -> Result<(), NixErrorCode> { - let err = unsafe { + ) -> Result<(), NixideError> { + wrap::nix_fn!(|ctx: &ErrorContext| unsafe { sys::nix_store_copy_closure( - self._context.as_ptr(), - src_store.inner.as_ptr(), - self.inner.as_ptr(), + ctx.as_ptr(), + src_store.as_ptr(), + self.as_ptr(), store_path.inner.as_ptr(), - ) - }; - NixErrorCode::result_from(err, "nix_store_copy_closure") + ); + }) // semi-colon to return () and not i32 } } impl Drop for Store { fn drop(&mut self) { - // SAFETY: We own the store and it's valid until drop unsafe { sys::nix_store_free(self.inner.as_ptr()); } diff --git a/nixide/src/store/path.rs b/nixide/src/store/path.rs index c298eb4..9696481 100644 --- a/nixide/src/store/path.rs +++ b/nixide/src/store/path.rs @@ -1,11 +1,11 @@ use std::ffi::CString; use std::path::PathBuf; use std::ptr::NonNull; -use std::sync::Arc; use super::Store; use crate::errors::{new_nixide_error, ErrorContext}; -use crate::util::bindings::{wrap_libnix_pathbuf_callback, wrap_libnix_string_callback}; +use crate::util::panic_issue_call_failed; +use crate::util::wrap; use crate::util::wrappers::AsInnerPtr; use crate::NixideError; @@ -14,10 +14,17 @@ use nixide_sys::{self as sys, nix_err_NIX_OK}; /// A path in the Nix store. /// /// Represents a store path that can be realized, queried, or manipulated. +/// pub struct StorePath { pub(crate) inner: NonNull, } +impl AsInnerPtr for StorePath { + unsafe fn as_ptr(&self) -> *mut sys::StorePath { + self.inner.as_ptr() + } +} + impl StorePath { /// Parse a store path string into a StorePath. /// @@ -30,24 +37,13 @@ impl StorePath { /// /// Returns an error if the path cannot be parsed. pub fn parse(store: &Store, path: &str) -> Result { - let path_cstring = CString::new(path).or(Err(new_nixide_error!( - InvalidArg, - "path", - "contains a `\\0` (NUL) byte".to_owned() - )))?; + let c_path = CString::new(path).or(Err(new_nixide_error!(StringNulByte)))?; - let ctx = ErrorContext::new(); - let path_ptr = unsafe { - sys::nix_store_parse_path(ctx.as_ptr(), store.as_ptr(), path_cstring.as_ptr()) - }; + let inner = wrap::nix_ptr_fn!(|ctx: &ErrorContext| unsafe { + sys::nix_store_parse_path(ctx.as_ptr(), store.as_ptr(), c_path.as_ptr()) + })?; - match ctx.peak() { - Some(err) => Err(err), - None => match NonNull::new(path_ptr) { - Some(inner) => Ok(Self { inner }), - None => Err(new_nixide_error!(NullPtr)), - }, - } + Ok(Self { inner }) } /// Get the name component of the store path. @@ -60,9 +56,8 @@ impl StorePath { /// Returns an error if the name cannot be retrieved. /// pub fn name(&self) -> Result { - wrap_libnix_string_callback(|_, callback, user_data| unsafe { + wrap::nix_string_callback(|callback, user_data, _| unsafe { sys::nix_store_path_name(self.inner.as_ptr(), Some(callback), user_data); - // NOTE: nix_store_path_name doesn't return nix_err, so we force it to return successfully nix_err_NIX_OK }) @@ -90,20 +85,14 @@ impl StorePath { /// * `store` - The store containing the path /// pub fn real_path(&self, store: &Store) -> Result { - wrap_libnix_pathbuf_callback(|ctx, callback, user_data| unsafe { - let err_code = sys::nix_store_real_path( + wrap::nix_pathbuf_callback(|callback, user_data, ctx| unsafe { + sys::nix_store_real_path( ctx.as_ptr(), store.inner.as_ptr(), self.as_ptr(), Some(callback), user_data, - ); - match ctx.pop() { - Some(err) => Err(err), - None => Ok(()), - } - - err_code + ) }) } @@ -115,45 +104,28 @@ impl StorePath { /// * `store` - The store containing the path /// pub fn is_valid(&self, store: &Store) -> bool { - let ctx = ErrorContext::new(); - unsafe { - sys::nix_store_is_valid_path(ctx.as_ptr(), store.inner.as_ptr(), self.inner.as_ptr()) - } - - ctx - } - - /// Get the raw store path pointer. - /// - /// # Safety - /// - /// The caller must ensure the pointer is used safely. - pub(crate) unsafe fn as_ptr(&self) -> *mut sys::StorePath { - self.inner.as_ptr() + wrap::nix_fn!(|ctx: &ErrorContext| unsafe { + sys::nix_store_is_valid_path(ctx.as_ptr(), store.as_ptr(), self.as_ptr()) + }) + .is_ok() } } impl Clone for StorePath { fn clone(&self) -> Self { - // SAFETY: self.inner is valid, nix_store_path_clone creates a new copy - let cloned_ptr = unsafe { sys::nix_store_path_clone(self.inner.as_ptr()) }; + let inner = wrap::nix_ptr_fn!(|_| unsafe { sys::nix_store_path_clone(self.as_ptr()) }) + .unwrap_or_else(|_| { + panic_issue_call_failed!("nix_store_path_clone returned None for valid path") + }); - // This should never fail as cloning a valid path should always succeed - let inner = - NonNull::new(cloned_ptr).expect("nix_store_path_clone returned null for valid path"); - - StorePath { - inner, - _context: Arc::clone(&self._context), - } + StorePath { inner } } } impl Drop for StorePath { fn drop(&mut self) { - // SAFETY: We own the store path and it's valid until drop unsafe { - sys::nix_store_path_free(self.inner.as_ptr()); + sys::nix_store_path_free(self.as_ptr()); } } } diff --git a/nixide/src/store/tests.rs b/nixide/src/store/tests.rs index 4fdef06..3e92b6c 100644 --- a/nixide/src/store/tests.rs +++ b/nixide/src/store/tests.rs @@ -5,23 +5,17 @@ use super::*; #[test] #[serial] fn test_store_opening() { - let ctx = Arc::new(ErrorContext::new().expect("Failed to create context")); - let _store = Store::open(&ctx, None).expect("Failed to open store"); + let _store = Store::open(None).expect("Failed to open store"); } #[test] #[serial] fn test_store_path_parse() { - let ctx = Arc::new(ErrorContext::new().expect("Failed to create context")); - let store = Store::open(&ctx, None).expect("Failed to open store"); + let store = Store::open(None).expect("Failed to open store"); // Try parsing a well-formed store path // Note: This may fail if the path doesn't exist in the store - let result = StorePath::parse( - &ctx, - &store, - "/nix/store/00000000000000000000000000000000-test", - ); + let result = StorePath::parse(&store, "/nix/store/00000000000000000000000000000000-test"); // We don't assert success here because the path might not exist // This test mainly checks that the API works correctly @@ -38,16 +32,11 @@ fn test_store_path_parse() { #[test] #[serial] fn test_store_path_clone() { - let ctx = Arc::new(ErrorContext::new().expect("Failed to create context")); - let store = Store::open(&ctx, None).expect("Failed to open store"); + let store = Store::open(None).expect("Failed to open store"); // Try to get a valid store path by parsing // Note: This test is somewhat limited without a guaranteed valid path - if let Ok(path) = StorePath::parse( - &ctx, - &store, - "/nix/store/00000000000000000000000000000000-test", - ) { + if let Ok(path) = StorePath::parse(&store, "/nix/store/00000000000000000000000000000000-test") { let cloned = path.clone(); // Assert that the cloned path has the same name as the original diff --git a/nixide/src/util/bindings.rs b/nixide/src/util/bindings.rs deleted file mode 100644 index 50408e6..0000000 --- a/nixide/src/util/bindings.rs +++ /dev/null @@ -1,47 +0,0 @@ -use std::mem::MaybeUninit; -use std::os::raw::{c_char, c_uint, c_void}; -use std::path::PathBuf; - -use crate::errors::{ErrorContext, NixideError}; -use crate::util::CCharPtrNixExt; - -pub fn wrap_libnix_string_callback(callback: F) -> Result -where - F: FnOnce( - &ErrorContext, - unsafe extern "C" fn(*const c_char, c_uint, *mut c_void), - *mut c_void, - ) -> i32, -{ - // Callback to receive the string - unsafe extern "C" fn wrapper_callback(start: *const c_char, n: c_uint, user_data: *mut c_void) { - let result = unsafe { &mut *(user_data as *mut Result) }; - - *result = start.to_utf8_string_sized(n as usize); - } - - let ctx = ErrorContext::new(); - let mut user_data: MaybeUninit> = MaybeUninit::uninit(); - - callback( - &ctx, - wrapper_callback, - user_data.as_mut_ptr() as *mut c_void, - ); - if let Some(err) = ctx.peak() { - return Err(err); - } - - unsafe { user_data.assume_init() } -} - -pub fn wrap_libnix_pathbuf_callback(callback: F) -> Result -where - F: FnOnce( - &ErrorContext, - unsafe extern "C" fn(*const c_char, c_uint, *mut c_void), - *mut c_void, - ) -> i32, -{ - wrap_libnix_string_callback(callback).map(PathBuf::from) -} diff --git a/nixide/src/util/mod.rs b/nixide/src/util/mod.rs index 5635219..2302b41 100644 --- a/nixide/src/util/mod.rs +++ b/nixide/src/util/mod.rs @@ -1,23 +1,38 @@ #[macro_use] pub mod panic; -pub(crate) mod bindings; -mod cchar_nix_ext; +pub(crate) mod wrap; pub mod wrappers; -pub use cchar_nix_ext::CCharPtrNixExt; pub(crate) use panic::*; -use crate::NixideError; +// use crate::NixideError; -pub trait AsErr { - fn as_err(self) -> Result<(), T>; -} +// pub trait AsErr { +// fn as_err(self) -> Result<(), T>; +// } -impl AsErr for Option { - fn as_err(self) -> Result<(), NixideError> { - match self { - Some(err) => Err(err), - None => Ok(()), - } - } -} +// impl AsErr for Option { +// fn as_err(self) -> Result<(), NixideError> { +// match self { +// Some(err) => Err(err), +// None => Ok(()), +// } +// } +// } + +// pub trait AsInnerPtr { +// /// Get a pointer to the underlying (`inner`) `libnix` C struct. +// /// +// /// # Safety +// /// +// /// Although this function isn't inherently `unsafe`, it is +// /// marked as such intentionally to force calls to be wrapped +// /// in `unsafe` blocks for clarity. +// unsafe fn as_ptr(&self) -> *mut T; +// } + +// pub trait FromC { +// /// Creates a new instance of [Self] from the underlying +// /// libnix C type [T]. +// unsafe fn from_c(value: T) -> Self; +// } diff --git a/nixide/src/util/panic.rs b/nixide/src/util/panic.rs index 7a22501..51f2f34 100644 --- a/nixide/src/util/panic.rs +++ b/nixide/src/util/panic.rs @@ -11,6 +11,9 @@ macro_rules! panic_issue_call_failed { () => {{ crate::util::panic_issue!("[nixide] call to `{}` failed", stdext::debug_name!()) }}; + ($($arg:expr),*) => {{ + crate::util::panic_issue!("[nixide] call to `{}` failed with \"{}\"", stdext::debug_name!(), format!($($arg),*)) + }}; } pub(crate) use panic_issue; diff --git a/nixide/src/util/wrap.rs b/nixide/src/util/wrap.rs new file mode 100644 index 0000000..1bb30cf --- /dev/null +++ b/nixide/src/util/wrap.rs @@ -0,0 +1,147 @@ +use std::mem::MaybeUninit; +use std::os::raw::{c_char, c_uint, c_void}; +use std::path::PathBuf; + +use crate::errors::{ErrorContext, NixideError}; +use crate::stdext::CCharPtrExt as _; +use crate::util::wrappers::AsInnerPtr; +use crate::NixideResult; + +struct UserData { + // inner: T, + inner: MaybeUninit, +} + +impl AsInnerPtr for UserData { + unsafe fn as_ptr(&self) -> *mut T { + self.inner.as_mut_ptr() + } +} + +macro_rules! nonnull { + ($ptr:expr $(,)? ) => {{ + match ::std::ptr::NonNull::new($ptr) { + ::std::option::Option::Some(p) => ::std::result::Result::Ok(p), + ::std::option::Option::None => { + ::std::result::Result::Err(crate::errors::new_nixide_error!(NullPtr)) + } + } + }}; +} +pub(crate) use nonnull; + +macro_rules! nix_fn { + ($callback:expr $(,)? ) => {{ + // XXX: TODO: what happens if i DO actually use `null_mut` instead of ErrorContext::new? does rust just panic? + let mut ctx = crate::errors::ErrorContext::new(); + let result = $callback( + &ctx, + ); + ctx.pop().and_then(|_| ::std::result::Result::Ok(result)) + }}; +} +pub(crate) use nix_fn; + +macro_rules! nix_ptr_fn { + ($callback:expr $(,)? ) => {{ + crate::util::wrap::nix_fn!($callback).and_then(|ptr| crate::util::wrap::nonnull!(ptr)) + }}; +} +pub(crate) use nix_ptr_fn; + +macro_rules! nix_callback { + ( | $($arg_name:ident : $arg_type:ty),* ; userdata $( : *mut c_void )? $(,)? | -> $ret:ty $body:block, $callback:expr $(,)? ) => {{ + // create a function item that wraps the closure body (so it has a concrete type) + #[allow(unused_variables)] + fn __captured_fn( $( $arg_name: $arg_type ),*, userdata: *mut ::std::os::raw::c_void) -> $ret $body + + unsafe extern "C" fn __wrapper_callback( + $( + $arg_name: $arg_type, + )* + userdata: *mut ::std::os::raw::c_void, + ) { + let result = unsafe { &mut *(userdata as *mut $ret) }; + + *result = __captured_fn( + $( + $arg_name, + )* + userdata, + ); + } + + // XXX: TODO: what happens if i DO actually use `null_mut` instead of ErrorContext::new? does rust just panic? + let mut ctx = crate::errors::ErrorContext::new(); + let mut result: ::std::mem::MaybeUninit<$ret> = ::std::mem::MaybeUninit::uninit(); + + $callback( + __wrapper_callback, + result.as_mut_ptr() as *mut ::std::os::raw::c_void, + &ctx, + ); + ctx.pop().and_then(|_| unsafe { result.assume_init() }) + + }}; + + ( | userdata $( : *mut c_void )? ; $($arg_name:ident : $arg_type:ty),* | -> $ret:ty $body:block, $callback:expr $(,)? ) => {{ + // create a function item that wraps the closure body (so it has a concrete type) + #[allow(unused_variables)] + fn __captured_fn(userdata: *mut ::std::os::raw::c_void, $($arg_name: $arg_type),*) -> $ret $body + + unsafe extern "C" fn __wrapper_callback( + userdata: *mut ::std::os::raw::c_void, + $( + $arg_name: $arg_type, + )* + ) { + let result = unsafe { &mut *(userdata as *mut $ret) }; + + *result = __captured_fn( + userdata, + $( + $arg_name, + )* + ); + } + + // XXX: TODO: what happens if i DO actually use `null_mut` instead of ErrorContext::new? does rust just panic? + let mut ctx = crate::errors::ErrorContext::new(); + let mut result: ::std::mem::MaybeUninit<$ret> = ::std::mem::MaybeUninit::uninit(); + + $callback( + __wrapper_callback, + result.as_mut_ptr() as *mut ::std::os::raw::c_void, + &ctx, + ); + ctx.pop().and_then(|_| unsafe { result.assume_init() }) + }}; +} +pub(crate) use nix_callback; + +pub fn nix_string_callback(callback: F) -> Result +where + F: FnOnce( + unsafe extern "C" fn(*const c_char, c_uint, *mut c_void), + *mut c_void, + &ErrorContext, + ) -> i32, +{ + crate::util::wrap::nix_callback!( + |start: *const c_char, n: c_uint ; userdata| -> NixideResult { + start.to_utf8_string_n(n as usize) + }, + callback + ) +} + +pub fn nix_pathbuf_callback(callback: F) -> Result +where + F: FnOnce( + unsafe extern "C" fn(*const c_char, c_uint, *mut c_void), + *mut c_void, + &ErrorContext, + ) -> i32, +{ + nix_string_callback(callback).map(::std::path::PathBuf::from) +} diff --git a/nixide/src/util/wrappers.rs b/nixide/src/util/wrappers.rs index 1f6af3b..829437a 100644 --- a/nixide/src/util/wrappers.rs +++ b/nixide/src/util/wrappers.rs @@ -1,3 +1,18 @@ +use crate::NixideError; + +pub trait AsErr { + fn as_err(self) -> Result<(), T>; +} + +impl AsErr for Option { + fn as_err(self) -> Result<(), NixideError> { + match self { + Some(err) => Err(err), + None => Ok(()), + } + } +} + pub trait AsInnerPtr { /// Get a pointer to the underlying (`inner`) `libnix` C struct. /// From f3802f63004ab1d8e12e5baca777ee23c9b1d0c3 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Tue, 24 Mar 2026 22:28:25 +1000 Subject: [PATCH 300/306] i'll get this macro one day --- TODO.md | 2 + nixide/src/errors/context.rs | 6 +- nixide/src/store/mod.rs | 34 +++--- nixide/src/util/wrap.rs | 219 ++++++++++++++++++++++------------- 4 files changed, 157 insertions(+), 104 deletions(-) diff --git a/TODO.md b/TODO.md index e8fdabf..ffeff7f 100644 --- a/TODO.md +++ b/TODO.md @@ -1,3 +1,5 @@ +- [ ] rename `AsInnerPtr::as_ptr` to `AsInnerPtr::as_mut_ptr` + - [ ] add NixError::from_nonnull that replaces calls to NonNull::new(...).ok_or(...) - [ ] replace all `use nixide_sys as sys;` -> `use crate::sys;` - [ ] store NonNull pointers in structs! diff --git a/nixide/src/errors/context.rs b/nixide/src/errors/context.rs index 87039f5..5c13781 100644 --- a/nixide/src/errors/context.rs +++ b/nixide/src/errors/context.rs @@ -271,8 +271,8 @@ impl ErrorContext { pub(super) fn get_nix_err_name(&self) -> Option { unsafe { // NOTE: an Err here only occurs when "Last error was not a nix error" - wrap::nix_string_callback(|callback, user_data, ctx| { - sys::nix_err_name(ctx.as_ptr(), self.as_ptr(), Some(callback), user_data) + wrap::nix_string_callback!(|callback, userdata: *mut __UserData, ctx: &ErrorContext| { + sys::nix_err_name(ctx.as_ptr(), self.as_ptr(), Some(callback), userdata) }) .ok() } @@ -316,7 +316,7 @@ impl ErrorContext { pub(super) fn get_nix_err_info_msg(&self) -> Option { unsafe { // NOTE: an Err here only occurs when "Last error was not a nix error" - wrap::nix_string_callback(|callback, user_data, ctx| { + wrap::nix_string_callback!(|callback, user_data, ctx| { sys::nix_err_info_msg(ctx.as_ptr(), self.as_ptr(), Some(callback), user_data) }) .ok() diff --git a/nixide/src/store/mod.rs b/nixide/src/store/mod.rs index e98595d..8e27fff 100644 --- a/nixide/src/store/mod.rs +++ b/nixide/src/store/mod.rs @@ -16,15 +16,14 @@ mod tests; mod path; pub use path::*; -use std::ffi::CString; -use std::os::raw::c_char; +use std::ffi::{c_char, c_void, CString}; use std::path::PathBuf; use std::ptr::{null, null_mut, NonNull}; use std::result::Result; use crate::errors::{new_nixide_error, ErrorContext}; use crate::stdext::CCharPtrExt; -use crate::util::wrap; +use crate::util::wrap::{self, UserData}; use crate::util::wrappers::AsInnerPtr; use crate::{NixideError, NixideResult}; use nixide_sys as sys; @@ -149,36 +148,33 @@ impl Store { // }; wrap::nix_callback!( - |userdata ; + |userdata: fn(&str, &StorePath); output_name_ptr: *const c_char, output_path_ptr: *const sys::StorePath| -> NixideResult<(String, StorePath)> { // XXX: TODO: test to see if this is ever null ("out" as a default feels unsafe...) // let output_name = output_name_ptr.to_utf8_string().unwrap_or("out".to_owned()); + // NOTE: this also ensures `output_name_ptr` isn't null let output_name = output_name_ptr.to_utf8_string()?; - // SAFETY: out is a valid StorePath pointer from Nix, we need to clone it - // because Nix owns the original and may free it after the callback - if !output_path_ptr.is_null() { - let inner = wrap::nix_ptr_fn!(|ctx| unsafe { - sys::nix_store_path_clone(output_path_ptr as *mut sys::StorePath) - })?; - let store_path = StorePath { inner }; + let inner = wrap::nix_ptr_fn!(|ctx| unsafe { + sys::nix_store_path_clone(output_path_ptr as *mut sys::StorePath) + })?; + let store_path = StorePath { inner }; - callback(output_name.as_ref(), &store_path); + let callback = userdata.inner; + callback(output_name.as_ref(), &store_path); - Ok((output_name, store_path)) - } else { - panic!("IDK YET"); - // XXX: TODO: how should `output_path_ptr.is_null()` be handled? - } + Ok((output_name, store_path)) }, - |callback, userdata, ctx: &ErrorContext| unsafe { + |callback, + state: *mut UserData>, + ctx: &ErrorContext| unsafe { sys::nix_store_realise( ctx.as_ptr(), self.inner.as_ptr(), path.as_ptr(), - userdata, + (*state).inner_ptr() as *mut c_void, Some(callback), ) } diff --git a/nixide/src/util/wrap.rs b/nixide/src/util/wrap.rs index 1bb30cf..5ede2a7 100644 --- a/nixide/src/util/wrap.rs +++ b/nixide/src/util/wrap.rs @@ -1,29 +1,47 @@ -use std::mem::MaybeUninit; -use std::os::raw::{c_char, c_uint, c_void}; -use std::path::PathBuf; - -use crate::errors::{ErrorContext, NixideError}; -use crate::stdext::CCharPtrExt as _; -use crate::util::wrappers::AsInnerPtr; -use crate::NixideResult; - -struct UserData { - // inner: T, - inner: MaybeUninit, +#[repr(C)] +#[derive(Debug)] +pub(crate) struct UserData { + pub inner: S, + pub retval: T, } -impl AsInnerPtr for UserData { - unsafe fn as_ptr(&self) -> *mut T { - self.inner.as_mut_ptr() +impl AsMut> for UserData { + fn as_mut(&mut self) -> &mut UserData { + self } } +impl UserData { + /// # Warning + /// + /// Ensure `self.retval` has been initialised before unwrapping! + /// + pub unsafe fn unwrap(self) -> (S, T) { + (self.inner, self.retval) + } + + pub unsafe fn as_mut_ptr(&mut self) -> *mut Self { + self as *mut Self + } + + pub unsafe fn inner_ptr(&mut self) -> *mut S { + unsafe { + let ptr = self.as_mut_ptr(); + &raw mut (*ptr).inner + } + } + + // pub unsafe fn retval_ptr(&mut self) -> *mut c_void { + // &mut self.retval as *mut T as *mut c_void + // } +} + macro_rules! nonnull { ($ptr:expr $(,)? ) => {{ match ::std::ptr::NonNull::new($ptr) { ::std::option::Option::Some(p) => ::std::result::Result::Ok(p), ::std::option::Option::None => { - ::std::result::Result::Err(crate::errors::new_nixide_error!(NullPtr)) + ::std::result::Result::Err($crate::errors::new_nixide_error!(NullPtr)) } } }}; @@ -32,116 +50,153 @@ pub(crate) use nonnull; macro_rules! nix_fn { ($callback:expr $(,)? ) => {{ - // XXX: TODO: what happens if i DO actually use `null_mut` instead of ErrorContext::new? does rust just panic? - let mut ctx = crate::errors::ErrorContext::new(); - let result = $callback( - &ctx, - ); - ctx.pop().and_then(|_| ::std::result::Result::Ok(result)) + let mut __ctx = $crate::errors::ErrorContext::new(); + let __result = $callback(&__ctx); + __ctx + .pop() + .and_then(|_| ::std::result::Result::Ok(__result)) }}; } pub(crate) use nix_fn; macro_rules! nix_ptr_fn { ($callback:expr $(,)? ) => {{ - crate::util::wrap::nix_fn!($callback).and_then(|ptr| crate::util::wrap::nonnull!(ptr)) + $crate::util::wrap::nix_fn!($callback).and_then(|ptr| $crate::util::wrap::nonnull!(ptr)) }}; } pub(crate) use nix_ptr_fn; +macro_rules! __nix_callback { + ($userdata_type:ty, $ret:ty, $callback:expr) => {{ + let mut __ctx = $crate::errors::ErrorContext::new(); + let mut __state: ::std::mem::MaybeUninit<__UserData> = ::std::mem::MaybeUninit::uninit(); + + $callback(__wrapper_callback, __state.as_mut_ptr(), &__ctx); + + // add type annotations for compiler + let __return: $ret = __ctx + .pop() + .and_then(|_| unsafe { __state.assume_init().retval }); + __return + }}; +} +pub(crate) use __nix_callback; + +/// `libnix` functions consistently either expect the `userdata`/`user_data` (inconsistently named in the API...) +/// field to be the first or last parameter (differs between function). The `nix_callback!` macro allows the +/// position to be specified by either the following syntax: +/// +/// ```rs +/// nix_callback(userdata; ...); // first parameter +/// nix_callback(...; userdata); // last parameter +/// ``` +/// macro_rules! nix_callback { - ( | $($arg_name:ident : $arg_type:ty),* ; userdata $( : *mut c_void )? $(,)? | -> $ret:ty $body:block, $callback:expr $(,)? ) => {{ + ( | $userdata:ident : $userdata_type:ty; $($arg_name:ident : $arg_type:ty),* $(,)? | -> $ret:ty $body:block, $function:expr $(,)? ) => {{ + type __UserData = $crate::util::wrap::UserData<$userdata_type, $ret>; // create a function item that wraps the closure body (so it has a concrete type) - #[allow(unused_variables)] - fn __captured_fn( $( $arg_name: $arg_type ),*, userdata: *mut ::std::os::raw::c_void) -> $ret $body + fn __captured_fn($userdata: &mut __UserData, $($arg_name: $arg_type),*) -> $ret $body unsafe extern "C" fn __wrapper_callback( + $userdata: *mut ::std::ffi::c_void, $( $arg_name: $arg_type, )* - userdata: *mut ::std::os::raw::c_void, ) { - let result = unsafe { &mut *(userdata as *mut $ret) }; + let ud = unsafe { &mut *($userdata as *mut __UserData) }; + let stored_retval = &raw mut ud.retval; - *result = __captured_fn( + let retval = __captured_fn( + ud, $( $arg_name, )* - userdata, ); + + unsafe { + stored_retval.write(retval) + }; } - // XXX: TODO: what happens if i DO actually use `null_mut` instead of ErrorContext::new? does rust just panic? - let mut ctx = crate::errors::ErrorContext::new(); - let mut result: ::std::mem::MaybeUninit<$ret> = ::std::mem::MaybeUninit::uninit(); + let mut __ctx: $crate::errors::ErrorContext = $crate::errors::ErrorContext::new(); + let mut __state: ::std::mem::MaybeUninit<__UserData> = ::std::mem::MaybeUninit::uninit(); - $callback( - __wrapper_callback, - result.as_mut_ptr() as *mut ::std::os::raw::c_void, - &ctx, - ); - ctx.pop().and_then(|_| unsafe { result.assume_init() }) + // fn __captured_function( + // callback: unsafe extern "C" fn( + // $userdata: *mut ::std::ffi::c_void, + // $( + // $arg_name: $arg_type, + // )* + // ), + // state: *mut __UserData, + // ctx: &$crate::errors::ErrorContext, + // ) { + // $function(callback, state, ctx); + // } + $function(__wrapper_callback, __state.as_mut_ptr(), &__ctx); + + // add type annotations for compiler + __ctx.pop().and_then(|_| unsafe { __state.assume_init().retval }) }}; - ( | userdata $( : *mut c_void )? ; $($arg_name:ident : $arg_type:ty),* | -> $ret:ty $body:block, $callback:expr $(,)? ) => {{ + ( | $($arg_name:ident : $arg_type:ty),* ; $userdata:ident : $userdata_type:ty $(,)? | -> $ret:ty $body:block, $callback:expr $(,)? ) => {{ + type __UserData = $crate::util::wrap::UserData<$userdata_type, $ret>; // create a function item that wraps the closure body (so it has a concrete type) - #[allow(unused_variables)] - fn __captured_fn(userdata: *mut ::std::os::raw::c_void, $($arg_name: $arg_type),*) -> $ret $body + unsafe extern "C" fn __captured_fn( $( $arg_name: $arg_type ),*, $userdata: &mut __UserData) -> $ret $body unsafe extern "C" fn __wrapper_callback( - userdata: *mut ::std::os::raw::c_void, $( $arg_name: $arg_type, )* + $userdata: *mut ::std::ffi::c_void, ) { - let result = unsafe { &mut *(userdata as *mut $ret) }; + unsafe { + let ud = &mut *($userdata as *mut __UserData); + let stored_retval = &raw mut ud.retval; - *result = __captured_fn( - userdata, - $( - $arg_name, - )* - ); + let retval = __captured_fn( + $( + $arg_name, + )* + ud, + ); + + stored_retval.write(retval) + } } - // XXX: TODO: what happens if i DO actually use `null_mut` instead of ErrorContext::new? does rust just panic? - let mut ctx = crate::errors::ErrorContext::new(); - let mut result: ::std::mem::MaybeUninit<$ret> = ::std::mem::MaybeUninit::uninit(); + // $crate::util::wrap::__nix_callback!($userdata_type, $ret, $callback) + let mut __ctx = $crate::errors::ErrorContext::new(); + let mut __state: ::std::mem::MaybeUninit< + __UserData + > = ::std::mem::MaybeUninit::uninit(); - $callback( - __wrapper_callback, - result.as_mut_ptr() as *mut ::std::os::raw::c_void, - &ctx, - ); - ctx.pop().and_then(|_| unsafe { result.assume_init() }) + $callback(__wrapper_callback, __state.as_mut_ptr(), &__ctx); + + // add type annotations for compiler + let __return: $ret = __ctx.pop().and_then(|_| unsafe { __state.assume_init().retval }); + __return }}; } pub(crate) use nix_callback; -pub fn nix_string_callback(callback: F) -> Result -where - F: FnOnce( - unsafe extern "C" fn(*const c_char, c_uint, *mut c_void), - *mut c_void, - &ErrorContext, - ) -> i32, -{ - crate::util::wrap::nix_callback!( - |start: *const c_char, n: c_uint ; userdata| -> NixideResult { - start.to_utf8_string_n(n as usize) - }, - callback - ) +// XXX: TODO: convert these to declarative macros +macro_rules! nix_string_callback { + ($callback:expr $(,)?) => {{ + $crate::util::wrap::nix_callback!( + |start: *const ::std::ffi::c_char, n: ::std::ffi::c_uint ; userdata: ()| -> $crate::NixideResult { + start.to_utf8_string_n(n as usize) + }, + $callback + ) + }}; } +pub(crate) use nix_string_callback; -pub fn nix_pathbuf_callback(callback: F) -> Result -where - F: FnOnce( - unsafe extern "C" fn(*const c_char, c_uint, *mut c_void), - *mut c_void, - &ErrorContext, - ) -> i32, -{ - nix_string_callback(callback).map(::std::path::PathBuf::from) +macro_rules! nix_pathbuf_callback { + ($callback:expr $(,)?) => {{ + $crate::util::wrap::nix_string_callback!($callback).map(::std::path::PathBuf::from) + }}; } +pub(crate) use nix_pathbuf_callback; From bc5a9cc3aa90d3025a487b1be6856bae7e43ea83 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Wed, 25 Mar 2026 01:05:30 +1000 Subject: [PATCH 301/306] merge macro arms --- nixide/src/store/mod.rs | 2 +- nixide/src/util/wrap.rs | 74 ++++++++++++----------------------------- 2 files changed, 22 insertions(+), 54 deletions(-) diff --git a/nixide/src/store/mod.rs b/nixide/src/store/mod.rs index 8e27fff..09ba32e 100644 --- a/nixide/src/store/mod.rs +++ b/nixide/src/store/mod.rs @@ -148,7 +148,7 @@ impl Store { // }; wrap::nix_callback!( - |userdata: fn(&str, &StorePath); + |; userdata: fn(&str, &StorePath); output_name_ptr: *const c_char, output_path_ptr: *const sys::StorePath| -> NixideResult<(String, StorePath)> { diff --git a/nixide/src/util/wrap.rs b/nixide/src/util/wrap.rs index 5ede2a7..d4a2fcb 100644 --- a/nixide/src/util/wrap.rs +++ b/nixide/src/util/wrap.rs @@ -92,35 +92,36 @@ pub(crate) use __nix_callback; /// ``` /// macro_rules! nix_callback { - ( | $userdata:ident : $userdata_type:ty; $($arg_name:ident : $arg_type:ty),* $(,)? | -> $ret:ty $body:block, $function:expr $(,)? ) => {{ + ( | $( $($pre:ident : $pre_ty:ty),+ $(,)? )? ; $userdata:ident : $userdata_type:ty ; $( $($post:ident : $post_ty:ty),+ $(,)? )? | -> $ret:ty $body:block, $function:expr $(,)? ) => {{ type __UserData = $crate::util::wrap::UserData<$userdata_type, $ret>; // create a function item that wraps the closure body (so it has a concrete type) - fn __captured_fn($userdata: &mut __UserData, $($arg_name: $arg_type),*) -> $ret $body + unsafe extern "C" fn __captured_fn( + $($( $pre: $pre_ty, )*)? + $userdata: &mut __UserData, + $($( $post: $post_ty, )*)? + ) -> $ret $body unsafe extern "C" fn __wrapper_callback( + $($( $pre: $pre_ty, )*)? $userdata: *mut ::std::ffi::c_void, - $( - $arg_name: $arg_type, - )* + $($( $post: $post_ty, )*)? ) { let ud = unsafe { &mut *($userdata as *mut __UserData) }; let stored_retval = &raw mut ud.retval; - let retval = __captured_fn( - ud, - $( - $arg_name, - )* - ); + let retval = unsafe { + __captured_fn( + $($( $pre, )*)? + ud, + $($( $post, )*)? + ); + }; unsafe { stored_retval.write(retval) }; } - let mut __ctx: $crate::errors::ErrorContext = $crate::errors::ErrorContext::new(); - let mut __state: ::std::mem::MaybeUninit<__UserData> = ::std::mem::MaybeUninit::uninit(); - // fn __captured_function( // callback: unsafe extern "C" fn( // $userdata: *mut ::std::ffi::c_void, @@ -134,49 +135,16 @@ macro_rules! nix_callback { // $function(callback, state, ctx); // } + let mut __ctx: $crate::errors::ErrorContext = $crate::errors::ErrorContext::new(); + let mut __state: ::std::mem::MaybeUninit<__UserData> = ::std::mem::MaybeUninit::uninit(); + $function(__wrapper_callback, __state.as_mut_ptr(), &__ctx); - // add type annotations for compiler __ctx.pop().and_then(|_| unsafe { __state.assume_init().retval }) - }}; - - ( | $($arg_name:ident : $arg_type:ty),* ; $userdata:ident : $userdata_type:ty $(,)? | -> $ret:ty $body:block, $callback:expr $(,)? ) => {{ - type __UserData = $crate::util::wrap::UserData<$userdata_type, $ret>; - // create a function item that wraps the closure body (so it has a concrete type) - unsafe extern "C" fn __captured_fn( $( $arg_name: $arg_type ),*, $userdata: &mut __UserData) -> $ret $body - - unsafe extern "C" fn __wrapper_callback( - $( - $arg_name: $arg_type, - )* - $userdata: *mut ::std::ffi::c_void, - ) { - unsafe { - let ud = &mut *($userdata as *mut __UserData); - let stored_retval = &raw mut ud.retval; - - let retval = __captured_fn( - $( - $arg_name, - )* - ud, - ); - - stored_retval.write(retval) - } - } - - // $crate::util::wrap::__nix_callback!($userdata_type, $ret, $callback) - let mut __ctx = $crate::errors::ErrorContext::new(); - let mut __state: ::std::mem::MaybeUninit< - __UserData - > = ::std::mem::MaybeUninit::uninit(); - - $callback(__wrapper_callback, __state.as_mut_ptr(), &__ctx); // add type annotations for compiler - let __return: $ret = __ctx.pop().and_then(|_| unsafe { __state.assume_init().retval }); - __return + // let __result: $ret = __ctx.pop().and_then(|_| unsafe { __state.assume_init().retval }); + // __result }}; } pub(crate) use nix_callback; @@ -185,7 +153,7 @@ pub(crate) use nix_callback; macro_rules! nix_string_callback { ($callback:expr $(,)?) => {{ $crate::util::wrap::nix_callback!( - |start: *const ::std::ffi::c_char, n: ::std::ffi::c_uint ; userdata: ()| -> $crate::NixideResult { + |start: *const ::std::ffi::c_char, n: ::std::ffi::c_uint; userdata: ();| -> $crate::NixideResult { start.to_utf8_string_n(n as usize) }, $callback From 648aadf5ab6b6b9a25da64819cb8e237318ed846 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Wed, 25 Mar 2026 01:13:31 +1000 Subject: [PATCH 302/306] prefer &mut __Userdata over *mut UserData --- nixide/src/errors/context.rs | 4 ++-- nixide/src/util/wrap.rs | 28 +++++----------------------- 2 files changed, 7 insertions(+), 25 deletions(-) diff --git a/nixide/src/errors/context.rs b/nixide/src/errors/context.rs index 5c13781..404eee5 100644 --- a/nixide/src/errors/context.rs +++ b/nixide/src/errors/context.rs @@ -271,7 +271,7 @@ impl ErrorContext { pub(super) fn get_nix_err_name(&self) -> Option { unsafe { // NOTE: an Err here only occurs when "Last error was not a nix error" - wrap::nix_string_callback!(|callback, userdata: *mut __UserData, ctx: &ErrorContext| { + wrap::nix_string_callback!(|callback, userdata: &mut __UserData, ctx: &ErrorContext| { sys::nix_err_name(ctx.as_ptr(), self.as_ptr(), Some(callback), userdata) }) .ok() @@ -316,7 +316,7 @@ impl ErrorContext { pub(super) fn get_nix_err_info_msg(&self) -> Option { unsafe { // NOTE: an Err here only occurs when "Last error was not a nix error" - wrap::nix_string_callback!(|callback, user_data, ctx| { + wrap::nix_string_callback!(|callback, user_data, ctx: &ErrorContext| { sys::nix_err_info_msg(ctx.as_ptr(), self.as_ptr(), Some(callback), user_data) }) .ok() diff --git a/nixide/src/util/wrap.rs b/nixide/src/util/wrap.rs index d4a2fcb..e4bdb4c 100644 --- a/nixide/src/util/wrap.rs +++ b/nixide/src/util/wrap.rs @@ -103,18 +103,17 @@ macro_rules! nix_callback { unsafe extern "C" fn __wrapper_callback( $($( $pre: $pre_ty, )*)? - $userdata: *mut ::std::ffi::c_void, + $userdata: &mut __UserData, $($( $post: $post_ty, )*)? ) { - let ud = unsafe { &mut *($userdata as *mut __UserData) }; - let stored_retval = &raw mut ud.retval; + let stored_retval = &raw mut $userdata.retval; let retval = unsafe { __captured_fn( $($( $pre, )*)? - ud, + $userdata, $($( $post, )*)? - ); + ) }; unsafe { @@ -122,29 +121,12 @@ macro_rules! nix_callback { }; } - // fn __captured_function( - // callback: unsafe extern "C" fn( - // $userdata: *mut ::std::ffi::c_void, - // $( - // $arg_name: $arg_type, - // )* - // ), - // state: *mut __UserData, - // ctx: &$crate::errors::ErrorContext, - // ) { - // $function(callback, state, ctx); - // } - let mut __ctx: $crate::errors::ErrorContext = $crate::errors::ErrorContext::new(); let mut __state: ::std::mem::MaybeUninit<__UserData> = ::std::mem::MaybeUninit::uninit(); - $function(__wrapper_callback, __state.as_mut_ptr(), &__ctx); + $function(__wrapper_callback, &mut __state, &__ctx); __ctx.pop().and_then(|_| unsafe { __state.assume_init().retval }) - - // add type annotations for compiler - // let __result: $ret = __ctx.pop().and_then(|_| unsafe { __state.assume_init().retval }); - // __result }}; } pub(crate) use nix_callback; From ff28c6f13f8c90966bc036ee73a59b713f922f34 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Wed, 25 Mar 2026 09:47:51 +1000 Subject: [PATCH 303/306] ok nvm *mut was im stupid --- nixide/src/errors/context.rs | 21 ++++++++-- nixide/src/stdext/cchar_ptr_ext.rs | 28 -------------- nixide/src/store/mod.rs | 46 ++++++++++++++-------- nixide/src/store/path.rs | 26 +++++++------ nixide/src/util/wrap.rs | 62 ++++++++++++++++++++---------- 5 files changed, 103 insertions(+), 80 deletions(-) diff --git a/nixide/src/errors/context.rs b/nixide/src/errors/context.rs index 404eee5..d425b38 100644 --- a/nixide/src/errors/context.rs +++ b/nixide/src/errors/context.rs @@ -22,6 +22,7 @@ // }; use std::ffi::c_uint; +use std::ffi::c_void; use std::ptr::NonNull; use super::{NixError, NixideResult}; @@ -269,10 +270,16 @@ impl ErrorContext { /// } /// ``` pub(super) fn get_nix_err_name(&self) -> Option { + #[allow(unused_unsafe)] // XXX: TODO: remove this `unused_unsafe` unsafe { // NOTE: an Err here only occurs when "Last error was not a nix error" - wrap::nix_string_callback!(|callback, userdata: &mut __UserData, ctx: &ErrorContext| { - sys::nix_err_name(ctx.as_ptr(), self.as_ptr(), Some(callback), userdata) + wrap::nix_string_callback!(|callback, userdata: *mut __UserData, ctx: &ErrorContext| { + sys::nix_err_name( + ctx.as_ptr(), + self.as_ptr(), + Some(callback), + userdata as *mut c_void, + ) }) .ok() } @@ -314,10 +321,16 @@ impl ErrorContext { /// } /// ``` pub(super) fn get_nix_err_info_msg(&self) -> Option { + #[allow(unused_unsafe)] // XXX: TODO: remove this `unused_unsafe` unsafe { // NOTE: an Err here only occurs when "Last error was not a nix error" - wrap::nix_string_callback!(|callback, user_data, ctx: &ErrorContext| { - sys::nix_err_info_msg(ctx.as_ptr(), self.as_ptr(), Some(callback), user_data) + wrap::nix_string_callback!(|callback, userdata, ctx: &ErrorContext| { + sys::nix_err_info_msg( + ctx.as_ptr(), + self.as_ptr(), + Some(callback), + userdata as *mut c_void, + ) }) .ok() } diff --git a/nixide/src/stdext/cchar_ptr_ext.rs b/nixide/src/stdext/cchar_ptr_ext.rs index a4af4fe..5d506a9 100644 --- a/nixide/src/stdext/cchar_ptr_ext.rs +++ b/nixide/src/stdext/cchar_ptr_ext.rs @@ -34,31 +34,3 @@ impl CCharPtrExt for *const c_char { } } } - -// XXX: TODO: remove if unused -// pub trait CCharPtrExt { -// fn to_utf8_string(self) -> Result>; -// -// fn to_utf8_string_n(self, n: usize) -> Result>; -// } -// -// impl CCharPtrExt for *const c_char { -// fn to_utf8_string(self) -> Result> { -// if self.is_null() { -// return Err(None); -// } -// let cstr = unsafe { CStr::from_ptr(self) }; -// match cstr.to_str() { -// Ok(s) => Ok(s.to_owned()), -// Err(err) => Err(Some(err)), -// } -// } -// -// fn to_utf8_string_n(self, n: usize) -> Result> { -// if self.is_null() || n == 0 { -// return Err(None); -// } -// let bytes = unsafe { from_raw_parts(self.cast::(), n as usize) }; -// from_utf8(bytes).map(str::to_string).map_err(Some) -// } -// } diff --git a/nixide/src/store/mod.rs b/nixide/src/store/mod.rs index 09ba32e..96b29b5 100644 --- a/nixide/src/store/mod.rs +++ b/nixide/src/store/mod.rs @@ -162,7 +162,7 @@ impl Store { })?; let store_path = StorePath { inner }; - let callback = userdata.inner; + let callback = unsafe { (*userdata).inner }; callback(output_name.as_ref(), &store_path); Ok((output_name, store_path)) @@ -213,27 +213,43 @@ impl Store { /// /// If the store doesn't have a version (like the dummy store), returns None pub fn version(&self) -> Result { - wrap::nix_string_callback(|callback, user_data, ctx| unsafe { - sys::nix_store_get_version(ctx.as_ptr(), self.inner.as_ptr(), Some(callback), user_data) - }) + wrap::nix_string_callback!( + |callback, userdata: *mut __UserData, ctx: &ErrorContext| unsafe { + sys::nix_store_get_version( + ctx.as_ptr(), + self.inner.as_ptr(), + Some(callback), + userdata as *mut c_void, + ) + } + ) } /// Get the URI of a Nix store pub fn uri(&self) -> Result { - wrap::nix_string_callback(|callback, user_data, ctx| unsafe { - sys::nix_store_get_uri(ctx.as_ptr(), self.inner.as_ptr(), Some(callback), user_data) - }) + wrap::nix_string_callback!( + |callback, userdata: *mut __UserData, ctx: &ErrorContext| unsafe { + sys::nix_store_get_uri( + ctx.as_ptr(), + self.inner.as_ptr(), + Some(callback), + userdata as *mut c_void, + ) + } + ) } pub fn store_dir(&self) -> Result { - wrap::nix_pathbuf_callback(|callback, user_data, ctx| unsafe { - sys::nix_store_get_storedir( - ctx.as_ptr(), - self.inner.as_ptr(), - Some(callback), - user_data, - ) - }) + wrap::nix_pathbuf_callback!( + |callback, userdata: *mut __UserData, ctx: &ErrorContext| unsafe { + sys::nix_store_get_storedir( + ctx.as_ptr(), + self.inner.as_ptr(), + Some(callback), + userdata as *mut c_void, + ) + } + ) } pub fn copy_closure_to( diff --git a/nixide/src/store/path.rs b/nixide/src/store/path.rs index 9696481..5d18865 100644 --- a/nixide/src/store/path.rs +++ b/nixide/src/store/path.rs @@ -1,4 +1,4 @@ -use std::ffi::CString; +use std::ffi::{c_void, CString}; use std::path::PathBuf; use std::ptr::NonNull; @@ -56,8 +56,8 @@ impl StorePath { /// Returns an error if the name cannot be retrieved. /// pub fn name(&self) -> Result { - wrap::nix_string_callback(|callback, user_data, _| unsafe { - sys::nix_store_path_name(self.inner.as_ptr(), Some(callback), user_data); + wrap::nix_string_callback!(|callback, userdata: *mut __UserData, _| unsafe { + sys::nix_store_path_name(self.inner.as_ptr(), Some(callback), userdata as *mut c_void); // NOTE: nix_store_path_name doesn't return nix_err, so we force it to return successfully nix_err_NIX_OK }) @@ -85,15 +85,17 @@ impl StorePath { /// * `store` - The store containing the path /// pub fn real_path(&self, store: &Store) -> Result { - wrap::nix_pathbuf_callback(|callback, user_data, ctx| unsafe { - sys::nix_store_real_path( - ctx.as_ptr(), - store.inner.as_ptr(), - self.as_ptr(), - Some(callback), - user_data, - ) - }) + wrap::nix_pathbuf_callback!( + |callback, userdata: *mut __UserData, ctx: &ErrorContext| unsafe { + sys::nix_store_real_path( + ctx.as_ptr(), + store.inner.as_ptr(), + self.as_ptr(), + Some(callback), + userdata as *mut c_void, + ) + } + ) } /// Check if a [StorePath] is valid (i.e. that its corresponding store object diff --git a/nixide/src/util/wrap.rs b/nixide/src/util/wrap.rs index e4bdb4c..f26a2ac 100644 --- a/nixide/src/util/wrap.rs +++ b/nixide/src/util/wrap.rs @@ -11,6 +11,22 @@ impl AsMut> for UserData { } } +// pub(crate) trait VoidPtrIsomorphism { +// fn as_void_ptr(self) -> *mut c_void; + +// fn from_void_ptr(ptr: *mut c_void) -> Self; +// } + +// impl VoidPtrIsomorphism for *mut UserData { +// fn as_void_ptr(self) -> *mut c_void { +// self as *mut c_void +// } + +// fn from_void_ptr(ptr: *mut c_void) -> Self { +// ptr as Self +// } +// } + impl UserData { /// # Warning /// @@ -46,6 +62,8 @@ macro_rules! nonnull { } }}; } +use std::ffi::c_void; + pub(crate) use nonnull; macro_rules! nix_fn { @@ -80,6 +98,7 @@ macro_rules! __nix_callback { __return }}; } +#[allow(unused_imports)] // XXX: TODO: replace the tail of `nix_callback!` with this macro pub(crate) use __nix_callback; /// `libnix` functions consistently either expect the `userdata`/`user_data` (inconsistently named in the API...) @@ -95,36 +114,37 @@ macro_rules! nix_callback { ( | $( $($pre:ident : $pre_ty:ty),+ $(,)? )? ; $userdata:ident : $userdata_type:ty ; $( $($post:ident : $post_ty:ty),+ $(,)? )? | -> $ret:ty $body:block, $function:expr $(,)? ) => {{ type __UserData = $crate::util::wrap::UserData<$userdata_type, $ret>; // create a function item that wraps the closure body (so it has a concrete type) + #[allow(unused_variables)] unsafe extern "C" fn __captured_fn( $($( $pre: $pre_ty, )*)? - $userdata: &mut __UserData, + $userdata: *mut __UserData, $($( $post: $post_ty, )*)? - ) -> $ret $body + ) -> $ret { $body } unsafe extern "C" fn __wrapper_callback( $($( $pre: $pre_ty, )*)? - $userdata: &mut __UserData, + $userdata: *mut ::std::ffi::c_void, $($( $post: $post_ty, )*)? ) { - let stored_retval = &raw mut $userdata.retval; - - let retval = unsafe { - __captured_fn( - $($( $pre, )*)? - $userdata, - $($( $post, )*)? - ) - }; - unsafe { + let userdata_ = $userdata as *mut __UserData; + let stored_retval = &raw mut (*userdata_).retval; + + let retval = + __captured_fn( + $($( $pre, )*)? + userdata_, + $($( $post, )*)? + ); + stored_retval.write(retval) - }; + } } let mut __ctx: $crate::errors::ErrorContext = $crate::errors::ErrorContext::new(); - let mut __state: ::std::mem::MaybeUninit<__UserData> = ::std::mem::MaybeUninit::uninit(); + let mut __state: ::std::mem::MaybeUninit<__UserData> = ::std::mem::MaybeUninit::zeroed(); - $function(__wrapper_callback, &mut __state, &__ctx); + $function(__wrapper_callback, __state.as_mut_ptr(), &__ctx); __ctx.pop().and_then(|_| unsafe { __state.assume_init().retval }) }}; @@ -133,20 +153,20 @@ pub(crate) use nix_callback; // XXX: TODO: convert these to declarative macros macro_rules! nix_string_callback { - ($callback:expr $(,)?) => {{ + ($function:expr $(,)?) => {{ $crate::util::wrap::nix_callback!( |start: *const ::std::ffi::c_char, n: ::std::ffi::c_uint; userdata: ();| -> $crate::NixideResult { - start.to_utf8_string_n(n as usize) + $crate::stdext::CCharPtrExt::to_utf8_string_n(start, n as usize) }, - $callback + $function ) }}; } pub(crate) use nix_string_callback; macro_rules! nix_pathbuf_callback { - ($callback:expr $(,)?) => {{ - $crate::util::wrap::nix_string_callback!($callback).map(::std::path::PathBuf::from) + ($function:expr $(,)?) => {{ + $crate::util::wrap::nix_string_callback!($function).map(::std::path::PathBuf::from) }}; } pub(crate) use nix_pathbuf_callback; From ab9886281add228eac0e0eea576dfd2ae4675980 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Wed, 25 Mar 2026 11:12:45 +1000 Subject: [PATCH 304/306] oh we're pushing --- nixide/src/store/mod.rs | 5 +-- nixide/src/store/path.rs | 17 ++++++--- nixide/src/store/tests.rs | 79 ++++++++++++++++++++++++++------------- nixide/src/util/wrap.rs | 33 ++++++---------- 4 files changed, 79 insertions(+), 55 deletions(-) diff --git a/nixide/src/store/mod.rs b/nixide/src/store/mod.rs index 96b29b5..7c057d7 100644 --- a/nixide/src/store/mod.rs +++ b/nixide/src/store/mod.rs @@ -197,10 +197,9 @@ impl Store { /// /// ```no_run /// # use std::sync::Arc; - /// # use nixide::{Context, Store}; + /// # use nixide::Store; /// # fn main() -> Result<(), Box> { - /// let ctx = Arc::new(Context::new()?); - /// let store = Store::open(&ctx, None)?; + /// let store = Store::open(None)?; /// let path = store.store_path("/nix/store/...")?; /// # Ok(()) /// # } diff --git a/nixide/src/store/path.rs b/nixide/src/store/path.rs index 5d18865..0c4e832 100644 --- a/nixide/src/store/path.rs +++ b/nixide/src/store/path.rs @@ -7,9 +7,9 @@ use crate::errors::{new_nixide_error, ErrorContext}; use crate::util::panic_issue_call_failed; use crate::util::wrap; use crate::util::wrappers::AsInnerPtr; -use crate::NixideError; +use crate::NixideResult; -use nixide_sys::{self as sys, nix_err_NIX_OK}; +use nixide_sys as sys; /// A path in the Nix store. /// @@ -36,7 +36,7 @@ impl StorePath { /// # Errors /// /// Returns an error if the path cannot be parsed. - pub fn parse(store: &Store, path: &str) -> Result { + pub fn parse(store: &Store, path: &str) -> NixideResult { let c_path = CString::new(path).or(Err(new_nixide_error!(StringNulByte)))?; let inner = wrap::nix_ptr_fn!(|ctx: &ErrorContext| unsafe { @@ -46,6 +46,10 @@ impl StorePath { Ok(Self { inner }) } + pub fn fake_path(store: &Store) -> NixideResult { + Self::parse(store, "/nix/store/00000000000000000000000000000000-fake") + } + /// Get the name component of the store path. /// /// This returns the name part of the store path (everything after the hash). @@ -55,11 +59,12 @@ impl StorePath { /// /// Returns an error if the name cannot be retrieved. /// - pub fn name(&self) -> Result { + pub fn name(&self) -> NixideResult { wrap::nix_string_callback!(|callback, userdata: *mut __UserData, _| unsafe { sys::nix_store_path_name(self.inner.as_ptr(), Some(callback), userdata as *mut c_void); // NOTE: nix_store_path_name doesn't return nix_err, so we force it to return successfully - nix_err_NIX_OK + // XXX: NOTE: now `nix_string_callback` is a macro this isn't necessary + // sys::nix_err_NIX_OK }) } @@ -84,7 +89,7 @@ impl StorePath { /// /// * `store` - The store containing the path /// - pub fn real_path(&self, store: &Store) -> Result { + pub fn real_path(&self, store: &Store) -> NixideResult { wrap::nix_pathbuf_callback!( |callback, userdata: *mut __UserData, ctx: &ErrorContext| unsafe { sys::nix_store_real_path( diff --git a/nixide/src/store/tests.rs b/nixide/src/store/tests.rs index 3e92b6c..d0b7744 100644 --- a/nixide/src/store/tests.rs +++ b/nixide/src/store/tests.rs @@ -1,53 +1,82 @@ use serial_test::serial; -use super::*; +use super::{Store, StorePath}; +use crate::errors::ErrorContext; +use crate::sys; +use crate::util::wrappers::AsInnerPtr as _; #[test] #[serial] fn test_store_opening() { + let mut ctx = ErrorContext::new(); + unsafe { + sys::nix_libutil_init(ctx.as_ptr()); + ctx.pop() + .expect("nix_libutil_init failed with bad ErrorContext"); + sys::nix_libstore_init(ctx.as_ptr()); + ctx.pop() + .expect("nix_libstore_init failed with bad ErrorContext"); + sys::nix_libexpr_init(ctx.as_ptr()); + ctx.pop() + .expect("nix_libexpr_init failed with bad ErrorContext"); + }; + let _store = Store::open(None).expect("Failed to open store"); } #[test] #[serial] fn test_store_path_parse() { + let mut ctx = ErrorContext::new(); + unsafe { + sys::nix_libutil_init(ctx.as_ptr()); + ctx.pop() + .expect("nix_libutil_init failed with bad ErrorContext"); + sys::nix_libstore_init(ctx.as_ptr()); + ctx.pop() + .expect("nix_libstore_init failed with bad ErrorContext"); + sys::nix_libexpr_init(ctx.as_ptr()); + ctx.pop() + .expect("nix_libexpr_init failed with bad ErrorContext"); + }; + let store = Store::open(None).expect("Failed to open store"); // Try parsing a well-formed store path - // Note: This may fail if the path doesn't exist in the store - let result = StorePath::parse(&store, "/nix/store/00000000000000000000000000000000-test"); - - // We don't assert success here because the path might not exist - // This test mainly checks that the API works correctly - match result { - Ok(_path) => { - // Successfully parsed the path - } - Err(_) => { - // Path doesn't exist or is invalid, which is expected - } - } + let result = StorePath::fake_path(&store); + result.expect("idk hopefully this fails"); } #[test] #[serial] fn test_store_path_clone() { + let mut ctx = ErrorContext::new(); + unsafe { + sys::nix_libutil_init(ctx.as_ptr()); + ctx.pop() + .expect("nix_libutil_init failed with bad ErrorContext"); + sys::nix_libstore_init(ctx.as_ptr()); + ctx.pop() + .expect("nix_libstore_init failed with bad ErrorContext"); + sys::nix_libexpr_init(ctx.as_ptr()); + ctx.pop() + .expect("nix_libexpr_init failed with bad ErrorContext"); + }; + let store = Store::open(None).expect("Failed to open store"); // Try to get a valid store path by parsing - // Note: This test is somewhat limited without a guaranteed valid path - if let Ok(path) = StorePath::parse(&store, "/nix/store/00000000000000000000000000000000-test") { - let cloned = path.clone(); + let path = StorePath::fake_path(&store).expect("Failed to create `StorePath::fake_path`"); + let cloned = path.clone(); - // Assert that the cloned path has the same name as the original - let original_name = path.name().expect("Failed to get original path name"); - let cloned_name = cloned.name().expect("Failed to get cloned path name"); + // Assert that the cloned path has the same name as the original + let original_name = path.name().expect("Failed to get original path name"); + let cloned_name = cloned.name().expect("Failed to get cloned path name"); - assert_eq!( - original_name, cloned_name, - "Cloned path should have the same name as original" - ); - } + assert_eq!( + original_name, cloned_name, + "Cloned path should have the same name as original" + ); } // Note: test_realize is not included because it requires a valid store path diff --git a/nixide/src/util/wrap.rs b/nixide/src/util/wrap.rs index f26a2ac..595511b 100644 --- a/nixide/src/util/wrap.rs +++ b/nixide/src/util/wrap.rs @@ -3,6 +3,12 @@ pub(crate) struct UserData { pub inner: S, pub retval: T, + + #[cfg(debug_assertions)] + pub init_inner: bool, + + #[cfg(debug_assertions)] + pub init_retval: bool, } impl AsMut> for UserData { @@ -11,30 +17,14 @@ impl AsMut> for UserData { } } -// pub(crate) trait VoidPtrIsomorphism { -// fn as_void_ptr(self) -> *mut c_void; - -// fn from_void_ptr(ptr: *mut c_void) -> Self; -// } - -// impl VoidPtrIsomorphism for *mut UserData { -// fn as_void_ptr(self) -> *mut c_void { -// self as *mut c_void -// } - -// fn from_void_ptr(ptr: *mut c_void) -> Self { -// ptr as Self -// } -// } - impl UserData { /// # Warning /// /// Ensure `self.retval` has been initialised before unwrapping! /// - pub unsafe fn unwrap(self) -> (S, T) { - (self.inner, self.retval) - } + // pub unsafe fn unwrap(self) -> (S, T) { + // (self.inner, self.retval) + // } pub unsafe fn as_mut_ptr(&mut self) -> *mut Self { self as *mut Self @@ -62,7 +52,6 @@ macro_rules! nonnull { } }}; } -use std::ffi::c_void; pub(crate) use nonnull; @@ -146,7 +135,9 @@ macro_rules! nix_callback { $function(__wrapper_callback, __state.as_mut_ptr(), &__ctx); - __ctx.pop().and_then(|_| unsafe { __state.assume_init().retval }) + // type annotations for compiler + let __result: $ret = __ctx.pop().and_then(|_| unsafe { __state.assume_init().retval }); + __result }}; } pub(crate) use nix_callback; From bfb2010f19341b374dfd4dd7bc959115a75faa7d Mon Sep 17 00:00:00 2001 From: _cry64 Date: Wed, 25 Mar 2026 14:21:14 +1000 Subject: [PATCH 305/306] nix_callback! macro now works <3 --- Cargo.lock | 1 + nixide/Cargo.toml | 3 +- nixide/src/errors/error.rs | 22 +++++----- nixide/src/lib.rs | 6 ++- nixide/src/store/mod.rs | 82 +++++++----------------------------- nixide/src/store/tests.rs | 9 ---- nixide/src/util/panic.rs | 4 +- nixide/src/util/wrap.rs | 84 +++++++++++++++---------------------- nixide/src/util/wrappers.rs | 40 +++++++++--------- 9 files changed, 88 insertions(+), 163 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ff32633..9af7062 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -190,6 +190,7 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" name = "nixide" version = "0.1.0" dependencies = [ + "libc", "nixide-sys", "serial_test", "stdext", diff --git a/nixide/Cargo.toml b/nixide/Cargo.toml index e97d5fe..7a1b4e4 100644 --- a/nixide/Cargo.toml +++ b/nixide/Cargo.toml @@ -24,8 +24,9 @@ util = [] gc = [] [dependencies] -nixide-sys = { path = "../nixide-sys", version = "0.1.0" } +libc = "0.2.183" stdext = "0.3.3" +nixide-sys = { path = "../nixide-sys", version = "0.1.0" } [dev-dependencies] serial_test = "3.4.0" diff --git a/nixide/src/errors/error.rs b/nixide/src/errors/error.rs index bcebcf8..29696f1 100644 --- a/nixide/src/errors/error.rs +++ b/nixide/src/errors/error.rs @@ -137,15 +137,15 @@ impl Display for NixideError { } } -pub trait AsErr { - fn as_err(self) -> Result<(), T>; -} +// pub trait AsErr { +// fn as_err(self) -> Result<(), T>; +// } -impl AsErr for Option { - fn as_err(self) -> Result<(), NixideError> { - match self { - Some(err) => Err(err), - None => Ok(()), - } - } -} +// impl AsErr for Option { +// fn as_err(self) -> Result<(), NixideError> { +// match self { +// Some(err) => Err(err), +// None => Ok(()), +// } +// } +// } diff --git a/nixide/src/lib.rs b/nixide/src/lib.rs index 05a1738..5b363b4 100644 --- a/nixide/src/lib.rs +++ b/nixide/src/lib.rs @@ -1,5 +1,9 @@ // #![warn(missing_docs)] +// #[allow(unused_extern_crates)] +pub extern crate libc; +pub extern crate nixide_sys as sys; + pub(crate) mod errors; // mod expr; // mod flake; @@ -15,8 +19,6 @@ pub use store::{Store, StorePath}; pub use verbosity::NixVerbosity; pub use version::NixVersion; -pub use nixide_sys as sys; - /// Sets the verbosity level /// /// # Arguments diff --git a/nixide/src/store/mod.rs b/nixide/src/store/mod.rs index 7c057d7..0d52edb 100644 --- a/nixide/src/store/mod.rs +++ b/nixide/src/store/mod.rs @@ -23,7 +23,7 @@ use std::result::Result; use crate::errors::{new_nixide_error, ErrorContext}; use crate::stdext::CCharPtrExt; -use crate::util::wrap::{self, UserData}; +use crate::util::wrap; use crate::util::wrappers::AsInnerPtr; use crate::{NixideError, NixideResult}; use nixide_sys as sys; @@ -87,96 +87,42 @@ impl Store { pub fn realise( &self, path: &StorePath, - callback: fn(&str, &StorePath), - ) -> NixideResult<(String, StorePath)> { - // // Type alias for our userdata: (outputs vector, context) - // type Userdata = ( - // Vec<(String, StorePath)>, - // Arc, - // fn(&str, &StorePath), - // ); - // - // // Callback function that will be called for each realized output - // unsafe extern "C" fn realise_callback( - // userdata: *mut c_void, - // out_name_ptr: *const c_char, - // out_path_ptr: *const sys::StorePath, - // ) { - // // SAFETY: userdata is a valid pointer to our (Vec, Arc) tuple - // let (outputs, ctx, callback) = unsafe { &mut *(userdata as *mut Userdata) }; - // - // // SAFETY: outname is a valid C string from Nix - // let output_name = if !out_name_ptr.is_null() { - // unsafe { CStr::from_ptr(out_name_ptr).to_string_lossy().into_owned() } - // } else { - // String::from("out") // Default output name - // }; - // - // // SAFETY: out is a valid StorePath pointer from Nix, we need to clone it - // // because Nix owns the original and may free it after the callback - // if !out_path_ptr.is_null() { - // let cloned_path_ptr = - // unsafe { sys::nix_store_path_clone(out_path_ptr as *mut sys::StorePath) }; - // if let Some(inner) = NonNull::new(cloned_path_ptr) { - // let store_path = StorePath { inner }; - // - // callback(output_name.as_ref(), &store_path); - // - // outputs.push((output_name, store_path)); - // } - // } - // } - // - // // Create userdata with empty outputs vector and context - // let mut userdata: Userdata = (Vec::new(), Arc::new(ErrorContext::new()), callback); - // let userdata_ptr = &mut userdata as *mut Userdata as *mut std::os::raw::c_void; - // - // // SAFETY: All pointers are valid, callback is compatible with the FFI signature - // // - self._context is valid for the duration of this call - // // - self.inner is valid (checked in Store::open) - // // - path.inner is valid (checked in StorePath::parse) - // // - userdata_ptr points to valid stack memory - // // - realize_callback matches the expected C function signature - // let err = unsafe { - // sys::nix_store_realise( - // ctx.as_ptr(), - // self.inner.as_ptr(), - // path.as_ptr(), - // userdata_ptr, - // Some(realise_callback), - // ) - // }; - + user_callback: fn(&str, &StorePath), + ) -> NixideResult> { wrap::nix_callback!( |; userdata: fn(&str, &StorePath); output_name_ptr: *const c_char, output_path_ptr: *const sys::StorePath| - -> NixideResult<(String, StorePath)> { + -> Vec<(String, StorePath)> { // XXX: TODO: test to see if this is ever null ("out" as a default feels unsafe...) - // let output_name = output_name_ptr.to_utf8_string().unwrap_or("out".to_owned()); // NOTE: this also ensures `output_name_ptr` isn't null - let output_name = output_name_ptr.to_utf8_string()?; + let output_name = output_name_ptr.to_utf8_string().expect("IDK1"); let inner = wrap::nix_ptr_fn!(|ctx| unsafe { sys::nix_store_path_clone(output_path_ptr as *mut sys::StorePath) - })?; + }).expect("IDK2"); let store_path = StorePath { inner }; let callback = unsafe { (*userdata).inner }; callback(output_name.as_ref(), &store_path); - Ok((output_name, store_path)) + (output_name, store_path); }, |callback, - state: *mut UserData>, + state: *mut __UserData, ctx: &ErrorContext| unsafe { + // register userdata + // WARNING: Using `write` instead of assignment via `=` + // WARNING: to not call `drop` on the old, uninitialized value. + (&raw mut (*state).inner).write(user_callback); + sys::nix_store_realise( ctx.as_ptr(), self.inner.as_ptr(), path.as_ptr(), (*state).inner_ptr() as *mut c_void, Some(callback), - ) + ); } ) } diff --git a/nixide/src/store/tests.rs b/nixide/src/store/tests.rs index d0b7744..da72ae7 100644 --- a/nixide/src/store/tests.rs +++ b/nixide/src/store/tests.rs @@ -16,9 +16,6 @@ fn test_store_opening() { sys::nix_libstore_init(ctx.as_ptr()); ctx.pop() .expect("nix_libstore_init failed with bad ErrorContext"); - sys::nix_libexpr_init(ctx.as_ptr()); - ctx.pop() - .expect("nix_libexpr_init failed with bad ErrorContext"); }; let _store = Store::open(None).expect("Failed to open store"); @@ -35,9 +32,6 @@ fn test_store_path_parse() { sys::nix_libstore_init(ctx.as_ptr()); ctx.pop() .expect("nix_libstore_init failed with bad ErrorContext"); - sys::nix_libexpr_init(ctx.as_ptr()); - ctx.pop() - .expect("nix_libexpr_init failed with bad ErrorContext"); }; let store = Store::open(None).expect("Failed to open store"); @@ -58,9 +52,6 @@ fn test_store_path_clone() { sys::nix_libstore_init(ctx.as_ptr()); ctx.pop() .expect("nix_libstore_init failed with bad ErrorContext"); - sys::nix_libexpr_init(ctx.as_ptr()); - ctx.pop() - .expect("nix_libexpr_init failed with bad ErrorContext"); }; let store = Store::open(None).expect("Failed to open store"); diff --git a/nixide/src/util/panic.rs b/nixide/src/util/panic.rs index 51f2f34..b9e5e95 100644 --- a/nixide/src/util/panic.rs +++ b/nixide/src/util/panic.rs @@ -9,10 +9,10 @@ macro_rules! panic_issue { macro_rules! panic_issue_call_failed { () => {{ - crate::util::panic_issue!("[nixide] call to `{}` failed", stdext::debug_name!()) + crate::util::panic_issue!("[nixide] call to `{}` failed", $crate::stdext::debug_name!()) }}; ($($arg:expr),*) => {{ - crate::util::panic_issue!("[nixide] call to `{}` failed with \"{}\"", stdext::debug_name!(), format!($($arg),*)) + crate::util::panic_issue!("[nixide] call to `{}` failed with \"{}\"", $crate::stdext::debug_name!(), format!($($arg),*)) }}; } diff --git a/nixide/src/util/wrap.rs b/nixide/src/util/wrap.rs index 595511b..33790c9 100644 --- a/nixide/src/util/wrap.rs +++ b/nixide/src/util/wrap.rs @@ -4,6 +4,8 @@ pub(crate) struct UserData { pub inner: S, pub retval: T, + // XXX: TODO: write impl functions to set and get these values, + // XXX: TODO: and another one to unwrap a `MaybeUninit>` #[cfg(debug_assertions)] pub init_inner: bool, @@ -18,14 +20,6 @@ impl AsMut> for UserData { } impl UserData { - /// # Warning - /// - /// Ensure `self.retval` has been initialised before unwrapping! - /// - // pub unsafe fn unwrap(self) -> (S, T) { - // (self.inner, self.retval) - // } - pub unsafe fn as_mut_ptr(&mut self) -> *mut Self { self as *mut Self } @@ -36,10 +30,6 @@ impl UserData { &raw mut (*ptr).inner } } - - // pub unsafe fn retval_ptr(&mut self) -> *mut c_void { - // &mut self.retval as *mut T as *mut c_void - // } } macro_rules! nonnull { @@ -73,42 +63,27 @@ macro_rules! nix_ptr_fn { } pub(crate) use nix_ptr_fn; -macro_rules! __nix_callback { - ($userdata_type:ty, $ret:ty, $callback:expr) => {{ - let mut __ctx = $crate::errors::ErrorContext::new(); - let mut __state: ::std::mem::MaybeUninit<__UserData> = ::std::mem::MaybeUninit::uninit(); - - $callback(__wrapper_callback, __state.as_mut_ptr(), &__ctx); - - // add type annotations for compiler - let __return: $ret = __ctx - .pop() - .and_then(|_| unsafe { __state.assume_init().retval }); - __return - }}; -} -#[allow(unused_imports)] // XXX: TODO: replace the tail of `nix_callback!` with this macro -pub(crate) use __nix_callback; - /// `libnix` functions consistently either expect the `userdata`/`user_data` (inconsistently named in the API...) /// field to be the first or last parameter (differs between function). The `nix_callback!` macro allows the /// position to be specified by either the following syntax: /// /// ```rs -/// nix_callback(userdata; ...); // first parameter -/// nix_callback(...; userdata); // last parameter +/// nix_callback!(; userdata; ...); // first parameter +/// nix_callback!(...; userdata; ); // last parameter /// ``` /// macro_rules! nix_callback { ( | $( $($pre:ident : $pre_ty:ty),+ $(,)? )? ; $userdata:ident : $userdata_type:ty ; $( $($post:ident : $post_ty:ty),+ $(,)? )? | -> $ret:ty $body:block, $function:expr $(,)? ) => {{ type __UserData = $crate::util::wrap::UserData<$userdata_type, $ret>; // create a function item that wraps the closure body (so it has a concrete type) + // WARNING: this function must have no return type, use the `UserData.inner` + // WARNING: field instead as an `out` pointer. #[allow(unused_variables)] unsafe extern "C" fn __captured_fn( $($( $pre: $pre_ty, )*)? $userdata: *mut __UserData, $($( $post: $post_ty, )*)? - ) -> $ret { $body } + ) { $body } unsafe extern "C" fn __wrapper_callback( $($( $pre: $pre_ty, )*)? @@ -116,17 +91,12 @@ macro_rules! nix_callback { $($( $post: $post_ty, )*)? ) { unsafe { - let userdata_ = $userdata as *mut __UserData; - let stored_retval = &raw mut (*userdata_).retval; - - let retval = - __captured_fn( - $($( $pre, )*)? - userdata_, - $($( $post, )*)? - ); - - stored_retval.write(retval) + __captured_fn( + $($( $pre, )*)? + // userdata_, + $userdata as *mut __UserData, + $($( $post, )*)? + ); } } @@ -136,21 +106,35 @@ macro_rules! nix_callback { $function(__wrapper_callback, __state.as_mut_ptr(), &__ctx); // type annotations for compiler - let __result: $ret = __ctx.pop().and_then(|_| unsafe { __state.assume_init().retval }); - __result + let __return: $crate::NixideResult<$ret> = __ctx.pop().and_then(|_| ::std::result::Result::Ok(unsafe { __state.assume_init().retval })); + __return }}; } pub(crate) use nix_callback; -// XXX: TODO: convert these to declarative macros macro_rules! nix_string_callback { ($function:expr $(,)?) => {{ - $crate::util::wrap::nix_callback!( - |start: *const ::std::ffi::c_char, n: ::std::ffi::c_uint; userdata: ();| -> $crate::NixideResult { - $crate::stdext::CCharPtrExt::to_utf8_string_n(start, n as usize) + #[repr(C)] + struct __ReturnType { + start: *const ::std::ffi::c_char, + n: ::std::ffi::c_uint, + } + + let __result = $crate::util::wrap::nix_callback!( + |start: *const ::std::ffi::c_char, n: ::std::ffi::c_uint; userdata: ();| -> (*const ::std::ffi::c_char, ::std::ffi::c_uint) { + unsafe { + let retval = &raw mut (*userdata).retval; + retval.write((start, n)) + } }, $function - ) + ); + + __result.and_then(|(start, n)| { + let __return = $crate::stdext::CCharPtrExt::to_utf8_string_n(start, n as usize); + + __return + }) }}; } pub(crate) use nix_string_callback; diff --git a/nixide/src/util/wrappers.rs b/nixide/src/util/wrappers.rs index 829437a..ab43cce 100644 --- a/nixide/src/util/wrappers.rs +++ b/nixide/src/util/wrappers.rs @@ -1,17 +1,23 @@ -use crate::NixideError; - -pub trait AsErr { - fn as_err(self) -> Result<(), T>; -} - -impl AsErr for Option { - fn as_err(self) -> Result<(), NixideError> { - match self { - Some(err) => Err(err), - None => Ok(()), - } - } -} +// use crate::NixideError; +// +// pub trait AsErr { +// fn as_err(self) -> Result<(), T>; +// } +// +// impl AsErr for Option { +// fn as_err(self) -> Result<(), NixideError> { +// match self { +// Some(err) => Err(err), +// None => Ok(()), +// } +// } +// } +// +// pub trait FromC { +// /// Creates a new instance of [Self] from the underlying +// /// libnix C type [T]. +// unsafe fn from_c(value: T) -> Self; +// } pub trait AsInnerPtr { /// Get a pointer to the underlying (`inner`) `libnix` C struct. @@ -23,9 +29,3 @@ pub trait AsInnerPtr { /// in `unsafe` blocks for clarity. unsafe fn as_ptr(&self) -> *mut T; } - -pub trait FromC { - /// Creates a new instance of [Self] from the underlying - /// libnix C type [T]. - unsafe fn from_c(value: T) -> Self; -} From 0ba06d0f2cc096a9d575b913b7f7414484384fb6 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Thu, 26 Mar 2026 01:30:38 +1000 Subject: [PATCH 306/306] begin nixide::expr refactor --- nixide/src/errors/context.rs | 11 ++ nixide/src/expr/evalstate.rs | 59 ++++----- nixide/src/expr/evalstatebuilder.rs | 54 +++++---- nixide/src/expr/mod.rs | 4 + nixide/src/expr/realised_string.rs | 121 +++++++++++++++++++ nixide/src/expr/tests.rs | 13 +- nixide/src/expr/value.rs | 180 ++++++++-------------------- nixide/src/expr/values/attrs.rs | 0 nixide/src/expr/values/bool.rs | 70 +++++++++++ nixide/src/expr/values/external.rs | 0 nixide/src/expr/values/float.rs | 69 +++++++++++ nixide/src/expr/values/function.rs | 0 nixide/src/expr/values/int.rs | 69 +++++++++++ nixide/src/expr/values/list.rs | 0 nixide/src/expr/values/mod.rs | 29 +++++ nixide/src/expr/values/null.rs | 0 nixide/src/expr/values/path.rs | 0 nixide/src/expr/values/string.rs | 75 ++++++++++++ nixide/src/expr/values/thunk.rs | 0 nixide/src/expr/valuetype.rs | 6 +- nixide/src/lib.rs | 4 +- nixide/src/store/mod.rs | 11 ++ nixide/src/util/lazy_array.rs | 79 ++++++++++++ nixide/src/util/mod.rs | 36 +----- nixide/src/util/wrappers.rs | 45 +++---- 25 files changed, 681 insertions(+), 254 deletions(-) create mode 100644 nixide/src/expr/realised_string.rs create mode 100644 nixide/src/expr/values/attrs.rs create mode 100644 nixide/src/expr/values/bool.rs create mode 100644 nixide/src/expr/values/external.rs create mode 100644 nixide/src/expr/values/float.rs create mode 100644 nixide/src/expr/values/function.rs create mode 100644 nixide/src/expr/values/int.rs create mode 100644 nixide/src/expr/values/list.rs create mode 100644 nixide/src/expr/values/mod.rs create mode 100644 nixide/src/expr/values/null.rs create mode 100644 nixide/src/expr/values/path.rs create mode 100644 nixide/src/expr/values/string.rs create mode 100644 nixide/src/expr/values/thunk.rs create mode 100644 nixide/src/util/lazy_array.rs diff --git a/nixide/src/errors/context.rs b/nixide/src/errors/context.rs index d425b38..87d4b8e 100644 --- a/nixide/src/errors/context.rs +++ b/nixide/src/errors/context.rs @@ -68,9 +68,20 @@ pub(crate) struct ErrorContext { } impl AsInnerPtr for ErrorContext { + #[inline] unsafe fn as_ptr(&self) -> *mut sys::nix_c_context { self.inner.as_ptr() } + + #[inline] + unsafe fn as_ref(&self) -> &sys::nix_c_context { + unsafe { self.inner.as_ref() } + } + + #[inline] + unsafe fn as_mut(&mut self) -> &mut sys::nix_c_context { + unsafe { self.inner.as_mut() } + } } impl Into> for &ErrorContext { diff --git a/nixide/src/expr/evalstate.rs b/nixide/src/expr/evalstate.rs index bdb7884..304409a 100644 --- a/nixide/src/expr/evalstate.rs +++ b/nixide/src/expr/evalstate.rs @@ -7,8 +7,9 @@ use crate::errors::new_nixide_error; use super::Value; use crate::errors::ErrorContext; use crate::sys; +use crate::util::wrap; use crate::util::wrappers::AsInnerPtr; -use crate::{NixideError, Store}; +use crate::{NixideResult, Store}; /// Nix evaluation state for evaluating expressions. /// @@ -18,14 +19,24 @@ pub struct EvalState { inner: NonNull, // XXX: TODO: is an `Arc` necessary or just a `Store` - #[allow(dead_code)] store: Arc, } impl AsInnerPtr for EvalState { + #[inline] unsafe fn as_ptr(&self) -> *mut sys::EvalState { self.inner.as_ptr() } + + #[inline] + unsafe fn as_ref(&self) -> &sys::EvalState { + unsafe { self.inner.as_ref() } + } + + #[inline] + unsafe fn as_mut(&mut self) -> &mut sys::EvalState { + unsafe { self.inner.as_mut() } + } } impl EvalState { @@ -34,6 +45,11 @@ impl EvalState { Self { inner, store } } + #[inline] + pub(crate) unsafe fn store_ref(&self) -> &Store { + self.store.as_ref() + } + /// Evaluate a Nix expression from a string. /// /// # Arguments @@ -44,24 +60,15 @@ impl EvalState { /// # Errors /// /// Returns an error if evaluation fails. - pub fn eval_from_string(&self, expr: &str, path: &str) -> Result { + pub fn eval_from_string(&self, expr: &str, path: &str) -> NixideResult { let expr_c = CString::new(expr).or(Err(new_nixide_error!(StringNulByte)))?; let path_c = CString::new(path).or(Err(new_nixide_error!(StringNulByte)))?; - let ctx = ErrorContext::new(); // Allocate value for result - // XXX: TODO: refactor this code to use `nixide::Value` - let value_ptr = unsafe { sys::nix_alloc_value(ctx.as_ptr(), self.as_ptr()) }; - let value = match ctx.peak() { - Some(err) => Err(err), - None => match NonNull::new(value_ptr) { - Some(inner) => Ok(Value { inner }), - None => Err(new_nixide_error!(NullPtr)), - }, - }?; + let value = self.new_value()?; // Evaluate expression - unsafe { + wrap::nix_fn!(|ctx: &ErrorContext| unsafe { sys::nix_expr_eval_from_string( ctx.as_ptr(), self.as_ptr(), @@ -69,11 +76,8 @@ impl EvalState { path_c.as_ptr(), value.as_ptr(), ); - }; - match ctx.peak() { - Some(err) => Err(err), - None => Ok(value), - } + value + }) } /// Allocate a new value. @@ -81,16 +85,13 @@ impl EvalState { /// # Errors /// /// Returns an error if value allocation fails. - pub fn alloc_value(&self) -> Result { - let ctx = ErrorContext::new(); - let value_ptr = unsafe { sys::nix_alloc_value(ctx.as_ptr(), self.as_ptr()) }; - match ctx.peak() { - Some(err) => Err(err), - None => match NonNull::new(value_ptr) { - Some(inner) => Ok(Value { inner }), - None => Err(new_nixide_error!(NullPtr)), - }, - } + pub(self) fn new_value(&self) -> NixideResult { + // XXX: TODO: should this function be `Value::new` instead? + let inner = wrap::nix_ptr_fn!(|ctx: &ErrorContext| unsafe { + sys::nix_alloc_value(ctx.as_ptr(), self.as_ptr()) + })?; + + Ok(Value::new(inner, self)) } } diff --git a/nixide/src/expr/evalstatebuilder.rs b/nixide/src/expr/evalstatebuilder.rs index 25cf117..82908dc 100644 --- a/nixide/src/expr/evalstatebuilder.rs +++ b/nixide/src/expr/evalstatebuilder.rs @@ -2,8 +2,9 @@ use std::ptr::NonNull; use std::sync::Arc; use super::EvalState; -use crate::errors::{new_nixide_error, ErrorContext, NixideError}; +use crate::errors::{ErrorContext, NixideResult}; use crate::sys; +use crate::util::wrap; use crate::util::wrappers::AsInnerPtr; use crate::Store; @@ -16,6 +17,23 @@ pub struct EvalStateBuilder { store: Arc, } +impl AsInnerPtr for EvalStateBuilder { + #[inline] + unsafe fn as_ptr(&self) -> *mut sys::nix_eval_state_builder { + self.inner.as_ptr() + } + + #[inline] + unsafe fn as_ref(&self) -> &sys::nix_eval_state_builder { + unsafe { self.inner.as_ref() } + } + + #[inline] + unsafe fn as_mut(&mut self) -> &mut sys::nix_eval_state_builder { + unsafe { self.inner.as_mut() } + } +} + impl EvalStateBuilder { /// Create a new evaluation state builder. /// @@ -26,12 +44,10 @@ impl EvalStateBuilder { /// # Errors /// /// Returns an error if the builder cannot be created. - pub fn new(store: &Arc) -> Result { - // SAFETY: store context and store are valid - let builder_ptr = - unsafe { sys::nix_eval_state_builder_new(store._context.as_ptr(), store.as_ptr()) }; - - let inner = NonNull::new(builder_ptr).ok_or(new_nixide_error!(NullPtr))?; + pub fn new(store: &Arc) -> NixideResult { + let inner = wrap::nix_ptr_fn!(|ctx: &ErrorContext| unsafe { + sys::nix_eval_state_builder_new(ctx.as_ptr(), store.as_ptr()) + })?; Ok(EvalStateBuilder { inner, @@ -44,29 +60,19 @@ impl EvalStateBuilder { /// # Errors /// /// Returns an error if the evaluation state cannot be built. - pub fn build(self) -> Result { - let ctx = ErrorContext::new(); + pub fn build(self) -> NixideResult { // Load configuration first - unsafe { sys::nix_eval_state_builder_load(ctx.as_ptr(), self.as_ptr()) }; - if let Some(err) = ctx.peak() { - return Err(err); - } + wrap::nix_fn!(|ctx: &ErrorContext| unsafe { + sys::nix_eval_state_builder_load(ctx.as_ptr(), self.as_ptr()) + })?; // Build the state - let state_ptr = unsafe { sys::nix_eval_state_build(ctx.as_ptr(), self.as_ptr()) }; - if let Some(err) = ctx.peak() { - return Err(err); - } + let inner = wrap::nix_ptr_fn!(|ctx: &ErrorContext| unsafe { + sys::nix_eval_state_build(ctx.as_ptr(), self.as_ptr()) + })?; - let inner = NonNull::new(state_ptr).ok_or(new_nixide_error!(NullPtr))?; - - // The builder is consumed here - its Drop will clean up Ok(EvalState::new(inner, self.store.clone())) } - - pub(crate) unsafe fn as_ptr(&self) -> *mut sys::nix_eval_state_builder { - self.inner.as_ptr() - } } impl Drop for EvalStateBuilder { diff --git a/nixide/src/expr/mod.rs b/nixide/src/expr/mod.rs index ab4ae81..8487311 100644 --- a/nixide/src/expr/mod.rs +++ b/nixide/src/expr/mod.rs @@ -3,10 +3,14 @@ mod tests; mod evalstate; mod evalstatebuilder; +mod realised_string; mod value; +mod values; mod valuetype; pub use evalstate::EvalState; pub use evalstatebuilder::EvalStateBuilder; +pub use realised_string::RealisedString; pub use value::Value; +pub use values::{NixInt, NixValue}; pub use valuetype::ValueType; diff --git a/nixide/src/expr/realised_string.rs b/nixide/src/expr/realised_string.rs new file mode 100644 index 0000000..5be5f8b --- /dev/null +++ b/nixide/src/expr/realised_string.rs @@ -0,0 +1,121 @@ +use std::ffi::c_char; +use std::ptr::NonNull; +use std::sync::Arc; + +use crate::errors::ErrorContext; +use crate::expr::values::NixString; +use crate::stdext::CCharPtrExt; +use crate::sys; +use crate::util::wrappers::AsInnerPtr; +use crate::util::LazyArray; +use crate::util::{panic_issue_call_failed, wrap}; +use crate::{EvalState, NixideResult, StorePath}; + +pub struct RealisedString { + inner: NonNull, + // pub path: LazyCell StorePath>>, + pub path: StorePath, + pub children: LazyArray StorePath>, usize) -> StorePath>>, +} + +impl AsInnerPtr for RealisedString { + #[inline] + unsafe fn as_ptr(&self) -> *mut sys::nix_realised_string { + self.inner.as_ptr() + } + + #[inline] + unsafe fn as_ref(&self) -> &sys::nix_realised_string { + unsafe { self.inner.as_ref() } + } + + #[inline] + unsafe fn as_mut(&mut self) -> &mut sys::nix_realised_string { + unsafe { self.inner.as_mut() } + } +} + +impl Drop for RealisedString { + fn drop(&mut self) { + unsafe { + sys::nix_realised_string_free(self.as_ptr()); + } + } +} + +impl RealisedString { + /// Realise a string context. + /// + /// This will + /// - realise the store paths referenced by the string's context, and + /// - perform the replacement of placeholders. + /// - create temporary garbage collection roots for the store paths, for + /// the lifetime of the current process. + /// - log to stderr + /// + /// # Arguments + /// + /// * value - Nix value, which must be a string + /// * state - Nix evaluator state + /// * isIFD - If true, disallow derivation outputs if setting `allow-import-from-derivation` is false. + /// You should set this to true when this call is part of a primop. + /// You should set this to false when building for your application's purpose. + /// + /// # Returns + /// + /// NULL if failed, or a new nix_realised_string, which must be freed with nix_realised_string_free + pub fn new(value: &NixString, state: &Arc) -> NixideResult { + let inner = wrap::nix_ptr_fn!(|ctx: &ErrorContext| unsafe { + sys::nix_string_realise( + ctx.as_ptr(), + state.as_ptr(), + value.as_ptr(), + false, // don't copy more + ) + })?; + + fn delegate( + inner: &LazyArray StorePath>>, + index: usize, + ) -> StorePath { + // XXX: TODO + // inner[index] + StorePath::fake_path(unsafe { state.store_ref() }).unwrap() + } + + let size = unsafe { sys::nix_realised_string_get_store_path_count(inner.as_ptr()) }; + + Ok(Self { + inner, + path: Self::parse_path(inner.as_ptr(), state), + // children: LazyArray::new(size, delegate as fn(usize) -> StorePath), + children: LazyArray:: StorePath>>::new( + size, + Box::new(delegate), + ), + }) + } + + fn parse_path( + realised_string: *mut sys::nix_realised_string, + state: &Arc, + ) -> StorePath { + let buffer_ptr = unsafe { sys::nix_realised_string_get_buffer_start(realised_string) }; + let buffer_size = unsafe { sys::nix_realised_string_get_buffer_size(realised_string) }; + + let path_str = (buffer_ptr as *const c_char) + .to_utf8_string_n(buffer_size) + .unwrap_or_else(|err| { + panic_issue_call_failed!( + "`sys::nix_realised_string_get_buffer_(start|size)` invalid UTF-8 ({})", + err + ) + }); + StorePath::parse(unsafe { state.store_ref() }, &path_str).unwrap_or_else(|err| { + panic_issue_call_failed!( + "`sys::nix_realised_string_get_buffer_(start|size)` invalid store path ({})", + err + ) + }) + } +} diff --git a/nixide/src/expr/tests.rs b/nixide/src/expr/tests.rs index 1bcc04a..702b44d 100644 --- a/nixide/src/expr/tests.rs +++ b/nixide/src/expr/tests.rs @@ -3,14 +3,12 @@ use std::sync::Arc; use serial_test::serial; use super::{EvalStateBuilder, ValueType}; -use crate::errors::ErrorContext; use crate::Store; #[test] #[serial] fn test_eval_state_builder() { - let ctx = Arc::new(ErrorContext::new()); - let store = Arc::new(Store::open(&ctx, None).expect("Failed to open store")); + let store = Arc::new(Store::open(None).expect("Failed to open store")); let _state = EvalStateBuilder::new(&store) .expect("Failed to create builder") .build() @@ -21,8 +19,7 @@ fn test_eval_state_builder() { #[test] #[serial] fn test_simple_evaluation() { - let ctx = Arc::new(ErrorContext::new()); - let store = Arc::new(Store::open(&ctx, None).expect("Failed to open store")); + let store = Arc::new(Store::open(None).expect("Failed to open store")); let state = EvalStateBuilder::new(&store) .expect("Failed to create builder") .build() @@ -39,8 +36,7 @@ fn test_simple_evaluation() { #[test] #[serial] fn test_value_types() { - let ctx = Arc::new(ErrorContext::new()); - let store = Arc::new(Store::open(&ctx, None).expect("Failed to open store")); + let store = Arc::new(Store::open(None).expect("Failed to open store")); let state = EvalStateBuilder::new(&store) .expect("Failed to create builder") .build() @@ -71,8 +67,7 @@ fn test_value_types() { #[test] #[serial] fn test_value_formatting() { - let ctx = Arc::new(ErrorContext::new()); - let store = Arc::new(Store::open(&ctx, None).expect("Failed to open store")); + let store = Arc::new(Store::open(None).expect("Failed to open store")); let state = EvalStateBuilder::new(&store) .expect("Failed to create builder") .build() diff --git a/nixide/src/expr/value.rs b/nixide/src/expr/value.rs index 93f3787..ce7237c 100644 --- a/nixide/src/expr/value.rs +++ b/nixide/src/expr/value.rs @@ -2,30 +2,40 @@ use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; use std::ptr::NonNull; use super::{EvalState, ValueType}; -use crate::errors::{new_nixide_error, ErrorContext, NixideError}; +use crate::errors::{ErrorContext, NixideResult}; use crate::sys; -use crate::util::wrappers::{AsInnerPtr, FromC as _}; -use crate::util::AsErr; +use crate::util::wrappers::AsInnerPtr; +use crate::util::{panic_issue_call_failed, wrap}; /// A Nix value /// /// This represents any value in the Nix language, including primitives, /// collections, and functions. -pub struct Value { +pub struct Value<'a> { pub(crate) inner: NonNull, + state: &'a EvalState, } -impl AsInnerPtr for Value { +impl<'a> AsInnerPtr for Value<'a> { + #[inline] unsafe fn as_ptr(&self) -> *mut sys::nix_value { self.inner.as_ptr() } + + #[inline] + unsafe fn as_ref(&self) -> &sys::nix_value { + unsafe { self.inner.as_ref() } + } + + #[inline] + unsafe fn as_mut(&mut self) -> &mut sys::nix_value { + unsafe { self.inner.as_mut() } + } } -impl Value { - pub(crate) unsafe fn new(inner: *mut sys::Value) -> Self { - Value { - inner: NonNull::new(inner).unwrap(), - } +impl<'a> Value<'a> { + pub(crate) fn new(inner: NonNull, state: &'a EvalState) -> Self { + Value { inner, state } } /// Force evaluation of this value. @@ -35,12 +45,11 @@ impl Value { /// # Errors /// /// Returns an error if evaluation fails. - pub fn force(&mut self, state: &EvalState) -> Result<(), NixideError> { + pub fn force(&mut self) -> NixideResult<()> { // XXX: TODO: move force and force_deep to the EvalState - let ctx = ErrorContext::new(); - - unsafe { sys::nix_value_force(ctx.as_ptr(), state.as_ptr(), self.as_ptr()) }; - ctx.peak() + wrap::nix_fn!(|ctx: &ErrorContext| unsafe { + sys::nix_value_force(ctx.as_ptr(), self.state.as_ptr(), self.as_ptr()); + }) } /// Force deep evaluation of this value. @@ -50,132 +59,39 @@ impl Value { /// # Errors /// /// Returns an error if evaluation fails. - pub fn force_deep(&mut self, state: &EvalState) -> Result<(), NixideError> { - let ctx = ErrorContext::new(); - - unsafe { sys::nix_value_force_deep(ctx.as_ptr(), state.as_ptr(), self.as_ptr()) }; - ctx.peak() + pub fn force_deep(&mut self) -> NixideResult<()> { + // XXX: TODO: move force and force_deep to the EvalState + wrap::nix_fn!(|ctx: &ErrorContext| unsafe { + sys::nix_value_force_deep(ctx.as_ptr(), self.state.as_ptr(), self.as_ptr()); + }) } /// Get the type of this value. #[must_use] pub fn value_type(&self) -> ValueType { - let ctx = ErrorContext::new(); - let value_type = - unsafe { ValueType::from_c(sys::nix_get_type(ctx.as_ptr(), self.as_ptr())) }; // NOTE: an error here only occurs if `nix_get_type` catches an error, // NOTE: which in turn only happens if the `sys::nix_value*` is a null pointer // NOTE: or points to an uninitialised `nix_value` struct. - ctx.peak().unwrap_or_else(|_| panic!("TODO im sleepy rn")); - value_type + ValueType::from({ + wrap::nix_fn!(|ctx: &ErrorContext| unsafe { + sys::nix_get_type(ctx.as_ptr(), self.as_ptr()) + }) + .unwrap_or_else(|err| panic_issue_call_failed!("{}", err)) + }) } - fn expect_type(&self, expected: ValueType) -> Result<(), NixideError> { - let got = self.value_type(); - if got != expected { - return Err(new_nixide_error!( - InvalidType, - expected.to_string(), - got.to_string() - )); - } - Ok(()) - } - - /// Convert this value to an integer. - /// - /// # Errors - /// - /// Returns an error if the value is not an integer. - pub fn as_int(&self) -> Result { - self.expect_type(ValueType::Int)?; - - let ctx = ErrorContext::new(); - let result = unsafe { sys::nix_get_int(ctx.as_ptr(), self.as_ptr()) }; - match ctx.peak() { - Some(err) => Err(err), - None => Ok(result), - } - } - - /// Convert this value to a float. - /// - /// # Errors - /// - /// Returns an error if the value is not a float. - pub fn as_float(&self) -> Result { - self.expect_type(ValueType::Float)?; - - let ctx = ErrorContext::new(); - let result = unsafe { sys::nix_get_float(ctx.as_ptr(), self.as_ptr()) }; - match ctx.peak() { - Some(err) => Err(err), - None => Ok(result), - } - } - - /// Convert this value to a boolean. - /// - /// # Errors - /// - /// Returns an error if the value is not a boolean. - pub fn as_bool(&self) -> Result { - self.expect_type(ValueType::Bool)?; - - let ctx = ErrorContext::new(); - let result = unsafe { sys::nix_get_bool(ctx.as_ptr(), self.as_ptr()) }; - match ctx.peak() { - Some(err) => Err(err), - None => Ok(result), - } - } - - /// Convert this value to a string. - /// - /// # Errors - /// - /// Returns an error if the value is not a string. - pub fn as_string(&self) -> Result { - self.expect_type(ValueType::String)?; - - let ctx = ErrorContext::new(); - - // For string values, we need to use realised string API - let realised_str = unsafe { - sys::nix_string_realise( - ctx.as_ptr(), - self.state.as_ptr(), - self.as_ptr(), - false, // don't copy more - ) - }; - - if realised_str.is_null() { - return Err(new_nixide_error!(NullPtr)); - } - - let buffer_start = unsafe { sys::nix_realised_string_get_buffer_start(realised_str) }; - let buffer_size = unsafe { sys::nix_realised_string_get_buffer_size(realised_str) }; - if buffer_start.is_null() { - // Clean up realised string - unsafe { - sys::nix_realised_string_free(realised_str); - } - return Err(new_nixide_error!(NullPtr)); - } - - let bytes = unsafe { std::slice::from_raw_parts(buffer_start.cast::(), buffer_size) }; - let string = std::str::from_utf8(bytes) - .map_err(|_| new_nixide_error!(StringNotUtf8))? - .to_owned(); - - // Clean up realised string - unsafe { - sys::nix_realised_string_free(realised_str); - } - - Ok(string) - } + // XXX: TODO: rewrite `expr/value.rs` to make this redundant + // fn expect_type(&self, expected: ValueType) -> NixideResult<()> { + // let got = self.value_type(); + // if got != expected { + // return Err(new_nixide_error!( + // InvalidType, + // expected.to_string(), + // got.to_string() + // )); + // } + // Ok(()) + // } /// Format this value as Nix syntax. /// @@ -186,7 +102,7 @@ impl Value { /// /// Returns an error if the value cannot be converted to a string /// representation. - pub fn to_nix_string(&self) -> Result { + pub fn to_nix_string(&self) -> NixideResult { match self.value_type() { ValueType::Int => Ok(self.as_int()?.to_string()), ValueType::Float => Ok(self.as_float()?.to_string()), diff --git a/nixide/src/expr/values/attrs.rs b/nixide/src/expr/values/attrs.rs new file mode 100644 index 0000000..e69de29 diff --git a/nixide/src/expr/values/bool.rs b/nixide/src/expr/values/bool.rs new file mode 100644 index 0000000..fd5598b --- /dev/null +++ b/nixide/src/expr/values/bool.rs @@ -0,0 +1,70 @@ +use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; +use std::ptr::NonNull; + +use super::NixValue; +use crate::errors::ErrorContext; +use crate::sys; +use crate::util::panic_issue_call_failed; +use crate::util::wrap; +use crate::util::wrappers::AsInnerPtr; + +pub struct NixBool { + inner: NonNull, + value: bool, +} + +impl Display for NixBool { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "") + } +} + +impl Debug for NixBool { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "NixBool(${})", self.value) + } +} + +impl AsInnerPtr for NixBool { + #[inline] + unsafe fn as_ptr(&self) -> *mut sys::nix_value { + self.inner.as_ptr() + } + + #[inline] + unsafe fn as_ref(&self) -> &sys::nix_value { + unsafe { self.inner.as_ref() } + } + + #[inline] + unsafe fn as_mut(&mut self) -> &mut sys::nix_value { + unsafe { self.inner.as_mut() } + } +} + +impl NixValue for NixBool { + #[inline] + fn get_enum_id(&self) -> sys::ValueType { + sys::ValueType_NIX_TYPE_BOOL + } + + fn new(inner: NonNull) -> Self { + let value = wrap::nix_fn!(|ctx: &ErrorContext| unsafe { + sys::nix_get_bool(ctx.as_ptr(), inner.as_ptr()) + }) + .unwrap_or_else(|err| { + panic_issue_call_failed!("`sys::nix_get_bool` failed for valid `NixBool` ({})", err) + }); + + Self { inner, value } + } +} + +impl NixBool { + /// Returns a shared reference to the underlying value. + /// + #[inline] + fn value(&self) -> &bool { + &self.value + } +} diff --git a/nixide/src/expr/values/external.rs b/nixide/src/expr/values/external.rs new file mode 100644 index 0000000..e69de29 diff --git a/nixide/src/expr/values/float.rs b/nixide/src/expr/values/float.rs new file mode 100644 index 0000000..84c5539 --- /dev/null +++ b/nixide/src/expr/values/float.rs @@ -0,0 +1,69 @@ +use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; +use std::ptr::NonNull; + +use super::NixValue; +use crate::errors::ErrorContext; +use crate::sys; +use crate::util::wrappers::AsInnerPtr; +use crate::util::{panic_issue_call_failed, wrap}; + +pub struct NixFloat { + inner: NonNull, + value: f64, +} + +impl Display for NixFloat { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "") + } +} + +impl Debug for NixFloat { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "NixFloat(${})", self.value()) + } +} + +impl AsInnerPtr for NixFloat { + #[inline] + unsafe fn as_ptr(&self) -> *mut sys::nix_value { + self.inner.as_ptr() + } + + #[inline] + unsafe fn as_ref(&self) -> &sys::nix_value { + unsafe { self.inner.as_ref() } + } + + #[inline] + unsafe fn as_mut(&mut self) -> &mut sys::nix_value { + unsafe { self.inner.as_mut() } + } +} + +impl NixValue for NixFloat { + #[inline] + fn get_enum_id(&self) -> sys::ValueType { + sys::ValueType_NIX_TYPE_FLOAT + } + + fn new(inner: NonNull) -> Self { + let value = wrap::nix_fn!(|ctx: &ErrorContext| unsafe { + sys::nix_get_float(ctx.as_ptr(), inner.as_ptr()) + }) + .unwrap_or_else(|err| { + panic_issue_call_failed!("`sys::nix_get_float` failed for valid `NixFloat` ({})", err) + }); + + Self { inner, value } + } +} + +impl NixFloat { + /// Returns a shared reference to the underlying value. + /// + #[inline] + fn value(&self) -> &f64 { + &self.value + } +} diff --git a/nixide/src/expr/values/function.rs b/nixide/src/expr/values/function.rs new file mode 100644 index 0000000..e69de29 diff --git a/nixide/src/expr/values/int.rs b/nixide/src/expr/values/int.rs new file mode 100644 index 0000000..e9a6264 --- /dev/null +++ b/nixide/src/expr/values/int.rs @@ -0,0 +1,69 @@ +use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; +use std::ptr::NonNull; + +use super::NixValue; +use crate::errors::ErrorContext; +use crate::sys; +use crate::util::wrappers::AsInnerPtr; +use crate::util::{panic_issue_call_failed, wrap}; + +pub struct NixInt { + inner: NonNull, + value: i64, +} + +impl Display for NixInt { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "") + } +} + +impl Debug for NixInt { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "NixInt(${})", self.value()) + } +} + +impl AsInnerPtr for NixInt { + #[inline] + unsafe fn as_ptr(&self) -> *mut sys::nix_value { + self.inner.as_ptr() + } + + #[inline] + unsafe fn as_ref(&self) -> &sys::nix_value { + unsafe { self.inner.as_ref() } + } + + #[inline] + unsafe fn as_mut(&mut self) -> &mut sys::nix_value { + unsafe { self.inner.as_mut() } + } +} + +impl NixValue for NixInt { + #[inline] + fn get_enum_id(&self) -> sys::ValueType { + sys::ValueType_NIX_TYPE_INT + } + + fn new(inner: NonNull) -> Self { + let value = wrap::nix_fn!(|ctx: &ErrorContext| unsafe { + sys::nix_get_int(ctx.as_ptr(), inner.as_ptr()) + }) + .unwrap_or_else(|err| { + panic_issue_call_failed!("`sys::nix_get_int` failed for valid `NixInt` ({})", err) + }); + + Self { inner, value } + } +} + +impl NixInt { + /// Returns a shared reference to the underlying value. + /// + #[inline] + fn value(&self) -> &i64 { + &self.value + } +} diff --git a/nixide/src/expr/values/list.rs b/nixide/src/expr/values/list.rs new file mode 100644 index 0000000..e69de29 diff --git a/nixide/src/expr/values/mod.rs b/nixide/src/expr/values/mod.rs new file mode 100644 index 0000000..bc503d1 --- /dev/null +++ b/nixide/src/expr/values/mod.rs @@ -0,0 +1,29 @@ +mod attrs; +mod bool; +mod external; +mod float; +mod function; +mod int; +mod list; +mod null; +mod path; +mod string; +mod thunk; + +pub use bool::NixBool; +pub use float::NixFloat; +pub use int::NixInt; +pub use string::NixString; + +use std::fmt::{Debug, Display}; +use std::ptr::NonNull; + +use crate::sys; +use crate::util::wrappers::AsInnerPtr; + +pub trait NixValue: Display + Debug + AsInnerPtr { + /// TODO + fn get_enum_id(&self) -> sys::ValueType; + + fn new(inner: NonNull) -> Self; +} diff --git a/nixide/src/expr/values/null.rs b/nixide/src/expr/values/null.rs new file mode 100644 index 0000000..e69de29 diff --git a/nixide/src/expr/values/path.rs b/nixide/src/expr/values/path.rs new file mode 100644 index 0000000..e69de29 diff --git a/nixide/src/expr/values/string.rs b/nixide/src/expr/values/string.rs new file mode 100644 index 0000000..78fc272 --- /dev/null +++ b/nixide/src/expr/values/string.rs @@ -0,0 +1,75 @@ +use std::cell::LazyCell; +use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; +use std::ptr::NonNull; + +use super::NixValue; +use crate::expr::RealisedString; +use crate::util::panic_issue_call_failed; +use crate::util::wrap; +use crate::util::wrappers::AsInnerPtr; +use crate::{sys, NixideResult}; + +pub struct NixString { + inner: NonNull, + value: String, +} + +impl Display for NixString { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "") + } +} + +impl Debug for NixString { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "NixString(\"${}\")", self.value()) + } +} + +impl AsInnerPtr for NixString { + #[inline] + unsafe fn as_ptr(&self) -> *mut sys::nix_value { + self.inner.as_ptr() + } + + #[inline] + unsafe fn as_ref(&self) -> &sys::nix_value { + unsafe { self.inner.as_ref() } + } + + #[inline] + unsafe fn as_mut(&mut self) -> &mut sys::nix_value { + unsafe { self.inner.as_mut() } + } +} + +impl NixValue for NixString { + #[inline] + fn get_enum_id(&self) -> sys::ValueType { + sys::ValueType_NIX_TYPE_STRING + } + + fn new(inner: NonNull) -> Self { + // wrap::nix_fn!(|ctx: &ErrorContext| unsafe { + // sys::nix_get_int(ctx.as_ptr(), inner.as_ptr()) + // }) + // .unwrap_or_else(|err| { + // panic_issue_call_failed!( + // "`sys::nix_get_int` failed for valid `NixString` ({})", + // err + // ) + // }) + // }; + + Self { inner, value } + } +} + +impl NixString { + /// Returns a shared reference to the underlying value. + /// + #[inline] + fn value(&self) -> &String { + &self.value + } +} diff --git a/nixide/src/expr/values/thunk.rs b/nixide/src/expr/values/thunk.rs new file mode 100644 index 0000000..e69de29 diff --git a/nixide/src/expr/valuetype.rs b/nixide/src/expr/valuetype.rs index 62ed8cd..ad7b194 100644 --- a/nixide/src/expr/valuetype.rs +++ b/nixide/src/expr/valuetype.rs @@ -1,6 +1,6 @@ use std::fmt::{Display, Formatter, Result as FmtResult}; -use crate::{sys, util::wrappers::FromC}; +use crate::sys; /// Nix value types. #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -29,8 +29,8 @@ pub enum ValueType { External, } -impl FromC for ValueType { - unsafe fn from_c(value_type: sys::ValueType) -> Self { +impl From for ValueType { + fn from(value_type: sys::ValueType) -> Self { match value_type { sys::ValueType_NIX_TYPE_THUNK => ValueType::Thunk, sys::ValueType_NIX_TYPE_INT => ValueType::Int, diff --git a/nixide/src/lib.rs b/nixide/src/lib.rs index 5b363b4..c8f3b58 100644 --- a/nixide/src/lib.rs +++ b/nixide/src/lib.rs @@ -5,7 +5,7 @@ pub extern crate libc; pub extern crate nixide_sys as sys; pub(crate) mod errors; -// mod expr; +mod expr; // mod flake; mod stdext; mod store; @@ -14,7 +14,7 @@ mod verbosity; mod version; pub use errors::{NixError, NixideError, NixideResult}; -// pub use expr::{EvalState, EvalStateBuilder, Value, ValueType}; +pub use expr::{EvalState, EvalStateBuilder, Value, ValueType}; pub use store::{Store, StorePath}; pub use verbosity::NixVerbosity; pub use version::NixVersion; diff --git a/nixide/src/store/mod.rs b/nixide/src/store/mod.rs index 0d52edb..4bca2a8 100644 --- a/nixide/src/store/mod.rs +++ b/nixide/src/store/mod.rs @@ -36,9 +36,20 @@ pub struct Store { } impl AsInnerPtr for Store { + #[inline] unsafe fn as_ptr(&self) -> *mut sys::Store { self.inner.as_ptr() } + + #[inline] + unsafe fn as_ref(&self) -> &sys::Store { + unsafe { self.inner.as_ref() } + } + + #[inline] + unsafe fn as_mut(&mut self) -> &mut sys::Store { + unsafe { self.inner.as_mut() } + } } impl Store { diff --git a/nixide/src/util/lazy_array.rs b/nixide/src/util/lazy_array.rs new file mode 100644 index 0000000..2808314 --- /dev/null +++ b/nixide/src/util/lazy_array.rs @@ -0,0 +1,79 @@ +use std::cell::RefCell; +use std::rc::Rc; + +#[derive(Debug)] +pub struct LazyArray +where + F: Fn(usize) -> T, +{ + inner: Rc>>>, + size: usize, + delegate: F, +} + +impl LazyArray +where + F: Fn(usize) -> T, +{ + pub fn new(size: usize, delegate: F) -> LazyArray { + let mut vec = Vec::with_capacity(size); + for _ in 0..size { + vec.push(None); + } + + LazyArray { + inner: Rc::new(RefCell::new(vec)), + size, + delegate, + } + } + + /// Returns `None` if `index < self.size` otherwise always succeeds + /// (unless of course the callback you supply panics). + /// + // pub fn get(&mut self, index: usize) -> Option<&T> { + // // let x = self.inner.get(index).copied().and_then(|value| match value { + // // Some(value) => Some(value), + // // None => { + // // // store the value first + // // let value = (self.delegate)(index); + // // self.inner[index] = Some(value); + + // // // now get a reference to it + // // if let Some(v) = &self.inner[index] { + // // return Some(v); + // // } + // // None + // // } + // // }) + // match self.inner.clone().borrow().get(index) { + // Some(Some(value)) => Some(value), + // Some(None) => { + // let mut inner = self.inner.clone().borrow_mut(); + // // store the value first + // inner[index] = Some((self.delegate)(index)); + + // // now get a reference to it + // inner[index].as_ref() + // } + // None => None, + // } + // } + pub fn get(&mut self, index: usize) -> Option> { + if index >= self.size { + return None; + } + + // let inner = self.inner.borrow(); + if let Some(value) = self.inner.borrow()[index].as_ref() { + return Some(Rc::new(value)); + } + + // drop(inner); // explicitly drop the borrow + + let value = (self.delegate)(index); + self.inner.borrow_mut()[index] = Some(value); + + Some(Rc::new(self.inner.borrow()[index].unwrap())) + } +} diff --git a/nixide/src/util/mod.rs b/nixide/src/util/mod.rs index 2302b41..f9ec55d 100644 --- a/nixide/src/util/mod.rs +++ b/nixide/src/util/mod.rs @@ -1,38 +1,8 @@ #[macro_use] pub mod panic; +mod lazy_array; pub(crate) mod wrap; pub mod wrappers; -pub(crate) use panic::*; - -// use crate::NixideError; - -// pub trait AsErr { -// fn as_err(self) -> Result<(), T>; -// } - -// impl AsErr for Option { -// fn as_err(self) -> Result<(), NixideError> { -// match self { -// Some(err) => Err(err), -// None => Ok(()), -// } -// } -// } - -// pub trait AsInnerPtr { -// /// Get a pointer to the underlying (`inner`) `libnix` C struct. -// /// -// /// # Safety -// /// -// /// Although this function isn't inherently `unsafe`, it is -// /// marked as such intentionally to force calls to be wrapped -// /// in `unsafe` blocks for clarity. -// unsafe fn as_ptr(&self) -> *mut T; -// } - -// pub trait FromC { -// /// Creates a new instance of [Self] from the underlying -// /// libnix C type [T]. -// unsafe fn from_c(value: T) -> Self; -// } +pub(crate) use lazy_array::LazyArray; +pub(crate) use panic::{panic_issue, panic_issue_call_failed}; diff --git a/nixide/src/util/wrappers.rs b/nixide/src/util/wrappers.rs index ab43cce..6fa0378 100644 --- a/nixide/src/util/wrappers.rs +++ b/nixide/src/util/wrappers.rs @@ -1,26 +1,5 @@ -// use crate::NixideError; -// -// pub trait AsErr { -// fn as_err(self) -> Result<(), T>; -// } -// -// impl AsErr for Option { -// fn as_err(self) -> Result<(), NixideError> { -// match self { -// Some(err) => Err(err), -// None => Ok(()), -// } -// } -// } -// -// pub trait FromC { -// /// Creates a new instance of [Self] from the underlying -// /// libnix C type [T]. -// unsafe fn from_c(value: T) -> Self; -// } - pub trait AsInnerPtr { - /// Get a pointer to the underlying (`inner`) `libnix` C struct. + /// Acquires the underlying pointer to the inner `libnix` C struct. /// /// # Safety /// @@ -28,4 +7,26 @@ pub trait AsInnerPtr { /// marked as such intentionally to force calls to be wrapped /// in `unsafe` blocks for clarity. unsafe fn as_ptr(&self) -> *mut T; + + /// Returns a shared reference to the inner `libnix` C struct. + /// + /// For the mutable counterpart see [AsInnerPtr::as_mut]. + /// + /// # Safety + /// + /// Although this function isn't inherently `unsafe`, it is + /// marked as such intentionally to force calls to be wrapped + /// in `unsafe` blocks for clarity. + unsafe fn as_ref(&self) -> &T; + + /// Returns a unique reference to the inner `libnix` C struct. + /// + /// For the shared counterpart see [AsInnerPtr::as_ref]. + /// + /// # Safety + /// + /// Although this function isn't inherently `unsafe`, it is + /// marked as such intentionally to force calls to be wrapped + /// in `unsafe` blocks for clarity. + unsafe fn as_mut(&mut self) -> &mut T; }