Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(string): impl variant validator and ensure backward compatibility #49

Merged
merged 2 commits into from
Jun 30, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 33 additions & 21 deletions string.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,43 @@ package goval
import (
"context"
"fmt"
"github.com/pkg-id/goval/funcs"
"strings"

"github.com/pkg-id/goval/funcs"
)

// StringValidator is a FunctionValidator that validates string.
type StringValidator FunctionValidator[string]
// For backward compatibility.
type StringValidator = SVV[string]

// String returns a StringValidator with no rules.
// For backward compatibility.
func String() StringValidator {
return NopFunctionValidator[string]
return StringVariant[string]()
}

// SVV (String Variant Validator) is a FunctionValidator that validates string
// variants.
type SVV[T ~string] FunctionValidator[T]

// StringVariant returns a SVV with no rules.
func StringVariant[T ~string]() SVV[T] {
return NopFunctionValidator[T]
}

// Validate executes the validation rules immediately.
func (f StringValidator) Validate(ctx context.Context, value string) error {
func (f SVV[T]) Validate(ctx context.Context, value T) error {
return validatorOf(f, value).Validate(ctx)
}

// With attaches the next rule to the chain.
func (f StringValidator) With(next StringValidator) StringValidator {
func (f SVV[T]) With(next SVV[T]) SVV[T] {
return Chain(f, next)
}

// Required ensures the string is not empty.
func (f StringValidator) Required() StringValidator {
return f.With(func(ctx context.Context, value string) error {
func (f SVV[T]) Required() SVV[T] {
return f.With(func(ctx context.Context, value T) error {
if value == "" {
return NewRuleError(StringRequired)
}
Expand All @@ -36,8 +48,8 @@ func (f StringValidator) Required() StringValidator {
}

// Min ensures the length of the string is not less than the given length.
func (f StringValidator) Min(length int) StringValidator {
return f.With(func(ctx context.Context, value string) error {
func (f SVV[T]) Min(length int) SVV[T] {
return f.With(func(ctx context.Context, value T) error {
if len(value) < length {
return NewRuleError(StringMin, length)
}
Expand All @@ -46,8 +58,8 @@ func (f StringValidator) Min(length int) StringValidator {
}

// Max ensures the length of the string is not greater than the given length.
func (f StringValidator) Max(length int) StringValidator {
return f.With(func(ctx context.Context, value string) error {
func (f SVV[T]) Max(length int) SVV[T] {
return f.With(func(ctx context.Context, value T) error {
if len(value) > length {
return NewRuleError(StringMax, length)
}
Expand All @@ -57,16 +69,16 @@ func (f StringValidator) Max(length int) StringValidator {

// Match ensures the string matches the given pattern.
// If pattern cause panic, will be recovered.
func (f StringValidator) Match(pattern Pattern) StringValidator {
return f.With(func(ctx context.Context, value string) (err error) {
func (f SVV[T]) Match(pattern Pattern) SVV[T] {
return f.With(func(ctx context.Context, value T) (err error) {
defer func() {
if rec := recover(); rec != nil {
err = fmt.Errorf("panic: %v", rec)
}
}()

exp := pattern.RegExp()
if !exp.MatchString(value) {
if !exp.MatchString(string(value)) {
return NewRuleError(StringMatch, exp.String())
}

Expand All @@ -76,9 +88,9 @@ func (f StringValidator) Match(pattern Pattern) StringValidator {

// In ensures that the provided string is one of the specified options.
// This validation is case-sensitive, use InFold to perform a case-insensitive In validation.
func (f StringValidator) In(options ...string) StringValidator {
return f.With(func(ctx context.Context, value string) error {
ok := funcs.Contains(options, func(opt string) bool { return value == opt })
func (f SVV[T]) In(options ...T) SVV[T] {
return f.With(func(ctx context.Context, value T) error {
ok := funcs.Contains(options, func(opt T) bool { return value == opt })
if !ok {
return NewRuleError(StringIn, options)
}
Expand All @@ -87,9 +99,9 @@ func (f StringValidator) In(options ...string) StringValidator {
}

// InFold ensures that the provided string is one of the specified options with case-insensitivity.
func (f StringValidator) InFold(options ...string) StringValidator {
return f.With(func(ctx context.Context, value string) error {
ok := funcs.Contains(options, func(opt string) bool { return strings.EqualFold(value, opt) })
func (f SVV[T]) InFold(options ...T) SVV[T] {
return f.With(func(ctx context.Context, value T) error {
ok := funcs.Contains(options, func(opt T) bool { return strings.EqualFold(string(value), string(opt)) })
if !ok {
return NewRuleError(StringInFold, options)
}
Expand All @@ -104,6 +116,6 @@ func (f StringValidator) InFold(options ...string) StringValidator {
//
// The mapper function takes a StringValidator instance and returns a new StringValidator instance with
// additional validation logic.
func (f StringValidator) When(p Predicate[string], m Mapper[string, StringValidator]) StringValidator {
func (f SVV[T]) When(p Predicate[T], m Mapper[T, SVV[T]]) SVV[T] {
return whenLinker(f, p, m)
}