diff --git a/base/map/map.go b/base/map/map.go index e124a0f..92e9f99 100644 --- a/base/map/map.go +++ b/base/map/map.go @@ -31,6 +31,16 @@ func HasKey[K comparable, V any, M ~map[K]V](m M, keys ...K) (bool, []base.Error return true, nil } +// NotHaveKey checks map not contain one or multiple keys +func NotHaveKey[K comparable, V any, M ~map[K]V](m M, keys ...K) (bool, []base.ErrorParam) { + for _, k := range keys { + if _, exists := m[k]; exists { + return false, []base.ErrorParam{{Key: kItemKey, Value: k}} + } + } + return true, nil +} + // KeyIn checks map keys must be in a list func KeyIn[K comparable, V any, M ~map[K]V](m M, vals ...K) (bool, []base.ErrorParam) { keys := gofn.MapKeys(m) diff --git a/base/map/map_test.go b/base/map/map_test.go index 444e6c4..cc4b497 100644 --- a/base/map/map_test.go +++ b/base/map/map_test.go @@ -31,6 +31,16 @@ func Test_HasKey(t *testing.T) { assert.True(t, params[0].Key == kItemKey && params[0].Value == 3) } +func Test_NotHaveKey(t *testing.T) { + assert.True(t, gofn.Head(NotHaveKey[int, int](map[int]int(nil)))) + assert.True(t, gofn.Head(NotHaveKey(map[int]int{}))) + assert.True(t, gofn.Head(NotHaveKey(map[int]int{1: 1, 2: 2}, 0, 3, 4))) + + ok, params := NotHaveKey(map[int]int{1: 1, 2: 2}, 3, 1) + assert.False(t, ok) + assert.True(t, params[0].Key == kItemKey && params[0].Value == 1) +} + func Test_KeyIn(t *testing.T) { assert.True(t, gofn.Head(KeyIn[int, int](map[int]int(nil), 0, 1, 2))) assert.True(t, gofn.Head(KeyIn[int, int](map[int]int(nil)))) diff --git a/internal/templates/en/templates.go b/internal/templates/en/templates.go index dc11eff..866c5c6 100644 --- a/internal/templates/en/templates.go +++ b/internal/templates/en/templates.go @@ -138,6 +138,7 @@ var ( // Map "map_len": `{{.Field}}: number of items must be in range {{.Min}} to {{.Max}}`, "map_has_key": `{{.Field}}: map must contain keys {{.TargetValue}}`, + "map_not_have_key": `{{.Field}}: map must not contain keys {{.TargetValue}}`, "map_key_in": `{{.Field}}: map keys must be one of {{.TargetValue}}`, "map_key_not_in": `{{.Field}}: map keys must not be one of {{.TargetValue}}`, "map_key_range": `{{.Field}}: map keys must be in range {{.Min}} to {{.Max}}`, diff --git a/validator_map.go b/validator_map.go index 730da21..876f828 100644 --- a/validator_map.go +++ b/validator_map.go @@ -24,6 +24,11 @@ func MapHasKey[K comparable, V any, M ~map[K]V](m M, keys ...K) SingleValidator return call2N[M]("has_key", mapType, "TargetValue", mapFunc.HasKey[K, V, M])(m, keys...) } +// MapNotHaveKey validates the input map must not have the specified keys +func MapNotHaveKey[K comparable, V any, M ~map[K]V](m M, keys ...K) SingleValidator { + return call2N[M]("not_have_key", mapType, "TargetValue", mapFunc.NotHaveKey[K, V, M])(m, keys...) +} + // MapKeyIn validates the input map must have keys in the specified values func MapKeyIn[K comparable, V any, M ~map[K]V](m M, keys ...K) SingleValidator { return call2N[M]("key_in", mapType, "TargetValue", mapFunc.KeyIn[K, V, M])(m, keys...) diff --git a/validator_map_test.go b/validator_map_test.go index c476a8d..e45be62 100644 --- a/validator_map_test.go +++ b/validator_map_test.go @@ -23,6 +23,14 @@ func Test_MapHasKey(t *testing.T) { assert.Equal(t, "has_key", errs[0].Type()) } +func Test_MapNotHaveKey(t *testing.T) { + errs := MapNotHaveKey(map[int]int{3: 3, 2: 2, 1: 1}, 0, 4, 5).Validate(ctxBg) + assert.Equal(t, 0, len(errs)) + + errs = MapNotHaveKey(map[int]int{3: 3, 2: 2, 1: 1}, 3, 1, 2).Validate(ctxBg) + assert.Equal(t, "not_have_key", errs[0].Type()) +} + func Test_MapKeyIn(t *testing.T) { errs := MapKeyIn(map[int]int{3: 3, 2: 2, 1: 1}, 1, 2, 3, 4, 5).Validate(ctxBg) assert.Equal(t, 0, len(errs))