diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..84a928d --- /dev/null +++ b/.envrc @@ -0,0 +1,12 @@ +#! /bin/sh + +# reload when these files change +watch_file devshell/flake.nix +watch_file devshell/flake.lock + +{ + # shell gc root dir + mkdir -p "$(direnv_layout_dir)" + eval "$(nix print-dev-env ./devshell\#default --no-update-lock-file --no-write-lock-file --profile $(direnv_layout_dir)/flake-profile)" +} + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..29f4b93 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/.direnv +/.data +result + diff --git a/devshell/flake.lock b/devshell/flake.lock new file mode 100644 index 0000000..003c7c7 --- /dev/null +++ b/devshell/flake.lock @@ -0,0 +1,203 @@ +{ + "nodes": { + "alejandra": { + "inputs": { + "flakeCompat": "flakeCompat", + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1646937818, + "narHash": "sha256-vkFKYnSmhPPXtc3AH7iRtqRRqxhj0o5WySqPT+klDWU=", + "owner": "kamadorueda", + "repo": "alejandra", + "rev": "e00f984b95e696a0878cd231e937f852eb79532c", + "type": "github" + }, + "original": { + "owner": "kamadorueda", + "repo": "alejandra", + "type": "github" + } + }, + "devshell": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1646667754, + "narHash": "sha256-LahZHvCC3UVzGQ55iWDRZkuDssXl1rYgqgScrPV9S38=", + "owner": "numtide", + "repo": "devshell", + "rev": "59fbe1dfc0de8c3332957c16998a7d16dff365d8", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "devshell", + "type": "github" + } + }, + "flake-utils": { + "locked": { + "lastModified": 1642700792, + "narHash": "sha256-XqHrk7hFb+zBvRg6Ghl+AZDq03ov6OshJLiSWOoX5es=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "846b2ae0fc4cc943637d3d1def4454213e203cba", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "locked": { + "lastModified": 1644229661, + "narHash": "sha256-1YdnJAsNy69bpcjuoKdOYQX0YxZBiCYZo4Twxerqv7k=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "3cecb5b042f7f209c56ffd8371b2711a290ec797", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flakeCompat": { + "flake": false, + "locked": { + "lastModified": 1641205782, + "narHash": "sha256-4jY7RCWUoZ9cKD8co0/4tFARpWB+57+r1bLLvXNJliY=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "b7547d3eed6f32d06102ead8991ec52ab0a4f1a7", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1646506091, + "narHash": "sha256-sWNAJE2m+HOh1jtXlHcnhxsj6/sXrHgbqVNcVRlveK4=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "3e644bd62489b516292c816f70bf0052c693b3c7", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1643381941, + "narHash": "sha256-pHTwvnN4tTsEKkWlXQ8JMY423epos8wUOhthpwJjtpc=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "5efc8ca954272c4376ac929f4c5ffefcc20551d5", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_3": { + "locked": { + "lastModified": 1646506091, + "narHash": "sha256-sWNAJE2m+HOh1jtXlHcnhxsj6/sXrHgbqVNcVRlveK4=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "3e644bd62489b516292c816f70bf0052c693b3c7", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_4": { + "locked": { + "lastModified": 1646506091, + "narHash": "sha256-sWNAJE2m+HOh1jtXlHcnhxsj6/sXrHgbqVNcVRlveK4=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "3e644bd62489b516292c816f70bf0052c693b3c7", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "alejandra": "alejandra", + "devshell": "devshell", + "flake-utils": "flake-utils_2", + "nixpkgs": "nixpkgs_3", + "std": "std" + } + }, + "std": { + "inputs": { + "nixpkgs": "nixpkgs_4", + "yants": "yants" + }, + "locked": { + "lastModified": 1646928713, + "narHash": "sha256-SdN4Lxkf8PLMLt7un//8c4xIWFCfTgBZoktx8JHpbwk=", + "owner": "divnix", + "repo": "std", + "rev": "f6a3f5920e4e7ab5e4a7d83f2c0f03d92265c19e", + "type": "github" + }, + "original": { + "owner": "divnix", + "repo": "std", + "type": "github" + } + }, + "yants": { + "inputs": { + "nixpkgs": [ + "std", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1645126146, + "narHash": "sha256-XQ1eg4gzXoc7Tl8iXak1uCt3KnsTyxqPtLE+vOoDnrQ=", + "owner": "divnix", + "repo": "yants", + "rev": "77df2be1b3cce9f571c6cf451f786b266a6869cc", + "type": "github" + }, + "original": { + "owner": "divnix", + "repo": "yants", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/devshell/flake.nix b/devshell/flake.nix new file mode 100644 index 0000000..c31c52e --- /dev/null +++ b/devshell/flake.nix @@ -0,0 +1,39 @@ +{ + description = "Bitte Cells development shell"; + inputs.nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; + inputs.devshell.url = "github:numtide/devshell"; + inputs.alejandra.url = "github:kamadorueda/alejandra"; + inputs.alejandra.inputs.treefmt.url = "github:divnix/blank"; + inputs.std.url = "github:divnix/std"; + inputs.flake-utils.url = "github:numtide/flake-utils"; + outputs = inputs: + inputs.flake-utils.lib.eachSystem ["x86_64-linux" "x86_64-darwin"] ( + system: let + inherit + (inputs.std.deSystemize system inputs) + main + devshell + nixpkgs + alejandra + treefmt + ; + in { + devShells.default = devshell.legacyPackages.mkShell { + name = "Data Merge"; + commands = [{package = nixpkgs.legacyPackages.treefmt;}]; + packages = [ + alejandra.defaultPackage + nixpkgs.legacyPackages.shfmt + nixpkgs.legacyPackages.nodePackages.prettier + nixpkgs.legacyPackages.nodePackages.prettier-plugin-toml + nixpkgs.legacyPackages.python3Packages.black + ]; + devshell.startup.nodejs-setuphook = nixpkgs.lib.stringsWithDeps.noDepEntry '' + export NODE_PATH=${ + nixpkgs.legacyPackages.nodePackages.prettier-plugin-toml + }/lib/node_modules:$NODE_PATH + ''; + }; + } + ); +} diff --git a/flake.nix b/flake.nix index 9648d22..6abcf26 100644 --- a/flake.nix +++ b/flake.nix @@ -1,13 +1,16 @@ -{ description = "A mini merge DSL for data overlays"; +{ + description = "A mini merge DSL for data overlays"; inputs.nixlib.url = "github:nix-community/nixpkgs.lib"; - outputs = { self, nixlib }: let - + outputs = { + self, + nixlib, + }: let # Incrementality of the Data Spine # -------------------------------- # To reduce mental complexity in chained merges, # we must ensure that the data spine of the left # hand side is not _destructively_ modified. - # + # # This means, that like the keys of an attribute # set cannot be removed through a merge operation, # we also must ensure that no array element can @@ -18,19 +21,17 @@ # And while individual simple type merges necessarily # destroy information, the spine itself shall not # allowed to be transformed itself destructively. - - mergeAt = here: lhs: rhs: - let + mergeAt = here: lhs: rhs: let inherit (builtins) isAttrs head tail typeOf concatStringsSep tryEval; inherit (nixlib.lib) zipAttrsWith isList isFunction getAttrFromPath; f = attrPath: - zipAttrsWith (n: values: - let - here' = attrPath ++ [ n ]; + zipAttrsWith ( + n: values: let + here' = attrPath ++ [n]; rhs' = head values; lhs' = head (tail values); - isSingleton = tail values == [ ]; + isSingleton = tail values == []; lhsFilePos = let lhsPos = builtins.unsafeGetAttrPos n (getAttrFromPath attrPath lhs); in "${lhsPos.file}:${toString lhsPos.line}:${toString lhsPos.column}"; @@ -38,52 +39,61 @@ rhsPos = builtins.unsafeGetAttrPos n (getAttrFromPath attrPath rhs); in "${rhsPos.file}:${toString rhsPos.line}:${toString rhsPos.column}"; in - if (isSingleton && isFunction (head values)) then abort '' - - a fresh right-hand-side cannot be an array merge function - at '${concatStringsSep "." here'}': - - rhs: ${typeOf rhs'} @ ${rhsFilePos} - '' - else if isSingleton then head values - else if !(isAttrs lhs' && isAttrs rhs') - then - if (typeOf lhs') != (typeOf rhs') && !(isList lhs' && isFunction rhs') - then abort '' + if (isSingleton && isFunction (head values)) + then + abort '' - rigt-hand-side must be of the same type as left-hand-side - at '${concatStringsSep "." here'}': - - lhs: ${typeOf lhs'} @ ${lhsFilePos} - - rhs: ${typeOf rhs'} @ ${rhsFilePos} - '' - else if isList lhs' && isList rhs' - then abort '' + a fresh right-hand-side cannot be an array merge function + at '${concatStringsSep "." here'}': + - rhs: ${typeOf rhs'} @ ${rhsFilePos} + '' + else if isSingleton + then head values + else if !(isAttrs lhs' && isAttrs rhs') + then + if (typeOf lhs') != (typeOf rhs') && !(isList lhs' && isFunction rhs') + then + abort '' - rigt-hand-side list is not allowed to override left-hand-side list, - this would break incrementality of the data spine. Use one of the array - merge functions instead at '${concatStringsSep "." here'}': - - lhs: ${typeOf lhs'} @ ${lhsFilePos} - - rhs: ${typeOf rhs'} @ ${rhsFilePos} + rigt-hand-side must be of the same type as left-hand-side + at '${concatStringsSep "." here'}': + - lhs: ${typeOf lhs'} @ ${lhsFilePos} + - rhs: ${typeOf rhs'} @ ${rhsFilePos} + '' + else if isList lhs' && isList rhs' + then + abort '' - Available array merge functions: - - data-merge.update [ idx ... ] [ v ... ] - - data-merge.append [ v ] - '' - # array function merge - else if isList lhs' && isFunction rhs' then let - ex = tryEval (rhs' lhs' here'); - in if ex.success then ex.value else abort '' + rigt-hand-side list is not allowed to override left-hand-side list, + this would break incrementality of the data spine. Use one of the array + merge functions instead at '${concatStringsSep "." here'}': + - lhs: ${typeOf lhs'} @ ${lhsFilePos} + - rhs: ${typeOf rhs'} @ ${rhsFilePos} - Array merge function error (see trace above the error line for details) on the right-hand-side: - - rhs: ${typeOf rhs'} @ ${rhsFilePos} - '' - else rhs' - else f here' values - ); - in f here [ rhs lhs ]; + Available array merge functions: + - data-merge.update [ idx ... ] [ v ... ] + - data-merge.append [ v ] + '' + # array function merge + else if isList lhs' && isFunction rhs' + then let + ex = tryEval (rhs' lhs' here'); + in + if ex.success + then ex.value + else + abort '' + Array merge function error (see trace above the error line for details) on the right-hand-side: + - rhs: ${typeOf rhs'} @ ${rhsFilePos} + '' + else rhs' + else f here' values + ); + in + f here [rhs lhs]; in { - - merge = mergeAt [ ]; + merge = mergeAt []; append = new: orig: _: let inherit (builtins) isList typeOf concatStringsSep; @@ -91,7 +101,7 @@ in assert assertMsg (isList new) '' APPENDING ARRAY MERGE: argument must be a list, got: ${typeOf new}''; - orig ++ new; + orig ++ new; update = indices: updates: orig: here: let inherit (builtins) isList all isInt length typeOf listToAttrs elemAt hasAttr concatStringsSep; @@ -104,23 +114,30 @@ assert assertMsg (all (i: isInt i) indices) '' UPDATING ARRAY MERGE: first argument must be a list of indices (integers) of items to update in the left-hand-side list, got: ${traceSeqN 1 indices "(see trace above)"}''; assert assertMsg (length indices == length updates) '' - UPDATING ARRAY MERGE: for each index there must be one corresponding update value, got: ${traceSeqN 1 indices "(see first trace above)"} indices & ${traceSeqN 1 updates "(see second trace above)"} updates''; - let + UPDATING ARRAY MERGE: for each index there must be one corresponding update value, got: ${traceSeqN 1 indices "(see first trace above)"} indices & ${traceSeqN 1 updates "(see second trace above)"} updates''; let updated = listToAttrs ( - zipListsWith (idx: upd: - { + zipListsWith ( + idx: upd: { name = toString idx; - value = (mergeAt here - { mergedListItem = (elemAt orig idx); } - { mergedListItem = upd; } - ).mergedListItem; + value = + ( + mergeAt here + {mergedListItem = elemAt orig idx;} + {mergedListItem = upd;} + ) + .mergedListItem; } - ) indices updates); - in imap0 (i: v: - if hasAttr "${toString i}" updated - then updated.${toString i} - else elemAt orig i - ) orig; + ) + indices + updates + ); + in + imap0 ( + i: v: + if hasAttr "${toString i}" updated + then updated.${toString i} + else elemAt orig i + ) + orig; }; } - diff --git a/treefmt.toml b/treefmt.toml new file mode 100644 index 0000000..b62d504 --- /dev/null +++ b/treefmt.toml @@ -0,0 +1,25 @@ +# One CLI to format the code tree - https://github.com/numtide/treefmt +[formatter.nix] +command = "alejandra" +includes = ["*.nix"] +excludes = ["./cells/cardano/packages/materialized/*"] + +[formatter.prettier] +command = "prettier" +options = ["--plugin", "prettier-plugin-toml", "--write"] +includes = ["*.md", "*.yaml", "*.toml"] + +[formatter.black] +command = "black" +includes = ["*.py"] + +[formatter.shell] +command = "shfmt" +options = [ + "-i", + "2", # indent 2 + "-s", # simplify the code + "-w", # write back to the file + +] +includes = ["*.sh"]