Converting from `types.path` to `types.str`

Is there a way to convert a types.path to a types.str such that the latter is relative to the project root (parent of flake.nix)?

Consider the following:

let x = ./foo;
in pathToString x; # should be "./foo"

The idea is that I would use x in nix build, but pathToString x in nix run and such, so that the latter uses the mutable directory in the project workspace, rather than the read-only one from the nix store.

At the moment I’m violating DRY and forcing the user to specify both, even if they are identical (except for their type):

              path = ./docs;
              pathString = "./docs";

But this is obviously not ideal. What’s the idiomatic way to approach this?

1 Like

It looks like this is a flake so there isn’t any way for you to access or reference mutable files outside of the Nix store in pure Nix code. Flakes enforce purity to encourage reproducible buids. If all you need is to run some sort of task using nix run in the mutable project root, I have used ROOT=$(git rev-parse --show-toplevel) in simple bash scripts to get a reliable reference to the project root, as an example.

I don’t think that’s really possible. Nix upon stumbling onto a path, will convert that to a store path as part of evaluation.

You could probably start with a string “./foo”, then run builtins.toPath if you want to reference the file by a store path. But, as @nrdxp pointed out, if it’s outside of the flake, you will need to run with --impure or something similar to allow for paths outside of the flake.

Not sure about your ultimate goal, magically switching between things at evaluation time depending on what nix subcommand you are using will likely violate flakes’ eval purity. In traditional Nix code you can to an extent exploit the IN_NIX_SHELL environment variable via e.g. lib.inNixShell.

For your desired conversion, however, the following would be idiomatic: You can of course convert paths to strings via toString, yielding an absolute path always (with flakes it may always be in the nix store, I’m not sure about on what occasions it dumps or does not dump its path to store). To get the relative path from the project root, you’d store the path to the project root in some attribute (e.g. in nixpkgs it’s pkgs.path) and then calculate a relative path like this:

  inherit (builtins) stringLength substring;
  toplevel = ./.;
  subpath = ./foo/bar/;

"." + substring (stringLength (toString toplevel)) (-1) (toString subpath)
# => "./foo/bar/"
1 Like

I have a bunch of routines in this file, and a few in the same directory that deal with local and Nix store paths in depth.

I implemented realpath -s --from, and a few files system crawlers. repl has ls implemented with globbing.

Some of those examples may help demystify things for you.

1 Like