package semver import ( "errors" "fmt" "regexp" "strconv" "strings" ) //nolint:lll // regex is as it is var ( ErrNotASemver = errors.New("the given version does not match the semantic versioning scheme") semVerRegex = regexp.MustCompile(`^(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)(?:-(?P(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$`) ) func IsValid(version string) bool { return semVerRegex.MatchString(strings.TrimPrefix(version, "v")) } func ParseVersion(version string) (v Version, err error) { if !IsValid(version) { return Version{}, fmt.Errorf("%w: %s", ErrNotASemver, version) } submatches := semVerRegex.FindStringSubmatch(strings.TrimPrefix(version, "v")) for i, name := range semVerRegex.SubexpNames() { switch name { case "major": if v.Major, err = strconv.Atoi(submatches[i]); err != nil { return Version{}, err } case "minor": if v.Minor, err = strconv.Atoi(submatches[i]); err != nil { return Version{}, err } case "patch": if v.Patch, err = strconv.Atoi(submatches[i]); err != nil { return Version{}, err } case "prerelease": v.Prerelease = submatches[i] case "buildmetadata": v.BuildMetadata = submatches[i] } } return v, nil } type Version struct { Prerelease string `cty:"prerelease"` BuildMetadata string `cty:"build_metadata"` Major int `cty:"major"` Minor int `cty:"minor"` Patch int `cty:"patch"` }