newt/newtutil/repo_version.go (168 lines of code) (raw):
/**
* 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 newtutil
import (
"fmt"
"math"
"sort"
"strconv"
"strings"
"mynewt.apache.org/newt/util"
)
const (
VERSION_STABILITY_NONE = ""
VERSION_STABILITY_STABLE = "stable"
VERSION_STABILITY_DEV = "dev"
VERSION_STABILITY_LATEST = "latest"
// "commit" is not actually a stability, but it takes the place of one in
// the repo version notation. The "commit" string indicates a commit hash,
// tag, or branch, rather than a version specifier.
VERSION_STABILITY_COMMIT = "commit"
)
// Represents an unspecified part in a version. For example, in "1-latest",
// the minor and revision parts are floating.
const VERSION_FLOATING = -1
type RepoVersion struct {
Major int64
Minor int64
Revision int64
Stability string
Commit string
}
func (v *RepoVersion) IsNormalized() bool {
return v.Stability == VERSION_STABILITY_NONE
}
func (v *RepoVersion) toComparable() RepoVersion {
clone := *v
// 0.0.0 means "latest develop"; it is greater than all over version
// numbers.
if v.Major == 0 && v.Minor == 0 && v.Revision == 0 {
clone.Major = math.MaxInt64
clone.Minor = math.MaxInt64
clone.Revision = math.MaxInt64
}
return clone
}
func CompareRepoVersions(v1 RepoVersion, v2 RepoVersion) int {
v1 = v1.toComparable()
v2 = v2.toComparable()
toInt := func(i64 int64) int {
if i64 < 0 {
return -1
} else if i64 > 0 {
return 1
} else {
return 0
}
}
if r := v1.Major - v2.Major; r != 0 {
return toInt(r)
}
if r := v1.Minor - v2.Minor; r != 0 {
return toInt(r)
}
if r := v1.Revision - v2.Revision; r != 0 {
return toInt(r)
}
return 0
}
func (ver *RepoVersion) String() string {
if ver.Commit != "" {
return ver.Commit
}
s := fmt.Sprintf("%d", ver.Major)
if ver.Minor != VERSION_FLOATING {
s += fmt.Sprintf(".%d", ver.Minor)
}
if ver.Revision != VERSION_FLOATING {
s += fmt.Sprintf(".%d", ver.Revision)
}
if ver.Stability != VERSION_STABILITY_NONE {
s += fmt.Sprintf("-%s", ver.Stability)
}
return s
}
func (ver *RepoVersion) ToNuVersion() Version {
return Version{
Major: ver.Major,
Minor: ver.Minor,
Revision: ver.Revision,
}
}
func ParseRepoVersion(verStr string) (RepoVersion, error) {
var err error
stability := VERSION_STABILITY_NONE
base := verStr
dashIdx := strings.LastIndex(verStr, "-")
if dashIdx != -1 {
stability = strings.TrimSpace(verStr[dashIdx+1:])
base = strings.TrimSpace(verStr[:dashIdx])
switch stability {
case VERSION_STABILITY_COMMIT:
return RepoVersion{Commit: strings.TrimSpace(base)}, nil
case VERSION_STABILITY_STABLE:
case VERSION_STABILITY_DEV:
case VERSION_STABILITY_LATEST:
default:
return RepoVersion{}, util.FmtNewtError(
"Unknown stability (%s) in version %s", stability, verStr)
}
}
parts := strings.Split(base, ".")
if len(parts) > 3 {
return RepoVersion{},
util.FmtNewtError("Invalid version string: \"%s\"; "+
"must be fixed (X.X.X) or floating (X[.X]-stability)", verStr)
}
if len(parts) != 3 && stability == VERSION_STABILITY_NONE {
return RepoVersion{},
util.FmtNewtError("Invalid version string: \"%s\"; "+
"must be fixed (X.X.X) or floating (X[.X]-stability)", verStr)
}
// Assume no parts of the version are specified.
ver := RepoVersion{
Major: VERSION_FLOATING,
Minor: VERSION_FLOATING,
Revision: VERSION_FLOATING,
Stability: stability,
}
// Convert each dot-delimited part to an integer.
if ver.Major, err = strconv.ParseInt(parts[0], 10, 64); err != nil {
return RepoVersion{}, util.NewNewtError(err.Error())
}
if len(parts) >= 2 {
if ver.Minor, err = strconv.ParseInt(parts[1], 10, 64); err != nil {
return RepoVersion{}, util.NewNewtError(err.Error())
}
}
if len(parts) == 3 {
if ver.Revision, err = strconv.ParseInt(parts[2], 10, 64); err != nil {
return RepoVersion{}, util.NewNewtError(err.Error())
}
}
return ver, nil
}
type verSorter struct {
vers []RepoVersion
}
func (v verSorter) Len() int {
return len(v.vers)
}
func (v verSorter) Swap(i, j int) {
v.vers[i], v.vers[j] = v.vers[j], v.vers[i]
}
func (v verSorter) Less(i, j int) bool {
a := v.vers[i]
b := v.vers[j]
return CompareRepoVersions(a, b) < 0
}
func SortVersions(vers []RepoVersion) {
sorter := verSorter{
vers: vers,
}
sort.Sort(sorter)
}
func SortedVersions(vers []RepoVersion) []RepoVersion {
clone := make([]RepoVersion, len(vers))
copy(clone, vers)
SortVersions(clone)
return clone
}
func SortedVersionsDesc(vers []RepoVersion) []RepoVersion {
slice := SortedVersions(vers)
size := len(slice)
for i := 0; i < size/2; i++ {
j := size - 1 - i
slice[i], slice[j] = slice[j], slice[i]
}
return slice
}