Repository: go-playground/validator
Pull request #1373 (2025-02-13 00:54:23)

Create dependabot
## Fixes Or Enhances
This PR adds a `dependabot.yml` configuration file to enable automated dependency updates for:
- Golang
- GitHub Actions

**Make sure that you've checked the boxes below before you submit PR:**
- [ ] Tests exist or have been written that cover this particular change.

@go-playground/validator-maintainers

coveralls: 
[![Coverage Status](https://coveralls.io/builds/72220640/badge)](https://coveralls.io/builds/72220640)

coverage: 74.319%. remained the same
when pulling **f7229c053ad9b8a745c18b525a08399e21bc0ea2 on nodivbyzero:add-dependabot**
into **e564451a1e151ec5a714cbe14b0dd344e05bb39d on go-playground:master**.


Patch:
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
deleted file mode 100644
index 40b774a..0000000
--- a/.github/ISSUE_TEMPLATE.md
+++ /dev/null
@@ -1,16 +0,0 @@
-- [ ] I have looked at the documentation [here](https://pkg.go.dev/github.com/go-playground/validator/v10#section-documentation) first?
-- [ ] I have looked at the examples provided that may showcase my question [here](/_examples)?
-
-### Package version eg. v9, v10: 
-
-
-
-### Issue, Question or Enhancement:
-
-
-
-### Code sample, to showcase or reproduce:
-
-```go
-
-```
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
new file mode 100644
index 0000000..38dce27
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -0,0 +1,31 @@
+name: Bug Report
+description: File a bug report
+title: "[Bug]: "
+labels: ["bug"]
+body:
+  - type: markdown
+    attributes:
+      value: |
+        Thanks for taking the time to fill out this bug report!
+  - type: textarea
+    id: what-happened
+    attributes:
+      label: What happened?
+      description: Also tell us, what did you expect to happen?
+    validations:
+      required: true
+  - type: textarea
+    id: version
+    attributes:
+      label: Version
+      description: What version of validator are you running?
+    validations:
+      required: true
+  - type: textarea
+    id: code
+    attributes:
+      label: Example Code
+      description: Please provide a code example that demonstrates the issue
+      render: go
+    validations:
+      required: true 
\ No newline at end of file
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..ce811d1
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,12 @@
+version: 2
+updates:
+# Maintain dependencies for Golang
+- package-ecosystem: gomod
+  directory: "/"
+  schedule:
+    interval: weekly
+# Maintain dependencies for GitHub Actions
+- package-ecosystem: github-actions
+  directory: "/"
+  schedule:
+    interval: weekly
diff --git a/README.md b/README.md
index ee51a5f..e273e39 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
 Package validator
 =================
 <img align="right" src="logo.png">[![Join the chat at https://gitter.im/go-playground/validator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-playground/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
-![Project status](https://img.shields.io/badge/version-10.24.0-green.svg)
+![Project status](https://img.shields.io/badge/version-10.25.0-green.svg)
 [![Build Status](https://github.com/go-playground/validator/actions/workflows/workflow.yml/badge.svg)](https://github.com/go-playground/validator/actions)
 [![Coverage Status](https://coveralls.io/repos/go-playground/validator/badge.svg?branch=master&service=github)](https://coveralls.io/github/go-playground/validator?branch=master)
 [![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/validator)](https://goreportcard.com/report/github.com/go-playground/validator)
diff --git a/_examples/simple/main.go b/_examples/simple/main.go
index 5a5da4d..606822b 100644
--- a/_examples/simple/main.go
+++ b/_examples/simple/main.go
@@ -62,7 +62,8 @@ func validateStruct() {
 		// this check is only needed when your code could produce
 		// an invalid value for validation such as interface with nil
 		// value most including myself do not usually have code like this.
-		if errors.As(err, &validator.InvalidValidationError{}) {
+		var invalidValidationError *validator.InvalidValidationError
+		if errors.As(err, &invalidValidationError) {
 			fmt.Println(err)
 			return
 		}
diff --git a/_examples/struct-level/main.go b/_examples/struct-level/main.go
index 1885fc7..faba03f 100644
--- a/_examples/struct-level/main.go
+++ b/_examples/struct-level/main.go
@@ -115,7 +115,8 @@ func main() {
 		// this check is only needed when your code could produce
 		// an invalid value for validation such as interface with nil
 		// value most including myself do not usually have code like this.
-		if errors.As(err, &validator.InvalidValidationError{}) {
+		var invalidValidationError *validator.InvalidValidationError
+		if errors.As(err, &invalidValidationError) {
 			fmt.Println(err)
 			return
 		}
diff --git a/baked_in.go b/baked_in.go
index dab60f1..cca3e09 100644
--- a/baked_in.go
+++ b/baked_in.go
@@ -50,6 +50,7 @@ var (
 		keysTag:           {},
 		endKeysTag:        {},
 		structOnlyTag:     {},
+		omitzero:          {},
 		omitempty:         {},
 		omitnil:           {},
 		skipValidationTag: {},
@@ -1797,6 +1798,20 @@ func hasValue(fl FieldLevel) bool {
 	}
 }
 
+// hasNotZeroValue is the validation function for validating if the current field's value is not the zero value for its type.
+func hasNotZeroValue(fl FieldLevel) bool {
+	field := fl.Field()
+	switch field.Kind() {
+	case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func:
+		return !field.IsNil()
+	default:
+		if fl.(*validate).fldIsPointer && field.Interface() != nil {
+			return !field.IsZero()
+		}
+		return field.IsValid() && !field.IsZero()
+	}
+}
+
 // requireCheckFieldKind is a func for check field kind
 func requireCheckFieldKind(fl FieldLevel, param string, defaultNotFoundValue bool) bool {
 	field := fl.Field()
diff --git a/cache.go b/cache.go
index 2063e1b..cbf5ff0 100644
--- a/cache.go
+++ b/cache.go
@@ -21,6 +21,7 @@ const (
 	typeKeys
 	typeEndKeys
 	typeOmitNil
+	typeOmitZero
 )
 
 const (
@@ -249,6 +250,10 @@ func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias s
 			}
 			return
 
+		case omitzero:
+			current.typeof = typeOmitZero
+			continue
+
 		case omitempty:
 			current.typeof = typeOmitEmpty
 			continue
diff --git a/validator.go b/validator.go
index 901e7b5..d7c2e65 100644
--- a/validator.go
+++ b/validator.go
@@ -117,6 +117,10 @@ func (v *validate) traverseField(ctx context.Context, parent reflect.Value, curr
 			return
 		}
 
+		if ct.typeof == typeOmitZero {
+			return
+		}
+
 		if ct.hasTag {
 			if kind == reflect.Invalid {
 				v.str1 = string(append(ns, cf.altName...))
@@ -238,6 +242,19 @@ OUTER:
 			ct = ct.next
 			continue
 
+		case typeOmitZero:
+			v.slflParent = parent
+			v.flField = current
+			v.cf = cf
+			v.ct = ct
+
+			if !hasNotZeroValue(v) {
+				return
+			}
+
+			ct = ct.next
+			continue
+
 		case typeOmitNil:
 			v.slflParent = parent
 			v.flField = current
diff --git a/validator_instance.go b/validator_instance.go
index d9f148d..779f689 100644
--- a/validator_instance.go
+++ b/validator_instance.go
@@ -21,6 +21,7 @@ const (
 	tagKeySeparator       = "="
 	structOnlyTag         = "structonly"
 	noStructLevelTag      = "nostructlevel"
+	omitzero              = "omitzero"
 	omitempty             = "omitempty"
 	omitnil               = "omitnil"
 	isdefault             = "isdefault"


Test patch:
diff --git a/validator_test.go b/validator_test.go
index 5eadb25..af05d19 100644
--- a/validator_test.go
+++ b/validator_test.go
@@ -14022,6 +14022,57 @@ func TestOmitNilAndRequired(t *testing.T) {
 	})
 }
 
+func TestOmitZero(t *testing.T) {
+	type (
+		OmitEmpty struct {
+			Str    string  `validate:"omitempty,min=10"`
+			StrPtr *string `validate:"omitempty,min=10"`
+		}
+		OmitZero struct {
+			Str    string  `validate:"omitzero,min=10"`
+			StrPtr *string `validate:"omitzero,min=10"`
+		}
+	)
+
+	var (
+		validate = New()
+		valid    = "this is the long string to pass the validation rule"
+		empty    = ""
+	)
+
+	t.Run("compare using valid data", func(t *testing.T) {
+		err1 := validate.Struct(OmitEmpty{Str: valid, StrPtr: &valid})
+		err2 := validate.Struct(OmitZero{Str: valid, StrPtr: &valid})
+
+		Equal(t, err1, nil)
+		Equal(t, err2, nil)
+	})
+
+	t.Run("compare fully empty omitempty and omitzero", func(t *testing.T) {
+		err1 := validate.Struct(OmitEmpty{})
+		err2 := validate.Struct(OmitZero{})
+
+		Equal(t, err1, nil)
+		Equal(t, err2, nil)
+	})
+
+	t.Run("compare with zero value", func(t *testing.T) {
+		err1 := validate.Struct(OmitEmpty{Str: "", StrPtr: nil})
+		err2 := validate.Struct(OmitZero{Str: "", StrPtr: nil})
+
+		Equal(t, err1, nil)
+		Equal(t, err2, nil)
+	})
+
+	t.Run("compare with empty value", func(t *testing.T) {
+		err1 := validate.Struct(OmitEmpty{Str: empty, StrPtr: &empty})
+		err2 := validate.Struct(OmitZero{Str: empty, StrPtr: &empty})
+
+		AssertError(t, err1, "OmitEmpty.StrPtr", "OmitEmpty.StrPtr", "StrPtr", "StrPtr", "min")
+		Equal(t, err2, nil)
+	})
+}
+
 func TestPrivateFieldsStruct(t *testing.T) {
 	type tc struct {
 		stct     interface{}