Skip to content

[WIP] Support validation markers for slice/array elements - #209

Draft
shiiyan wants to merge 8 commits into
sivchari:mainfrom
shiiyan:feat/support-markers-for-slice-array
Draft

[WIP] Support validation markers for slice/array elements#209
shiiyan wants to merge 8 commits into
sivchari:mainfrom
shiiyan:feat/support-markers-for-slice-array

Conversation

@shiiyan

@shiiyan shiiyan commented Jan 2, 2026

Copy link
Copy Markdown
Contributor

Description

This Work In Progress (WIP) pull request implements support for validation markers targeted at elements within slices and arrays. It's a new feature addition in the repository and addresses the use case where validations need to be applied to individual elements of collections, rather than just the container.

How Slice/Array Validation for gt Was Implemented:

  • Kept the generator template unchanged if {{.Validate}} { ... }
  • Taught the gt rule factory to accept both: numeric scalars (int, float64, etc.) []T / [N]T where element type T is numeric
  • For scalar fields, generated the existing failure condition: !(t.Field > value)
  • For slice/array fields, generated a boolean “failure condition” as an inline function with a for _, v := range t.Field loop that returns true on the first violating element

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Refactoring (no functional changes)

Quality Checklist

Before submitting this pull request, ensure your contribution meets these quality standards:

Code Quality

  • Code passes make golangci-lint without errors
  • All tests pass: make test
  • Code follows existing patterns and conventions
  • No hard-coded values (use constants or configuration)
  • Error messages are clear and actionable

Testing Requirements

  • Golden tests created and passing (internal/analyzers/govalid/testdata/)
  • Unit tests implemented with boundary value testing (test/unit/)
  • Benchmark tests comparing against popular validation libraries (test/benchmark/)
  • All test files generated: cd test && go generate

Performance Standards

  • Zero allocations in validation logic (verified via benchmarks)
  • Performance improvement over existing validation libraries (minimum 2x faster)
  • Benchmark results added to test/benchmark/README.md

Documentation

  • README.md updated with new marker documentation
  • Code includes appropriate comments and examples
  • Validator usage examples provided

Integration

  • Validator scaffold generated with make generate-validator MARKER=yourmarker
  • Registry files automatically updated
  • Binary rebuilt and installed: go install ./cmd/govalid/

Final Verification

  • All GitHub Actions CI checks pass
  • No breaking changes to existing functionality
  • Contribution follows project license requirements

Additional Notes

Related Issues

Closes #80

@shiiyan shiiyan changed the title Support validation markers for slice/array elements [WIP] Support validation markers for slice/array elements Jan 2, 2026
@shiiyan shiiyan closed this Jan 6, 2026
@shiiyan shiiyan reopened this Jan 6, 2026
@github-actions

github-actions Bot commented Jan 6, 2026

Copy link
Copy Markdown
Contributor

📊 Coverage Report

Total Coverage: 81.4%

Details

Run make coverage locally to see detailed coverage by function.

@shiiyan

shiiyan commented Jan 6, 2026

Copy link
Copy Markdown
Contributor Author

@sivchari
Happy New Year! I’ve added support for slice and array elements in the gt marker as an example. Could you take a quick look? If everything looks good, I’ll move on to implementing the other markers.

@sivchari

sivchari commented Jan 8, 2026

Copy link
Copy Markdown
Owner

Hi @shiiyan
Happy New Year! I'll take a look this weekend!

@sivchari

Copy link
Copy Markdown
Owner

Sorry for the late response!

Thanks for working on this! Slice/array element validation is a great addition.

I took a look at the implementation and have some thoughts on how we could make it more extensible. Let me share a few ideas.


Error path should include the index

Right now, when an element fails validation, we can't tell which one:

// Current
err.Path = "GT.Scores"      // Which element failed?
err.Value = t.Scores        // The whole slice

// Ideal
err.Path = "GT.Scores[0]"   // Ah, the first element\!
err.Value = v               // Just that element

This would make debugging much easier and matches what other validators like go-playground do with dive.


Suggestion: Extract type-checking utilities

The helpers in ValidateGT (isNumeric, isIterable, etc.) would be useful for other validators too — lt, gte, lte, and string validators like minLength, email.

How about extracting them to a shared place?

// internal/validator/rules/types.go

func GetElementType(t types.Type) types.Type {
    switch u := t.Underlying().(type) {
    case *types.Slice:
        return u.Elem()
    case *types.Array:
        return u.Elem()
    default:
        return nil
    }
}

func IsNumeric(t types.Type) bool {
    basic, ok := t.Underlying().(*types.Basic)
    return ok && (basic.Info()&types.IsNumeric) \!= 0
}

// Returns (supported, isElementValidation)
func CheckTypeSupport(t types.Type, check func(types.Type) bool) (bool, bool) {
    if check(t) {
        return true, false
    }
    if elem := GetElementType(t); elem \!= nil && check(elem) {
        return true, true
    }
    return false, false
}

Then in the factory:

func ValidateGT(input registry.ValidatorInput) validator.Validator {
    typ := input.Pass.TypesInfo.TypeOf(input.Field.Type)
    
    supported, isElement := CheckTypeSupport(typ, IsNumeric)
    if \!supported {
        return nil
    }
    
    v := &gtValidator{...}
    
    if isElement {
        return &validator.ElementValidatorWrapper{Inner: v}
    }
    return v
}

Suggestion: Move loop generation to the template

Instead of generating the loop inside Validate(), we could keep Validate() simple and handle the loop in the template:

// Validate() stays simple — single value logic only
func (m *gtValidator) Validate() string {
    return fmt.Sprintf("\!(t.%s > %s)", m.FieldName(), m.gtValue)
}

Template handles the rest:

{{ if isElementValidation . }}
    for i, v := range t.{{.FieldName}} {
        if {{ .Validate | replaceFieldRef .FieldName }} {
            err := {{.ErrVariable}}
            err.Path = fmt.Sprintf("{{.FieldPath}}[%d]", i)
            err.Value = v
            errs = append(errs, err)
        }
    }
{{ else }}
    // existing single-value logic
{{ end }}

This way:

  • Validators stay simple and easy to test
  • Loop + index logic is in one place
  • Adding element validation to other validators is just: wrap with ElementValidatorWrapper

Quick summary

Current Suggested
Error path Scores Scores[0]
err.Value whole slice failed element
Type checking inline in factory shared utilities
Loop generation in Validate() in template

What do you think? Happy to discuss or help with the implementation!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support validation markers for slice/array elements

2 participants