Skip to content

Commit 06ec79d

Browse files
Including regex version validation (#831)
1 parent 8fe074c commit 06ec79d

File tree

5 files changed

+95
-0
lines changed

5 files changed

+95
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ Baked-in Validations
189189
| uuid5 | Universally Unique Identifier UUID v5 |
190190
| uuid5_rfc4122 | Universally Unique Identifier UUID v5 RFC4122 |
191191
| uuid_rfc4122 | Universally Unique Identifier UUID RFC4122 |
192+
| semver | Semantic Versioning 2.0.0 |
192193
| ulid | Universally Unique Lexicographically Sortable Identifier ULID |
193194

194195
### Comparisons:

baked_in.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ var (
199199
"postcode_iso3166_alpha2": isPostcodeByIso3166Alpha2,
200200
"postcode_iso3166_alpha2_field": isPostcodeByIso3166Alpha2Field,
201201
"bic": isIsoBicFormat,
202+
"semver": isSemverFormat,
202203
"dns_rfc1035_label": isDnsRFC1035LabelFormat,
203204
}
204205
)
@@ -2421,6 +2422,13 @@ func isIsoBicFormat(fl FieldLevel) bool {
24212422
return bicRegex.MatchString(bicString)
24222423
}
24232424

2425+
// isSemverFormat is the validation function for validating if the current field's value is a valid semver version, defined in Semantic Versioning 2.0.0
2426+
func isSemverFormat(fl FieldLevel) bool {
2427+
semverString := fl.Field().String()
2428+
2429+
return semverRegex.MatchString(semverString)
2430+
}
2431+
24242432
// isDnsRFC1035LabelFormat is the validation function
24252433
// for validating if the current field's value is
24262434
// a valid dns RFC 1035 label, defined in RFC 1035.

doc.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1276,6 +1276,12 @@ More information on https://golang.org/pkg/time/#LoadLocation
12761276
12771277
Usage: timezone
12781278
1279+
Semantic Version
1280+
1281+
This validates that a string value is a valid semver version, defined in Semantic Versioning 2.0.0.
1282+
More information on https://semver.org/
1283+
1284+
Usage: semver
12791285
12801286
Alias Validators and Tags
12811287

regexes.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ const (
5252
jWTRegexString = "^[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]*$"
5353
splitParamsRegexString = `'[^']*'|\S+`
5454
bicRegexString = `^[A-Za-z]{6}[A-Za-z0-9]{2}([A-Za-z0-9]{3})?$`
55+
semverRegexString = `^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$` // numbered capture groups https://semver.org/
5556
dnsRegexStringRFC1035Label = "^[a-z]([-a-z0-9]*[a-z0-9]){0,62}$"
5657
)
5758

@@ -105,5 +106,6 @@ var (
105106
jWTRegex = regexp.MustCompile(jWTRegexString)
106107
splitParamsRegex = regexp.MustCompile(splitParamsRegexString)
107108
bicRegex = regexp.MustCompile(bicRegexString)
109+
semverRegex = regexp.MustCompile(semverRegexString)
108110
dnsRegexRFC1035Label = regexp.MustCompile(dnsRegexStringRFC1035Label)
109111
)

validator_test.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11383,6 +11383,84 @@ func TestBicIsoFormatValidation(t *testing.T) {
1138311383
}
1138411384
}
1138511385

11386+
func TestSemverFormatValidation(t *testing.T) {
11387+
tests := []struct {
11388+
value string `validate:"semver"`
11389+
tag string
11390+
expected bool
11391+
}{
11392+
{"1.2.3", "semver", true},
11393+
{"10.20.30", "semver", true},
11394+
{"1.1.2-prerelease+meta", "semver", true},
11395+
{"1.1.2+meta", "semver", true},
11396+
{"1.1.2+meta-valid", "semver", true},
11397+
{"1.0.0-alpha", "semver", true},
11398+
{"1.0.0-alpha.1", "semver", true},
11399+
{"1.0.0-alpha.beta", "semver", true},
11400+
{"1.0.0-alpha.beta.1", "semver", true},
11401+
{"1.0.0-alpha0.valid", "semver", true},
11402+
{"1.0.0-alpha.0valid", "semver", true},
11403+
{"1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay", "semver", true},
11404+
{"1.0.0-rc.1+build.1", "semver", true},
11405+
{"1.0.0-rc.1+build.123", "semver", true},
11406+
{"1.2.3-beta", "semver", true},
11407+
{"1.2.3-DEV-SNAPSHOT", "semver", true},
11408+
{"1.2.3-SNAPSHOT-123", "semver", true},
11409+
{"2.0.0+build.1848", "semver", true},
11410+
{"2.0.1-alpha.1227", "semver", true},
11411+
{"1.0.0-alpha+beta", "semver", true},
11412+
{"1.2.3----RC-SNAPSHOT.12.9.1--.12+788", "semver", true},
11413+
{"1.2.3----R-S.12.9.1--.12+meta", "semver", true},
11414+
{"1.2.3----RC-SNAPSHOT.12.9.1--.12", "semver", true},
11415+
{"1.0.0+0.build.1-rc.10000aaa-kk-0.1", "semver", true},
11416+
{"99999999999999999999999.999999999999999999.99999999999999999", "semver", true},
11417+
{"1.0.0-0A.is.legal", "semver", true},
11418+
{"1", "semver", false},
11419+
{"1.2", "semver", false},
11420+
{"1.2.3-0123", "semver", false},
11421+
{"1.2.3-0123.0123", "semver", false},
11422+
{"1.1.2+.123", "semver", false},
11423+
{"+invalid", "semver", false},
11424+
{"-invalid", "semver", false},
11425+
{"-invalid+invalid", "semver", false},
11426+
{"alpha", "semver", false},
11427+
{"alpha.beta.1", "semver", false},
11428+
{"alpha.1", "semver", false},
11429+
{"1.0.0-alpha_beta", "semver", false},
11430+
{"1.0.0-alpha_beta", "semver", false},
11431+
{"1.0.0-alpha...1", "semver", false},
11432+
{"01.1.1", "semver", false},
11433+
{"1.01.1", "semver", false},
11434+
{"1.1.01", "semver", false},
11435+
{"1.2", "semver", false},
11436+
{"1.2.Dev", "semver", false},
11437+
{"1.2.3.Dev", "semver", false},
11438+
{"1.2-SNAPSHOT", "semver", false},
11439+
}
11440+
11441+
validate := New()
11442+
11443+
for i, test := range tests {
11444+
11445+
errs := validate.Var(test.value, test.tag)
11446+
11447+
if test.expected {
11448+
if !IsEqual(errs, nil) {
11449+
t.Fatalf("Index: %d semver failed Error: %s", i, errs)
11450+
}
11451+
} else {
11452+
if IsEqual(errs, nil) {
11453+
t.Fatalf("Index: %d semver failed Error: %s", i, errs)
11454+
} else {
11455+
val := getError(errs, "", "")
11456+
if val.Tag() != "semver" {
11457+
t.Fatalf("Index: %d semver failed Error: %s", i, errs)
11458+
}
11459+
}
11460+
}
11461+
}
11462+
}
11463+
1138611464
func TestRFC1035LabelFormatValidation(t *testing.T) {
1138711465
tests := []struct {
1138811466
value string `validate:"dns_rfc1035_label"`

0 commit comments

Comments
 (0)