Skip to content

Commit dc14628

Browse files
committed
add all_of and any_of to compose multiple predicates
1 parent 004a25c commit dc14628

File tree

3 files changed

+83
-0
lines changed

3 files changed

+83
-0
lines changed

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,26 @@ end
4242

4343
spec.valid(my_predicate, instance_of_type) -- true if condition is valid
4444

45+
-- compose multiple predicates
46+
local bool_or_user = spec.any_of(spec.boolean, user_spec)
47+
48+
spec.valid(bool_or_user, true) -- true
49+
spec.valid(bool_or_user, { name = "Alice", age = 30 }) -- true
50+
spec.valid(bool_or_user, { name = "Bob" }) -- false
51+
52+
local has_name = spec.keys {
53+
name = spec.string,
54+
}
55+
56+
local has_age = spec.keys {
57+
age = spec.number,
58+
}
59+
60+
-- same as the previous user_spec
61+
local is_user = spec.all_of(has_name, has_age)
62+
63+
spec.valid(is_user, { name = "Alice", age = 30 })
64+
4565
-- some useful helper functions...
4666

4767
-- conform returns the value if it conforms to the spec

spec.lua

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,52 @@ function M.some(value)
6666
return value ~= nil
6767
end
6868

69+
---Tests if all of predicates are valid
70+
---@param ... fun(value: any): boolean
71+
---@return fun(value: any): boolean
72+
function M.all_of(...)
73+
local predicates = { ... }
74+
75+
for _, pred in ipairs(predicates) do
76+
if type(pred) ~= "function" then
77+
error "Spec must be a function (e.g. spec.keys for tables)"
78+
end
79+
end
80+
81+
return function(value)
82+
for _, pred in ipairs(predicates) do
83+
if not pred(value) then
84+
return false
85+
end
86+
end
87+
88+
return true
89+
end
90+
end
91+
92+
---Tests if any of predicates is valid
93+
---@param ... fun(value: any): boolean
94+
---@return fun(value: any): boolean
95+
function M.any_of(...)
96+
local predicates = { ... }
97+
98+
for _, pred in ipairs(predicates) do
99+
if type(pred) ~= "function" then
100+
error "Spec must be a function (e.g. spec.keys for tables)"
101+
end
102+
end
103+
104+
return function(value)
105+
for _, pred in ipairs(predicates) do
106+
if pred(value) then
107+
return true
108+
end
109+
end
110+
111+
return false
112+
end
113+
end
114+
69115
---Test if table matches a shape
70116
---@param spec_map table<string, fun(value: any): boolean>
71117
---@return fun(value: any): boolean

spec_test.lua

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,23 @@ describe("spec.lua", function()
4343
assert.True(spec.some "Test")
4444
end)
4545

46+
it("spec.all_of", function()
47+
assert.False(spec.valid(spec.all_of(spec.string, spec.number), nil))
48+
assert.True(spec.valid(spec.all_of(spec.table, spec.some), { 1, 2 }))
49+
assert.True(spec.valid(
50+
spec.all_of(spec.table, function(value)
51+
return #value >= 2
52+
end),
53+
{ 1, 2, 3 }
54+
))
55+
end)
56+
57+
it("spec.any_of", function()
58+
assert.False(spec.valid(spec.any_of(spec.string, spec.number), nil))
59+
assert.True(spec.valid(spec.any_of(spec.table, spec.some), { 1, 2 }))
60+
assert.True(spec.valid(spec.any_of(spec.number, spec.string, spec.table), { 1, 2 }))
61+
end)
62+
4663
it("spec.keys", function()
4764
local user_spec = spec.keys {
4865
name = spec.string,

0 commit comments

Comments
 (0)