Skip to content

Commit 13f146c

Browse files
committed
feat(sequence): add transformAll for parallel error accumulation
1 parent 9dce325 commit 13f146c

3 files changed

Lines changed: 81 additions & 1 deletion

File tree

default.nix

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ let
5656
sequence
5757
collect
5858
transform
59+
defaultTransformError
60+
transformAllWith
61+
transformAll
5962
;
6063

6164
inherit (applyLib) apply;

nix/sequence.nix

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,52 @@ let
7171
if r ? left then r else either.right (toAttrs r.right);
7272
set = _: b: b;
7373
};
74+
defaultTransformError = field: got: { inherit field got; };
75+
76+
transformAllWith =
77+
errorFn: validators:
78+
let
79+
fieldNames = builtins.attrNames validators;
80+
81+
validateField =
82+
name: s:
83+
let
84+
attrResult = (attr name).get s;
85+
in
86+
if attrResult ? left then
87+
either.left (errorFn name attrResult.left)
88+
else
89+
let
90+
valResult = (validators.${name}).get attrResult.right;
91+
in
92+
if valResult ? left then either.left (errorFn name valResult.left) else either.right valResult.right;
93+
94+
collectResults =
95+
s:
96+
let
97+
results = map (name: validateField name s) fieldNames;
98+
errors = builtins.filter (r: r ? left) results;
99+
successes = builtins.filter (r: r ? right) results;
100+
in
101+
if errors != [ ] then
102+
either.left (map (r: r.left) errors)
103+
else
104+
either.right (
105+
builtins.listToAttrs (
106+
builtins.genList (i: {
107+
name = builtins.elemAt fieldNames i;
108+
value = (builtins.elemAt successes i).right;
109+
}) (builtins.length fieldNames)
110+
)
111+
);
112+
in
113+
{
114+
get = collectResults;
115+
set = _: b: b;
116+
};
117+
118+
transformAll = transformAllWith defaultTransformError;
74119
in
75120
{
76-
inherit sequence collect transform;
121+
inherit sequence collect transform defaultTransformError transformAllWith transformAll;
77122
}

tests.nix

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -934,6 +934,38 @@ in
934934
expected = bend.left "hi";
935935
};
936936

937+
transformAll."test-valid-input-returns-right-attrset" = {
938+
expr = (bend.transformAll { name = bend.str; age = bend.int; }).get { name = "alice"; age = 30; };
939+
expected = bend.right { name = "alice"; age = 30; };
940+
};
941+
transformAll."test-single-invalid-field-returns-left-list" = {
942+
expr = (bend.transformAll { name = bend.str; age = bend.int; }).get { name = "alice"; age = "thirty"; };
943+
expected = bend.left [ { field = "age"; got = "thirty"; } ];
944+
};
945+
transformAll."test-all-invalid-fields-accumulated" = {
946+
# attrNames sorts alphabetically: age < name → age error first
947+
expr = (bend.transformAll { name = bend.str; age = bend.int; }).get { name = 1; age = "thirty"; };
948+
expected = bend.left [
949+
{ field = "age"; got = "thirty"; }
950+
{ field = "name"; got = 1; }
951+
];
952+
};
953+
transformAll."test-missing-field-counted-as-error" = {
954+
expr = (bend.transformAll { name = bend.str; age = bend.int; }).get { name = "alice"; };
955+
expected = bend.left [ { field = "age"; got = { name = "alice"; }; } ];
956+
};
957+
transformAll."test-transformAllWith-custom-error-shape" = {
958+
expr = (bend.transformAllWith (f: g: "${f}:${builtins.typeOf g}") {
959+
name = bend.str;
960+
age = bend.int;
961+
}).get { name = "alice"; age = "thirty"; };
962+
expected = bend.left [ "age:string" ];
963+
};
964+
transformAll."test-defaultTransformError-shape" = {
965+
expr = bend.defaultTransformError "age" 30;
966+
expected = { field = "age"; got = 30; };
967+
};
968+
937969
index."test-index-zero-gets-first-element" = {
938970
expr =
939971
let

0 commit comments

Comments
 (0)