blob: ce3dc047a52d8d384243aa6b37b8cd2fced236e7 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package protoversion
import (
"strconv"
"strings"
)
var _ PackageVersion = &packageVersion{}
type packageVersion struct {
major int
stabilityLevel StabilityLevel
minor int
patch int
suffix string
}
func newPackageVersionForPackage(pkg string) (*packageVersion, bool) {
if pkg == "" {
return nil, false
}
parts := strings.Split(pkg, ".")
if len(parts) < 2 {
return nil, false
}
lastPart := parts[len(parts)-1]
// must at least contain 'v' and a number
if len(lastPart) < 2 {
return nil, false
}
if lastPart[0] != 'v' {
return nil, false
}
// v1beta1 -> 1beta1
// v1testfoo -> 1testfoo
// v1p1alpha1 -> p1alpha1
version := lastPart[1:]
if strings.Contains(version, "test") {
// 1testfoo -> [1, foo]
split := strings.SplitN(version, "test", 2)
if len(split) != 2 {
return nil, false
}
major, ok := positiveNumber(split[0])
if !ok {
return nil, false
}
return newPackageVersion(major, StabilityLevelTest, 0, 0, split[1]), true
}
var stabilityLevel StabilityLevel
containsAlpha := strings.Contains(version, "alpha")
containsBeta := strings.Contains(version, "beta")
switch {
case !containsAlpha && !containsBeta:
stabilityLevel = StabilityLevelStable
case containsAlpha && !containsBeta:
stabilityLevel = StabilityLevelAlpha
case !containsAlpha && containsBeta:
stabilityLevel = StabilityLevelBeta
case containsAlpha && containsBeta:
return nil, false
}
if stabilityLevel != StabilityLevelStable {
// 1alpha1 -> [1, 1]
// 1p1alpha1 ->[1p1, 1]
// 1alpha -> [1, ""]
split := strings.SplitN(version, stabilityLevel.String(), 2)
if len(split) != 2 {
return nil, false
}
minor := 0
var ok bool
if split[1] != "" {
minor, ok = positiveNumber(split[1])
if !ok {
return nil, false
}
}
major, patch, ok := getAlphaBetaMajorPatch(split[0])
if !ok {
return nil, false
}
return newPackageVersion(major, stabilityLevel, minor, patch, ""), true
}
// no suffix that is valid, make sure we just have a number
major, ok := positiveNumber(version)
if !ok {
return nil, false
}
return newPackageVersion(major, StabilityLevelStable, 0, 0, ""), true
}
func newPackageVersion(
major int,
stabilityLevel StabilityLevel,
minor int,
patch int,
suffix string,
) *packageVersion {
return &packageVersion{
major: major,
stabilityLevel: stabilityLevel,
minor: minor,
patch: patch,
suffix: suffix,
}
}
func (p *packageVersion) Major() int {
return p.major
}
func (p *packageVersion) StabilityLevel() StabilityLevel {
return p.stabilityLevel
}
func (p *packageVersion) Minor() int {
return p.minor
}
func (p *packageVersion) Patch() int {
return p.patch
}
func (p *packageVersion) Suffix() string {
return p.suffix
}
func (p *packageVersion) String() string {
var builder strings.Builder
builder.WriteRune('v')
builder.WriteString(strconv.Itoa(p.major))
if p.patch > 0 {
builder.WriteRune('p')
builder.WriteString(strconv.Itoa(p.patch))
}
builder.WriteString(p.stabilityLevel.String())
if p.minor > 0 {
builder.WriteString(strconv.Itoa(p.minor))
}
if p.suffix != "" {
builder.WriteString(p.suffix)
}
return builder.String()
}
func (p *packageVersion) isPackageVersion() {}
func getAlphaBetaMajorPatch(remainder string) (int, int, bool) {
if strings.Contains(remainder, "p") {
// 1p1 -> [1, 1]
patchSplit := strings.SplitN(remainder, "p", 2)
if len(patchSplit) != 2 {
return 0, 0, false
}
major, ok := positiveNumber(patchSplit[0])
if !ok {
return 0, 0, false
}
patch, ok := positiveNumber(patchSplit[1])
if !ok {
return 0, 0, false
}
return major, patch, true
}
// no patch, make sure just a number
major, ok := positiveNumber(remainder)
if !ok {
return 0, 0, false
}
return major, 0, true
}
func positiveNumber(s string) (int, bool) {
if s == "" {
return 0, false
}
value, err := strconv.ParseInt(s, 10, 32)
if err != nil {
return 0, false
}
if value < 1 {
return 0, false
}
return int(value), true
}