Skip to content

Commit

Permalink
format: add semver
Browse files Browse the repository at this point in the history
  • Loading branch information
santhosh-tekuri committed May 5, 2024
1 parent 0600f4a commit 5fdfddc
Show file tree
Hide file tree
Showing 2 changed files with 227 additions and 0 deletions.
77 changes: 77 additions & 0 deletions format.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ var formats = map[string]*Format{
"uri-reference": {"uri-reference", validateURIReference},
"iri-reference": {"iri-reference", validateURIReference},
"uri-template": {"uri-template", validateURITemplate},
"semver": {"semver", validateSemver},
}

// see https://www.rfc-editor.org/rfc/rfc6901#section-3
Expand Down Expand Up @@ -633,3 +634,79 @@ func validatePeriod(v any) error {

return nil
}

// see https://semver.org/#backusnaur-form-grammar-for-valid-semver-versions
func validateSemver(v any) error {
s, ok := v.(string)
if !ok {
return nil
}

// build --
if i := strings.IndexByte(s, '+'); i != -1 {
build := s[i+1:]
if build == "" {
return errors.New("build is empty")
}
for _, buildID := range strings.Split(build, ".") {
if buildID == "" {
return errors.New("build identifier is empty")
}
for _, ch := range buildID {
switch {
case ch >= '0' && ch <= '9':
case (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '-':
default:
return fmt.Errorf("invalid character %q in build identifier", ch)
}
}
}
s = s[:i]
}

// pre-release --
if i := strings.IndexByte(s, '-'); i != -1 {
preRelease := s[i+1:]
for _, preReleaseID := range strings.Split(preRelease, ".") {
if preReleaseID == "" {
return errors.New("pre-release identifier is empty")
}
allDigits := true
for _, ch := range preReleaseID {
switch {
case ch >= '0' && ch <= '9':
case (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '-':
allDigits = false
default:
return fmt.Errorf("invalid character %q in pre-release identifier", ch)
}
}
if allDigits && len(preReleaseID) > 1 && preReleaseID[0] == '0' {
return fmt.Errorf("pre-release numeric identifier starts with zero")
}
}
s = s[:i]
}

// versionCore --
versions := strings.Split(s, ".")
if len(versions) != 3 {
return errors.New("versionCore must have 3 numbers separated by dot")
}
names := []string{"major", "minor", "patch"}
for i, version := range versions {
if version == "" {
return fmt.Errorf("%s is empty", names[i])
}
if len(version) > 1 && version[0] == '0' {
return fmt.Errorf("%s starts with zero", names[i])
}
for _, ch := range version {
if ch < '0' || ch > '9' {
return fmt.Errorf("%s contains non-digit", names[i])
}
}
}

return nil
}
150 changes: 150 additions & 0 deletions testdata/Extra-Test-Suite/tests/draft7/optional/format/semver.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
[
{
"description": "validation of semver",
"schema": {
"format": "semver"
},
"tests": [
{
"description": "all string formats ignore integers",
"data": 12,
"valid": true
},
{
"description": "all string formats ignore floats",
"data": 13.7,
"valid": true
},
{
"description": "all string formats ignore objects",
"data": {},
"valid": true
},
{
"description": "all string formats ignore arrays",
"data": [],
"valid": true
},
{
"description": "all string formats ignore booleans",
"data": false,
"valid": true
},
{
"description": "all string formats ignore nulls",
"data": null,
"valid": true
},
{
"description": "valid1",
"data": "1.0.0-alpha",
"valid": true
},
{
"description": "valid2",
"data": "1.0.0-alpha.1",
"valid": true
},
{
"description": "valid3",
"data": "1.0.0-alpha.beta",
"valid": true
},
{
"description": "valid4",
"data": "1.0.0-beta",
"valid": true
},
{
"description": "valid5",
"data": "1.0.0-beta.2",
"valid": true
},
{
"description": "valid6",
"data": "1.0.0-beta.11",
"valid": true
},
{
"description": "valid7",
"data": "1.0.0-rc.1",
"valid": true
},
{
"description": "valid8",
"data": "1.0.0",
"valid": true
},
{
"description": "build1",
"data": "1.0.0-alpha+001",
"valid": true
},
{
"description": "build2",
"data": "1.0.0+20130313144700",
"valid": true
},
{
"description": "build3",
"data": "1.0.0-beta+exp.sha.5114f85",
"valid": true
},
{
"description": "build4",
"data": "1.0.0+21AF26D3----117B344092BD",
"valid": true
},
{
"description": "prerelease1",
"data": "1.0.0-0.3.7",
"valid": true
},
{
"description": "prerelease2",
"data": "1.0.0-x.7.z.92",
"valid": true
},
{
"description": "prerelease3",
"data": "1.0.0-x-y-z.--",
"valid": true
},
{
"description": "bad",
"data": "bad",
"valid": false
},
{
"description": "valid",
"data": "1.2.3+test.01",
"valid": true
},
{
"description": "no version-core",
"data": "-1.2.3",
"valid": false
},
{
"description": "underscore in pre-release identifier",
"data": "0.88.0-11_e4e5dcabb",
"valid": false
},
{
"description": "underscore in build identifier",
"data": "0.88.0+11_e4e5dcabb",
"valid": false
},
{
"description": "letter in version-core",
"data": "1.2.3x",
"valid": false
},
{
"description": "version starting with zero",
"data": "0x1.3.4",
"valid": false
}
]
}
]

0 comments on commit 5fdfddc

Please sign in to comment.