From 504e42b559d74b013e95d93f5fc3868f756fd46b Mon Sep 17 00:00:00 2001 From: zimbatm Date: Sat, 22 Jul 2023 11:14:51 +0200 Subject: [PATCH] writers: introduce data writers Make it easy to write structured data back to disk. --- pkgs/build-support/writers/data.nix | 80 ++++++++++++++++++++++++++ pkgs/build-support/writers/default.nix | 12 +++- pkgs/build-support/writers/test.nix | 38 +++++++++++- 3 files changed, 126 insertions(+), 4 deletions(-) create mode 100644 pkgs/build-support/writers/data.nix diff --git a/pkgs/build-support/writers/data.nix b/pkgs/build-support/writers/data.nix new file mode 100644 index 0000000000000..a080f8c29d0e0 --- /dev/null +++ b/pkgs/build-support/writers/data.nix @@ -0,0 +1,80 @@ +{ lib, runCommandNoCC, dasel }: +let + daselBin = lib.getExe dasel; + + inherit (lib) + last + optionalString + types + ; +in +rec { + # Creates a transformer function that writes input data to disk, transformed + # by both the `input` and `output` arguments. + # + # Type: makeDataWriter :: input -> output -> nameOrPath -> data -> (any -> string) -> string -> string -> any -> derivation + # + # input :: T -> string: function that takes the nix data and returns a string + # output :: string: script that takes the $inputFile and write the result into $out + # nameOrPath :: string: if the name contains a / the files gets written to a sub-folder of $out. The derivation name is the basename of this argument. + # data :: T: the data that will be converted. + # + # Example: + # writeJSON = makeDataWriter { input = builtins.toJSON; output = "cp $inputPath $out"; }; + # myConfig = writeJSON "config.json" { hello = "world"; } + # + makeDataWriter = { input ? lib.id, output ? "cp $inputPath $out" }: nameOrPath: data: + assert lib.or (types.path.check nameOrPath) (builtins.match "([0-9A-Za-z._])[0-9A-Za-z._-]*" nameOrPath != null); + let + name = last (builtins.split "/" nameOrPath); + in + runCommandNoCC name + { + input = input data; + passAsFile = [ "input" ]; + } '' + ${output} + + ${optionalString (types.path.check nameOrPath) '' + mv $out tmp + mkdir -p $out/$(dirname "${nameOrPath}") + mv tmp $out/${nameOrPath} + ''} + ''; + + # Writes the content to text. + # + # Example: + # writeText "filename.txt" "file content" + writeText = makeDataWriter { + input = toString; + output = "cp $inputPath $out"; + }; + + # Writes the content to a JSON file. + # + # Example: + # writeJSON "data.json" { hello = "world"; } + writeJSON = makeDataWriter { + input = builtins.toJSON; + output = "${daselBin} -f $inputPath -r json -w json > $out"; + }; + + # Writes the content to a TOML file. + # + # Example: + # writeTOML "data.toml" { hello = "world"; } + writeTOML = makeDataWriter { + input = builtins.toJSON; + output = "${daselBin} -f $inputPath -r json -w toml > $out"; + }; + + # Writes the content to a YAML file. + # + # Example: + # writeYAML "data.yaml" { hello = "world"; } + writeYAML = makeDataWriter { + input = builtins.toJSON; + output = "${daselBin} -f $inputPath -r json -w yaml > $out"; + }; +} diff --git a/pkgs/build-support/writers/default.nix b/pkgs/build-support/writers/default.nix index c26ada8bc5718..5ef5794221953 100644 --- a/pkgs/build-support/writers/default.nix +++ b/pkgs/build-support/writers/default.nix @@ -3,8 +3,16 @@ let aliases = if config.allowAliases then (import ./aliases.nix lib) else prev: {}; - scriptWriters = import ./scripts.nix { inherit pkgs lib; }; + # Writers for JSON-like data structures + dataWriters = import ./data.nix { + inherit lib; inherit (pkgs) runCommandNoCC dasel; + }; - writers = scriptWriters; + # Writers for scripts + scriptWriters = import ./scripts.nix { + inherit lib pkgs; + }; + + writers = scriptWriters // dataWriters; in writers // (aliases writers) diff --git a/pkgs/build-support/writers/test.nix b/pkgs/build-support/writers/test.nix index 9484f8bbe31b5..2411f8c03a70b 100644 --- a/pkgs/build-support/writers/test.nix +++ b/pkgs/build-support/writers/test.nix @@ -14,7 +14,7 @@ with writers; let expectSuccess = test: runCommand "run-${test.name}" {} '' - if test "$(${test})" != "success"; then + if [[ "$(${test})" != success ]]; then echo 'test ${test.name} failed' exit 1 fi @@ -24,11 +24,24 @@ let expectSuccessBin = test: runCommand "run-${test.name}" {} '' - if test "$(${lib.getExe test})" != "success"; then + if [[ "$(${lib.getExe test})" != success ]]; then echo 'test ${test.name} failed' exit 1 fi + touch $out + ''; + + expectDataEqual = { file, expected }: + let + expectedFile = writeText "${file.name}-expected" expected; + in + runCommand "run-${file.name}" {} '' + if ! diff -u ${file} ${expectedFile}; then + echo 'test ${file.name} failed' + exit 1 + fi + touch $out ''; in @@ -235,4 +248,25 @@ lib.recurseIntoAttrs { _ -> print "fail" '')); }; + + data = { + json = expectDataEqual { + file = writeJSON "data.json" { hello = "world"; }; + expected = '' + { + "hello": "world" + } + ''; + }; + + toml = expectDataEqual { + file = writeTOML "data.toml" { hello = "world"; }; + expected = "hello = 'world'\n"; + }; + + yaml = expectDataEqual { + file = writeYAML "data.yaml" { hello = "world"; }; + expected = "hello: world\n"; + }; + }; }