mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-26 04:05:40 +01:00
Implement the functions of getting chart version details and chart lists
getting chart list returns chart list instead of map and provides total versions info getting chart details will return dependencies and values besides the metatdata of the chart version
This commit is contained in:
parent
b65cc34c70
commit
7d782c41fc
8
src/Gopkg.lock
generated
8
src/Gopkg.lock
generated
@ -58,6 +58,12 @@
|
||||
packages = ["."]
|
||||
revision = "e87155e8f0c05bf323d0b13470e1b97af0cb5652"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/blang/semver"
|
||||
packages = ["."]
|
||||
revision = "2ee87856327ba09384cabd113bc6b5d174e9ec0f"
|
||||
version = "v3.5.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/davecgh/go-spew"
|
||||
packages = ["spew"]
|
||||
@ -371,6 +377,6 @@
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "db970de67fb6d82511e2fb991860473786678b3c1dfc4fb835046fafbaa61f57"
|
||||
inputs-digest = "0392e2659013ae00be46cd4f0b9d56aa0765b6297d672a56175249c465e98a8d"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
@ -90,4 +90,8 @@ ignored = ["github.com/vmware/harbor/tests*"]
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/ghodss/yaml"
|
||||
version = "=1.0.0"
|
||||
version = "=1.0.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/blang/semver"
|
||||
version = "=3.5.1"
|
@ -1,12 +1,182 @@
|
||||
package chartserver
|
||||
|
||||
import "k8s.io/helm/pkg/proto/hapi/chart"
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/Masterminds/semver"
|
||||
|
||||
"k8s.io/helm/pkg/chartutil"
|
||||
helm_repo "k8s.io/helm/pkg/repo"
|
||||
)
|
||||
|
||||
const (
|
||||
readmeFileName = "README.md"
|
||||
)
|
||||
|
||||
//ChartVersionDetails keeps the detailed data info of the chart version
|
||||
type ChartVersionDetails struct {
|
||||
Metadata *helm_repo.ChartVersion `json:"metadata"`
|
||||
Dependencies []*chartutil.Dependency `json:"dependencies"`
|
||||
Values map[string]interface{} `json:"values"`
|
||||
Files map[string]string `json:"files"`
|
||||
}
|
||||
|
||||
//ChartInfo keeps the information of the chart
|
||||
type ChartInfo struct {
|
||||
Name string
|
||||
TotalVersions uint32 `json:"total_versions"`
|
||||
Created time.Time
|
||||
Icon string
|
||||
Home string
|
||||
}
|
||||
|
||||
//ChartOperator is designed to process the contents of
|
||||
//the specified chart version to get more details
|
||||
type ChartOperator struct{}
|
||||
|
||||
//GetChartDetails parse the details from the provided content bytes
|
||||
func (cho *ChartOperator) GetChartDetails(content []byte) *chart.Chart {
|
||||
return nil
|
||||
func (cho *ChartOperator) GetChartDetails(content []byte) (*ChartVersionDetails, error) {
|
||||
if content == nil || len(content) == 0 {
|
||||
return nil, errors.New("zero content")
|
||||
}
|
||||
|
||||
//Load chart from in-memory content
|
||||
reader := bytes.NewReader(content)
|
||||
chartData, err := chartutil.LoadArchive(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//Parse the requirements of chart
|
||||
requirements, err := chartutil.LoadRequirements(chartData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var values map[string]interface{}
|
||||
files := make(map[string]string)
|
||||
//Parse values
|
||||
if chartData.Values != nil {
|
||||
values = parseRawValues([]byte(chartData.Values.GetRaw()))
|
||||
//Append values.yaml file
|
||||
files["values.yaml"] = chartData.Values.Raw
|
||||
}
|
||||
|
||||
//Append other files like 'README.md'
|
||||
for _, v := range chartData.GetFiles() {
|
||||
if v.TypeUrl == readmeFileName {
|
||||
files[readmeFileName] = string(v.GetValue())
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
theChart := &ChartVersionDetails{
|
||||
Dependencies: requirements.Dependencies,
|
||||
Values: values,
|
||||
Files: files,
|
||||
}
|
||||
|
||||
return theChart, nil
|
||||
}
|
||||
|
||||
//GetChartList returns a reorganized chart list
|
||||
func (cho *ChartOperator) GetChartList(content []byte) ([]*ChartInfo, error) {
|
||||
if content == nil || len(content) == 0 {
|
||||
return nil, errors.New("zero content")
|
||||
}
|
||||
|
||||
allCharts := make(map[string]helm_repo.ChartVersions)
|
||||
if err := json.Unmarshal(content, &allCharts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
chartList := make([]*ChartInfo, 0)
|
||||
for key, chartVersions := range allCharts {
|
||||
lVersion, oVersion := getTheTwoCharts(chartVersions)
|
||||
if lVersion != nil && oVersion != nil {
|
||||
chartInfo := &ChartInfo{
|
||||
Name: key,
|
||||
TotalVersions: uint32(len(chartVersions)),
|
||||
}
|
||||
chartInfo.Created = oVersion.Created
|
||||
chartInfo.Home = lVersion.Home
|
||||
chartInfo.Icon = lVersion.Icon
|
||||
chartList = append(chartList, chartInfo)
|
||||
}
|
||||
}
|
||||
|
||||
return chartList, nil
|
||||
}
|
||||
|
||||
//Get the latest and oldest chart versions
|
||||
func getTheTwoCharts(chartVersions helm_repo.ChartVersions) (latestChart *helm_repo.ChartVersion, oldestChart *helm_repo.ChartVersion) {
|
||||
if len(chartVersions) == 1 {
|
||||
return chartVersions[0], chartVersions[0]
|
||||
}
|
||||
|
||||
for _, chartVersion := range chartVersions {
|
||||
currentV, err := semver.NewVersion(chartVersion.Version)
|
||||
if err != nil {
|
||||
//ignore it
|
||||
continue
|
||||
}
|
||||
|
||||
//Find latest chart
|
||||
if latestChart == nil {
|
||||
latestChart = chartVersion
|
||||
} else {
|
||||
lVersion, _ := semver.NewVersion(latestChart.Version)
|
||||
if lVersion.LessThan(currentV) {
|
||||
latestChart = chartVersion
|
||||
}
|
||||
}
|
||||
|
||||
if oldestChart == nil {
|
||||
oldestChart = chartVersion
|
||||
} else {
|
||||
if oldestChart.Created.After(chartVersion.Created) {
|
||||
oldestChart = chartVersion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return latestChart, oldestChart
|
||||
}
|
||||
|
||||
//Parse the raw values to value map
|
||||
func parseRawValues(rawValue []byte) map[string]interface{} {
|
||||
valueMap := make(map[string]interface{})
|
||||
|
||||
if len(rawValue) == 0 {
|
||||
return valueMap
|
||||
}
|
||||
|
||||
values, err := chartutil.ReadValues(rawValue)
|
||||
if err != nil || len(values) == 0 {
|
||||
return valueMap
|
||||
}
|
||||
|
||||
readValue(values, "", valueMap)
|
||||
|
||||
return valueMap
|
||||
}
|
||||
|
||||
//Recursively read value
|
||||
func readValue(values map[string]interface{}, keyPrefix string, valueMap map[string]interface{}) {
|
||||
for key, value := range values {
|
||||
longKey := key
|
||||
if keyPrefix != "" {
|
||||
longKey = fmt.Sprintf("%s.%s", keyPrefix, key)
|
||||
}
|
||||
|
||||
if subValues, ok := value.(map[string]interface{}); ok {
|
||||
readValue(subValues, longKey, valueMap)
|
||||
} else {
|
||||
valueMap[longKey] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
67
src/chartserver/client.go
Normal file
67
src/chartserver/client.go
Normal file
@ -0,0 +1,67 @@
|
||||
package chartserver
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
clientTimeout = 10 * time.Second
|
||||
maxIdleConnections = 10
|
||||
idleConnectionTimeout = 30 * time.Second
|
||||
)
|
||||
|
||||
//ChartClient is a http client to get the content from the external http server
|
||||
type ChartClient struct {
|
||||
httpClient *http.Client
|
||||
}
|
||||
|
||||
//NewChartClient is constructor of ChartClient
|
||||
func NewChartClient() *ChartClient { //Create http client with customized timeouts
|
||||
client := &http.Client{
|
||||
Timeout: clientTimeout,
|
||||
Transport: &http.Transport{
|
||||
MaxIdleConns: maxIdleConnections,
|
||||
IdleConnTimeout: idleConnectionTimeout,
|
||||
},
|
||||
}
|
||||
|
||||
return &ChartClient{client}
|
||||
}
|
||||
|
||||
//GetContent get the bytes from the specified url
|
||||
func (cc *ChartClient) GetContent(url string) ([]byte, error) {
|
||||
if len(strings.TrimSpace(url)) == 0 {
|
||||
return nil, errors.New("empty url is not allowed")
|
||||
}
|
||||
|
||||
request, err := http.NewRequest(http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//Set basic auth
|
||||
request.SetBasicAuth(userName, os.Getenv(passwordKey))
|
||||
|
||||
response, err := cc.httpClient.Do(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if response.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("failed to retrieve content from url %s", url)
|
||||
}
|
||||
|
||||
content, err := ioutil.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
return content, nil
|
||||
}
|
@ -2,15 +2,7 @@ package chartserver
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
clientTimeout = 10 * time.Second
|
||||
maxIdleConnections = 10
|
||||
idleConnectionTimeout = 30 * time.Second
|
||||
)
|
||||
|
||||
//Controller is used to handle flows of related requests based on the corresponding handlers
|
||||
@ -40,13 +32,7 @@ func NewController(backendServer *url.URL) (*Controller, error) {
|
||||
proxy := NewProxyEngine(backendServer)
|
||||
|
||||
//Create http client with customized timeouts
|
||||
client := &http.Client{
|
||||
Timeout: clientTimeout,
|
||||
Transport: &http.Transport{
|
||||
MaxIdleConns: maxIdleConnections,
|
||||
IdleConnTimeout: idleConnectionTimeout,
|
||||
},
|
||||
}
|
||||
client := NewChartClient()
|
||||
|
||||
//Initialize chart operator for use
|
||||
operator := &ChartOperator{}
|
||||
@ -60,8 +46,10 @@ func NewController(backendServer *url.URL) (*Controller, error) {
|
||||
backendServerAddress: backendServer,
|
||||
},
|
||||
manipulationHandler: &ManipulationHandler{
|
||||
trafficProxy: proxy,
|
||||
chartOperator: operator,
|
||||
trafficProxy: proxy,
|
||||
chartOperator: operator,
|
||||
apiClient: client,
|
||||
backendServerAddress: backendServer,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
@ -1,7 +1,14 @@
|
||||
package chartserver
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
helm_repo "k8s.io/helm/pkg/repo"
|
||||
)
|
||||
|
||||
//ManipulationHandler includes all the handler methods for the purpose of manipulating the
|
||||
@ -13,11 +20,38 @@ type ManipulationHandler struct {
|
||||
|
||||
//Parse and process the chart version to provide required info data
|
||||
chartOperator *ChartOperator
|
||||
|
||||
//HTTP client used to call the realted APIs of the backend chart repositories
|
||||
apiClient *ChartClient
|
||||
|
||||
//Point to the url of the backend server
|
||||
backendServerAddress *url.URL
|
||||
}
|
||||
|
||||
//ListCharts lists all the charts under the specified namespace
|
||||
func (mh *ManipulationHandler) ListCharts(w http.ResponseWriter, req *http.Request) {
|
||||
mh.trafficProxy.ServeHTTP(w, req)
|
||||
rootURL := strings.TrimSuffix(mh.backendServerAddress.String(), "/")
|
||||
fullURL := fmt.Sprintf("%s%s", rootURL, req.RequestURI)
|
||||
|
||||
content, err := mh.apiClient.GetContent(fullURL)
|
||||
if err != nil {
|
||||
writeInternalError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
chartList, err := mh.chartOperator.GetChartList(content)
|
||||
if err != nil {
|
||||
writeInternalError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
jsonData, err := json.Marshal(chartList)
|
||||
if err != nil {
|
||||
writeInternalError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
writeJSONData(w, jsonData)
|
||||
}
|
||||
|
||||
//GetChart returns all the chart versions under the specified chart
|
||||
@ -29,8 +63,35 @@ func (mh *ManipulationHandler) GetChart(w http.ResponseWriter, req *http.Request
|
||||
//This handler should return the details of the chart version,
|
||||
//maybe including metadata,dependencies and values etc.
|
||||
func (mh *ManipulationHandler) GetChartVersion(w http.ResponseWriter, req *http.Request) {
|
||||
w.WriteHeader(http.StatusNotImplemented)
|
||||
w.Write([]byte("not implemented"))
|
||||
chartV, err := mh.getChartVersion(req.RequestURI)
|
||||
if err != nil {
|
||||
writeInternalError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
//TODO:
|
||||
namespace := "repo2"
|
||||
content, err := mh.getChartVersionContent(namespace, chartV.URLs[0])
|
||||
if err != nil {
|
||||
writeInternalError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
//Process bytes and get more details of chart version
|
||||
chartDetails, err := mh.chartOperator.GetChartDetails(content)
|
||||
if err != nil {
|
||||
writeInternalError(w, err)
|
||||
return
|
||||
}
|
||||
chartDetails.Metadata = chartV
|
||||
|
||||
bytes, err := json.Marshal(chartDetails)
|
||||
if err != nil {
|
||||
writeInternalError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
writeJSONData(w, bytes)
|
||||
}
|
||||
|
||||
//UploadChartVersion will save the new version of the chart to the backend storage
|
||||
@ -47,3 +108,29 @@ func (mh *ManipulationHandler) UploadProvenanceFile(w http.ResponseWriter, req *
|
||||
func (mh *ManipulationHandler) DeleteChartVersion(w http.ResponseWriter, req *http.Request) {
|
||||
mh.trafficProxy.ServeHTTP(w, req)
|
||||
}
|
||||
|
||||
//Get the basic metadata of chart version
|
||||
func (mh *ManipulationHandler) getChartVersion(path string) (*helm_repo.ChartVersion, error) {
|
||||
rootURL := strings.TrimSuffix(mh.backendServerAddress.String(), "/")
|
||||
fullURL := fmt.Sprintf("%s%s", rootURL, path)
|
||||
|
||||
content, err := mh.apiClient.GetContent(fullURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
chartVersion := &helm_repo.ChartVersion{}
|
||||
if err := yaml.Unmarshal(content, chartVersion); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return chartVersion, nil
|
||||
}
|
||||
|
||||
//Get the content bytes of the chart version
|
||||
func (mh *ManipulationHandler) getChartVersionContent(namespace string, path string) ([]byte, error) {
|
||||
rootURL := strings.TrimSuffix(mh.backendServerAddress.String(), "/")
|
||||
fullPath := fmt.Sprintf("%s/%s/%s", rootURL, namespace, path)
|
||||
|
||||
return mh.apiClient.GetContent(fullPath)
|
||||
}
|
||||
|
@ -4,10 +4,8 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
@ -30,7 +28,7 @@ type RepositoryHandler struct {
|
||||
trafficProxy *ProxyEngine
|
||||
|
||||
//HTTP client used to call the realted APIs of the backend chart repositories
|
||||
apiClient *http.Client
|
||||
apiClient *ChartClient
|
||||
|
||||
//Point to the url of the backend server
|
||||
backendServerAddress *url.URL
|
||||
@ -60,14 +58,14 @@ func (rh *RepositoryHandler) GetIndexFile(w http.ResponseWriter, req *http.Reque
|
||||
//Get project manager references
|
||||
projectMgr, err := filter.GetProjectManager(req)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, err)
|
||||
writeInternalError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
//Get all the projects
|
||||
results, err := projectMgr.List(nil)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, err)
|
||||
writeInternalError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
@ -161,7 +159,7 @@ LOOP:
|
||||
writeError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
case <-req.Context().Done():
|
||||
writeError(w, http.StatusInternalServerError, errors.New("request aborted"))
|
||||
writeInternalError(w, errors.New("request aborted"))
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -187,7 +185,7 @@ LOOP:
|
||||
|
||||
bytes, err := yaml.Marshal(mergedIndexFile)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, err)
|
||||
writeInternalError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
@ -206,29 +204,12 @@ func (rh *RepositoryHandler) getIndexYamlWithNS(namespace string) (*helm_repo.In
|
||||
rootURL := strings.TrimSuffix(rh.backendServerAddress.String(), "/")
|
||||
url := fmt.Sprintf("%s/%s/index.yaml", rootURL, namespace)
|
||||
|
||||
request, err := http.NewRequest(http.MethodGet, url, nil)
|
||||
content, err := rh.apiClient.GetContent(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//Set basic auth
|
||||
request.SetBasicAuth(userName, os.Getenv(passwordKey))
|
||||
|
||||
response, err := rh.apiClient.Do(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if response.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("failed to retrieve the index.yaml under repo %s", namespace)
|
||||
}
|
||||
|
||||
content, err := ioutil.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
//Traverse to index file object for merging
|
||||
indexFile := helm_repo.NewIndexFile()
|
||||
if err := yaml.Unmarshal(content, indexFile); err != nil {
|
||||
return nil, err
|
||||
@ -276,12 +257,3 @@ func emptyIndexFile() []byte {
|
||||
|
||||
return rawData
|
||||
}
|
||||
|
||||
func writeError(w http.ResponseWriter, code int, err error) {
|
||||
errorObj := make(map[string]string)
|
||||
errorObj["error"] = err.Error()
|
||||
errorContent, _ := json.Marshal(errorObj)
|
||||
|
||||
w.WriteHeader(code)
|
||||
w.Write(errorContent)
|
||||
}
|
||||
|
33
src/chartserver/utils.go
Normal file
33
src/chartserver/utils.go
Normal file
@ -0,0 +1,33 @@
|
||||
package chartserver
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const (
|
||||
contentTypeHeader = "content-type"
|
||||
contentTypeJSON = "application/json"
|
||||
)
|
||||
|
||||
//Write error to http client
|
||||
func writeError(w http.ResponseWriter, code int, err error) {
|
||||
errorObj := make(map[string]string)
|
||||
errorObj["error"] = err.Error()
|
||||
errorContent, _ := json.Marshal(errorObj)
|
||||
|
||||
w.WriteHeader(code)
|
||||
w.Write(errorContent)
|
||||
}
|
||||
|
||||
//StatusCode == 500
|
||||
func writeInternalError(w http.ResponseWriter, err error) {
|
||||
writeError(w, http.StatusInternalServerError, err)
|
||||
}
|
||||
|
||||
//Write JSON data to http client
|
||||
func writeJSONData(w http.ResponseWriter, data []byte) {
|
||||
w.Header().Set(contentTypeHeader, contentTypeJSON)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(data)
|
||||
}
|
1
src/vendor/github.com/blang/semver/.gx/lastpubver
generated
vendored
Normal file
1
src/vendor/github.com/blang/semver/.gx/lastpubver
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
3.5.1: QmYRGECuvQnRX73fcvPnGbYijBcGN2HbKZQ7jh26qmLiHG
|
21
src/vendor/github.com/blang/semver/.travis.yml
generated
vendored
Normal file
21
src/vendor/github.com/blang/semver/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
language: go
|
||||
matrix:
|
||||
include:
|
||||
- go: 1.4.3
|
||||
- go: 1.5.4
|
||||
- go: 1.6.3
|
||||
- go: 1.7
|
||||
- go: tip
|
||||
allow_failures:
|
||||
- go: tip
|
||||
install:
|
||||
- go get golang.org/x/tools/cmd/cover
|
||||
- go get github.com/mattn/goveralls
|
||||
script:
|
||||
- echo "Test and track coverage" ; $HOME/gopath/bin/goveralls -package "." -service=travis-ci
|
||||
-repotoken $COVERALLS_TOKEN
|
||||
- echo "Build examples" ; cd examples && go build
|
||||
- echo "Check if gofmt'd" ; diff -u <(echo -n) <(gofmt -d -s .)
|
||||
env:
|
||||
global:
|
||||
secure: HroGEAUQpVq9zX1b1VIkraLiywhGbzvNnTZq2TMxgK7JHP8xqNplAeF1izrR2i4QLL9nsY+9WtYss4QuPvEtZcVHUobw6XnL6radF7jS1LgfYZ9Y7oF+zogZ2I5QUMRLGA7rcxQ05s7mKq3XZQfeqaNts4bms/eZRefWuaFZbkw=
|
22
src/vendor/github.com/blang/semver/LICENSE
generated
vendored
Normal file
22
src/vendor/github.com/blang/semver/LICENSE
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2014 Benedikt Lang <github at benediktlang.de>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
194
src/vendor/github.com/blang/semver/README.md
generated
vendored
Normal file
194
src/vendor/github.com/blang/semver/README.md
generated
vendored
Normal file
@ -0,0 +1,194 @@
|
||||
semver for golang [![Build Status](https://travis-ci.org/blang/semver.svg?branch=master)](https://travis-ci.org/blang/semver) [![GoDoc](https://godoc.org/github.com/blang/semver?status.png)](https://godoc.org/github.com/blang/semver) [![Coverage Status](https://img.shields.io/coveralls/blang/semver.svg)](https://coveralls.io/r/blang/semver?branch=master)
|
||||
======
|
||||
|
||||
semver is a [Semantic Versioning](http://semver.org/) library written in golang. It fully covers spec version `2.0.0`.
|
||||
|
||||
Usage
|
||||
-----
|
||||
```bash
|
||||
$ go get github.com/blang/semver
|
||||
```
|
||||
Note: Always vendor your dependencies or fix on a specific version tag.
|
||||
|
||||
```go
|
||||
import github.com/blang/semver
|
||||
v1, err := semver.Make("1.0.0-beta")
|
||||
v2, err := semver.Make("2.0.0-beta")
|
||||
v1.Compare(v2)
|
||||
```
|
||||
|
||||
Also check the [GoDocs](http://godoc.org/github.com/blang/semver).
|
||||
|
||||
Why should I use this lib?
|
||||
-----
|
||||
|
||||
- Fully spec compatible
|
||||
- No reflection
|
||||
- No regex
|
||||
- Fully tested (Coverage >99%)
|
||||
- Readable parsing/validation errors
|
||||
- Fast (See [Benchmarks](#benchmarks))
|
||||
- Only Stdlib
|
||||
- Uses values instead of pointers
|
||||
- Many features, see below
|
||||
|
||||
|
||||
Features
|
||||
-----
|
||||
|
||||
- Parsing and validation at all levels
|
||||
- Comparator-like comparisons
|
||||
- Compare Helper Methods
|
||||
- InPlace manipulation
|
||||
- Ranges `>=1.0.0 <2.0.0 || >=3.0.0 !3.0.1-beta.1`
|
||||
- Wildcards `>=1.x`, `<=2.5.x`
|
||||
- Sortable (implements sort.Interface)
|
||||
- database/sql compatible (sql.Scanner/Valuer)
|
||||
- encoding/json compatible (json.Marshaler/Unmarshaler)
|
||||
|
||||
Ranges
|
||||
------
|
||||
|
||||
A `Range` is a set of conditions which specify which versions satisfy the range.
|
||||
|
||||
A condition is composed of an operator and a version. The supported operators are:
|
||||
|
||||
- `<1.0.0` Less than `1.0.0`
|
||||
- `<=1.0.0` Less than or equal to `1.0.0`
|
||||
- `>1.0.0` Greater than `1.0.0`
|
||||
- `>=1.0.0` Greater than or equal to `1.0.0`
|
||||
- `1.0.0`, `=1.0.0`, `==1.0.0` Equal to `1.0.0`
|
||||
- `!1.0.0`, `!=1.0.0` Not equal to `1.0.0`. Excludes version `1.0.0`.
|
||||
|
||||
Note that spaces between the operator and the version will be gracefully tolerated.
|
||||
|
||||
A `Range` can link multiple `Ranges` separated by space:
|
||||
|
||||
Ranges can be linked by logical AND:
|
||||
|
||||
- `>1.0.0 <2.0.0` would match between both ranges, so `1.1.1` and `1.8.7` but not `1.0.0` or `2.0.0`
|
||||
- `>1.0.0 <3.0.0 !2.0.3-beta.2` would match every version between `1.0.0` and `3.0.0` except `2.0.3-beta.2`
|
||||
|
||||
Ranges can also be linked by logical OR:
|
||||
|
||||
- `<2.0.0 || >=3.0.0` would match `1.x.x` and `3.x.x` but not `2.x.x`
|
||||
|
||||
AND has a higher precedence than OR. It's not possible to use brackets.
|
||||
|
||||
Ranges can be combined by both AND and OR
|
||||
|
||||
- `>1.0.0 <2.0.0 || >3.0.0 !4.2.1` would match `1.2.3`, `1.9.9`, `3.1.1`, but not `4.2.1`, `2.1.1`
|
||||
|
||||
Range usage:
|
||||
|
||||
```
|
||||
v, err := semver.Parse("1.2.3")
|
||||
range, err := semver.ParseRange(">1.0.0 <2.0.0 || >=3.0.0")
|
||||
if range(v) {
|
||||
//valid
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Example
|
||||
-----
|
||||
|
||||
Have a look at full examples in [examples/main.go](examples/main.go)
|
||||
|
||||
```go
|
||||
import github.com/blang/semver
|
||||
|
||||
v, err := semver.Make("0.0.1-alpha.preview+123.github")
|
||||
fmt.Printf("Major: %d\n", v.Major)
|
||||
fmt.Printf("Minor: %d\n", v.Minor)
|
||||
fmt.Printf("Patch: %d\n", v.Patch)
|
||||
fmt.Printf("Pre: %s\n", v.Pre)
|
||||
fmt.Printf("Build: %s\n", v.Build)
|
||||
|
||||
// Prerelease versions array
|
||||
if len(v.Pre) > 0 {
|
||||
fmt.Println("Prerelease versions:")
|
||||
for i, pre := range v.Pre {
|
||||
fmt.Printf("%d: %q\n", i, pre)
|
||||
}
|
||||
}
|
||||
|
||||
// Build meta data array
|
||||
if len(v.Build) > 0 {
|
||||
fmt.Println("Build meta data:")
|
||||
for i, build := range v.Build {
|
||||
fmt.Printf("%d: %q\n", i, build)
|
||||
}
|
||||
}
|
||||
|
||||
v001, err := semver.Make("0.0.1")
|
||||
// Compare using helpers: v.GT(v2), v.LT, v.GTE, v.LTE
|
||||
v001.GT(v) == true
|
||||
v.LT(v001) == true
|
||||
v.GTE(v) == true
|
||||
v.LTE(v) == true
|
||||
|
||||
// Or use v.Compare(v2) for comparisons (-1, 0, 1):
|
||||
v001.Compare(v) == 1
|
||||
v.Compare(v001) == -1
|
||||
v.Compare(v) == 0
|
||||
|
||||
// Manipulate Version in place:
|
||||
v.Pre[0], err = semver.NewPRVersion("beta")
|
||||
if err != nil {
|
||||
fmt.Printf("Error parsing pre release version: %q", err)
|
||||
}
|
||||
|
||||
fmt.Println("\nValidate versions:")
|
||||
v.Build[0] = "?"
|
||||
|
||||
err = v.Validate()
|
||||
if err != nil {
|
||||
fmt.Printf("Validation failed: %s\n", err)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Benchmarks
|
||||
-----
|
||||
|
||||
BenchmarkParseSimple-4 5000000 390 ns/op 48 B/op 1 allocs/op
|
||||
BenchmarkParseComplex-4 1000000 1813 ns/op 256 B/op 7 allocs/op
|
||||
BenchmarkParseAverage-4 1000000 1171 ns/op 163 B/op 4 allocs/op
|
||||
BenchmarkStringSimple-4 20000000 119 ns/op 16 B/op 1 allocs/op
|
||||
BenchmarkStringLarger-4 10000000 206 ns/op 32 B/op 2 allocs/op
|
||||
BenchmarkStringComplex-4 5000000 324 ns/op 80 B/op 3 allocs/op
|
||||
BenchmarkStringAverage-4 5000000 273 ns/op 53 B/op 2 allocs/op
|
||||
BenchmarkValidateSimple-4 200000000 9.33 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkValidateComplex-4 3000000 469 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkValidateAverage-4 5000000 256 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkCompareSimple-4 100000000 11.8 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkCompareComplex-4 50000000 30.8 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkCompareAverage-4 30000000 41.5 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkSort-4 3000000 419 ns/op 256 B/op 2 allocs/op
|
||||
BenchmarkRangeParseSimple-4 2000000 850 ns/op 192 B/op 5 allocs/op
|
||||
BenchmarkRangeParseAverage-4 1000000 1677 ns/op 400 B/op 10 allocs/op
|
||||
BenchmarkRangeParseComplex-4 300000 5214 ns/op 1440 B/op 30 allocs/op
|
||||
BenchmarkRangeMatchSimple-4 50000000 25.6 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkRangeMatchAverage-4 30000000 56.4 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkRangeMatchComplex-4 10000000 153 ns/op 0 B/op 0 allocs/op
|
||||
|
||||
See benchmark cases at [semver_test.go](semver_test.go)
|
||||
|
||||
|
||||
Motivation
|
||||
-----
|
||||
|
||||
I simply couldn't find any lib supporting the full spec. Others were just wrong or used reflection and regex which i don't like.
|
||||
|
||||
|
||||
Contribution
|
||||
-----
|
||||
|
||||
Feel free to make a pull request. For bigger changes create a issue first to discuss about it.
|
||||
|
||||
|
||||
License
|
||||
-----
|
||||
|
||||
See [LICENSE](LICENSE) file.
|
83
src/vendor/github.com/blang/semver/examples/main.go
generated
vendored
Normal file
83
src/vendor/github.com/blang/semver/examples/main.go
generated
vendored
Normal file
@ -0,0 +1,83 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/blang/semver"
|
||||
)
|
||||
|
||||
func main() {
|
||||
v, err := semver.Parse("0.0.1-alpha.preview.222+123.github")
|
||||
if err != nil {
|
||||
fmt.Printf("Error while parsing (not valid): %q", err)
|
||||
}
|
||||
fmt.Printf("Version to string: %q\n", v)
|
||||
|
||||
fmt.Printf("Major: %d\n", v.Major)
|
||||
fmt.Printf("Minor: %d\n", v.Minor)
|
||||
fmt.Printf("Patch: %d\n", v.Patch)
|
||||
|
||||
// Prerelease versions
|
||||
if len(v.Pre) > 0 {
|
||||
fmt.Println("Prerelease versions:")
|
||||
for i, pre := range v.Pre {
|
||||
fmt.Printf("%d: %q\n", i, pre)
|
||||
}
|
||||
}
|
||||
|
||||
// Build meta data
|
||||
if len(v.Build) > 0 {
|
||||
fmt.Println("Build meta data:")
|
||||
for i, build := range v.Build {
|
||||
fmt.Printf("%d: %q\n", i, build)
|
||||
}
|
||||
}
|
||||
|
||||
// Make == Parse (Value), New for Pointer
|
||||
v001, err := semver.Make("0.0.1")
|
||||
|
||||
fmt.Println("\nUse Version.Compare for comparisons (-1, 0, 1):")
|
||||
fmt.Printf("%q is greater than %q: Compare == %d\n", v001, v, v001.Compare(v))
|
||||
fmt.Printf("%q is less than %q: Compare == %d\n", v, v001, v.Compare(v001))
|
||||
fmt.Printf("%q is equal to %q: Compare == %d\n", v, v, v.Compare(v))
|
||||
|
||||
fmt.Println("\nUse comparison helpers returning booleans:")
|
||||
fmt.Printf("%q is greater than %q: %t\n", v001, v, v001.GT(v))
|
||||
fmt.Printf("%q is greater than equal %q: %t\n", v001, v, v001.GTE(v))
|
||||
fmt.Printf("%q is greater than equal %q: %t\n", v, v, v.GTE(v))
|
||||
fmt.Printf("%q is less than %q: %t\n", v, v001, v.LT(v001))
|
||||
fmt.Printf("%q is less than equal %q: %t\n", v, v001, v.LTE(v001))
|
||||
fmt.Printf("%q is less than equal %q: %t\n", v, v, v.LTE(v))
|
||||
|
||||
fmt.Println("\nManipulate Version in place:")
|
||||
v.Pre[0], err = semver.NewPRVersion("beta")
|
||||
if err != nil {
|
||||
fmt.Printf("Error parsing pre release version: %q", err)
|
||||
}
|
||||
fmt.Printf("Version to string: %q\n", v)
|
||||
|
||||
fmt.Println("\nCompare Prerelease versions:")
|
||||
pre1, _ := semver.NewPRVersion("123")
|
||||
pre2, _ := semver.NewPRVersion("alpha")
|
||||
pre3, _ := semver.NewPRVersion("124")
|
||||
fmt.Printf("%q is less than %q: Compare == %d\n", pre1, pre2, pre1.Compare(pre2))
|
||||
fmt.Printf("%q is greater than %q: Compare == %d\n", pre3, pre1, pre3.Compare(pre1))
|
||||
fmt.Printf("%q is equal to %q: Compare == %d\n", pre1, pre1, pre1.Compare(pre1))
|
||||
|
||||
fmt.Println("\nValidate versions:")
|
||||
v.Build[0] = "?"
|
||||
|
||||
err = v.Validate()
|
||||
if err != nil {
|
||||
fmt.Printf("Validation failed: %s\n", err)
|
||||
}
|
||||
|
||||
fmt.Println("Create valid build meta data:")
|
||||
b1, _ := semver.NewBuildVersion("build123")
|
||||
v.Build[0] = b1
|
||||
fmt.Printf("Version with new build version %q\n", v)
|
||||
|
||||
_, err = semver.NewBuildVersion("build?123")
|
||||
if err != nil {
|
||||
fmt.Printf("Create build version failed: %s\n", err)
|
||||
}
|
||||
}
|
23
src/vendor/github.com/blang/semver/json.go
generated
vendored
Normal file
23
src/vendor/github.com/blang/semver/json.go
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
package semver
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// MarshalJSON implements the encoding/json.Marshaler interface.
|
||||
func (v Version) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(v.String())
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the encoding/json.Unmarshaler interface.
|
||||
func (v *Version) UnmarshalJSON(data []byte) (err error) {
|
||||
var versionString string
|
||||
|
||||
if err = json.Unmarshal(data, &versionString); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
*v, err = Parse(versionString)
|
||||
|
||||
return
|
||||
}
|
49
src/vendor/github.com/blang/semver/json_test.go
generated
vendored
Normal file
49
src/vendor/github.com/blang/semver/json_test.go
generated
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
package semver
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestJSONMarshal(t *testing.T) {
|
||||
versionString := "3.1.4-alpha.1.5.9+build.2.6.5"
|
||||
v, err := Parse(versionString)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
versionJSON, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
quotedVersionString := strconv.Quote(versionString)
|
||||
|
||||
if string(versionJSON) != quotedVersionString {
|
||||
t.Fatalf("JSON marshaled semantic version not equal: expected %q, got %q", quotedVersionString, string(versionJSON))
|
||||
}
|
||||
}
|
||||
|
||||
func TestJSONUnmarshal(t *testing.T) {
|
||||
versionString := "3.1.4-alpha.1.5.9+build.2.6.5"
|
||||
quotedVersionString := strconv.Quote(versionString)
|
||||
|
||||
var v Version
|
||||
if err := json.Unmarshal([]byte(quotedVersionString), &v); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if v.String() != versionString {
|
||||
t.Fatalf("JSON unmarshaled semantic version not equal: expected %q, got %q", versionString, v.String())
|
||||
}
|
||||
|
||||
badVersionString := strconv.Quote("3.1.4.1.5.9.2.6.5-other-digits-of-pi")
|
||||
if err := json.Unmarshal([]byte(badVersionString), &v); err == nil {
|
||||
t.Fatal("expected JSON unmarshal error, got nil")
|
||||
}
|
||||
|
||||
if err := json.Unmarshal([]byte("3.1"), &v); err == nil {
|
||||
t.Fatal("expected JSON unmarshal error, got nil")
|
||||
}
|
||||
}
|
17
src/vendor/github.com/blang/semver/package.json
generated
vendored
Normal file
17
src/vendor/github.com/blang/semver/package.json
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"author": "blang",
|
||||
"bugs": {
|
||||
"URL": "https://github.com/blang/semver/issues",
|
||||
"url": "https://github.com/blang/semver/issues"
|
||||
},
|
||||
"gx": {
|
||||
"dvcsimport": "github.com/blang/semver"
|
||||
},
|
||||
"gxVersion": "0.10.0",
|
||||
"language": "go",
|
||||
"license": "MIT",
|
||||
"name": "semver",
|
||||
"releaseCmd": "git commit -a -m \"gx publish $VERSION\"",
|
||||
"version": "3.5.1"
|
||||
}
|
||||
|
416
src/vendor/github.com/blang/semver/range.go
generated
vendored
Normal file
416
src/vendor/github.com/blang/semver/range.go
generated
vendored
Normal file
@ -0,0 +1,416 @@
|
||||
package semver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
type wildcardType int
|
||||
|
||||
const (
|
||||
noneWildcard wildcardType = iota
|
||||
majorWildcard wildcardType = 1
|
||||
minorWildcard wildcardType = 2
|
||||
patchWildcard wildcardType = 3
|
||||
)
|
||||
|
||||
func wildcardTypefromInt(i int) wildcardType {
|
||||
switch i {
|
||||
case 1:
|
||||
return majorWildcard
|
||||
case 2:
|
||||
return minorWildcard
|
||||
case 3:
|
||||
return patchWildcard
|
||||
default:
|
||||
return noneWildcard
|
||||
}
|
||||
}
|
||||
|
||||
type comparator func(Version, Version) bool
|
||||
|
||||
var (
|
||||
compEQ comparator = func(v1 Version, v2 Version) bool {
|
||||
return v1.Compare(v2) == 0
|
||||
}
|
||||
compNE = func(v1 Version, v2 Version) bool {
|
||||
return v1.Compare(v2) != 0
|
||||
}
|
||||
compGT = func(v1 Version, v2 Version) bool {
|
||||
return v1.Compare(v2) == 1
|
||||
}
|
||||
compGE = func(v1 Version, v2 Version) bool {
|
||||
return v1.Compare(v2) >= 0
|
||||
}
|
||||
compLT = func(v1 Version, v2 Version) bool {
|
||||
return v1.Compare(v2) == -1
|
||||
}
|
||||
compLE = func(v1 Version, v2 Version) bool {
|
||||
return v1.Compare(v2) <= 0
|
||||
}
|
||||
)
|
||||
|
||||
type versionRange struct {
|
||||
v Version
|
||||
c comparator
|
||||
}
|
||||
|
||||
// rangeFunc creates a Range from the given versionRange.
|
||||
func (vr *versionRange) rangeFunc() Range {
|
||||
return Range(func(v Version) bool {
|
||||
return vr.c(v, vr.v)
|
||||
})
|
||||
}
|
||||
|
||||
// Range represents a range of versions.
|
||||
// A Range can be used to check if a Version satisfies it:
|
||||
//
|
||||
// range, err := semver.ParseRange(">1.0.0 <2.0.0")
|
||||
// range(semver.MustParse("1.1.1") // returns true
|
||||
type Range func(Version) bool
|
||||
|
||||
// OR combines the existing Range with another Range using logical OR.
|
||||
func (rf Range) OR(f Range) Range {
|
||||
return Range(func(v Version) bool {
|
||||
return rf(v) || f(v)
|
||||
})
|
||||
}
|
||||
|
||||
// AND combines the existing Range with another Range using logical AND.
|
||||
func (rf Range) AND(f Range) Range {
|
||||
return Range(func(v Version) bool {
|
||||
return rf(v) && f(v)
|
||||
})
|
||||
}
|
||||
|
||||
// ParseRange parses a range and returns a Range.
|
||||
// If the range could not be parsed an error is returned.
|
||||
//
|
||||
// Valid ranges are:
|
||||
// - "<1.0.0"
|
||||
// - "<=1.0.0"
|
||||
// - ">1.0.0"
|
||||
// - ">=1.0.0"
|
||||
// - "1.0.0", "=1.0.0", "==1.0.0"
|
||||
// - "!1.0.0", "!=1.0.0"
|
||||
//
|
||||
// A Range can consist of multiple ranges separated by space:
|
||||
// Ranges can be linked by logical AND:
|
||||
// - ">1.0.0 <2.0.0" would match between both ranges, so "1.1.1" and "1.8.7" but not "1.0.0" or "2.0.0"
|
||||
// - ">1.0.0 <3.0.0 !2.0.3-beta.2" would match every version between 1.0.0 and 3.0.0 except 2.0.3-beta.2
|
||||
//
|
||||
// Ranges can also be linked by logical OR:
|
||||
// - "<2.0.0 || >=3.0.0" would match "1.x.x" and "3.x.x" but not "2.x.x"
|
||||
//
|
||||
// AND has a higher precedence than OR. It's not possible to use brackets.
|
||||
//
|
||||
// Ranges can be combined by both AND and OR
|
||||
//
|
||||
// - `>1.0.0 <2.0.0 || >3.0.0 !4.2.1` would match `1.2.3`, `1.9.9`, `3.1.1`, but not `4.2.1`, `2.1.1`
|
||||
func ParseRange(s string) (Range, error) {
|
||||
parts := splitAndTrim(s)
|
||||
orParts, err := splitORParts(parts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
expandedParts, err := expandWildcardVersion(orParts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var orFn Range
|
||||
for _, p := range expandedParts {
|
||||
var andFn Range
|
||||
for _, ap := range p {
|
||||
opStr, vStr, err := splitComparatorVersion(ap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vr, err := buildVersionRange(opStr, vStr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Could not parse Range %q: %s", ap, err)
|
||||
}
|
||||
rf := vr.rangeFunc()
|
||||
|
||||
// Set function
|
||||
if andFn == nil {
|
||||
andFn = rf
|
||||
} else { // Combine with existing function
|
||||
andFn = andFn.AND(rf)
|
||||
}
|
||||
}
|
||||
if orFn == nil {
|
||||
orFn = andFn
|
||||
} else {
|
||||
orFn = orFn.OR(andFn)
|
||||
}
|
||||
|
||||
}
|
||||
return orFn, nil
|
||||
}
|
||||
|
||||
// splitORParts splits the already cleaned parts by '||'.
|
||||
// Checks for invalid positions of the operator and returns an
|
||||
// error if found.
|
||||
func splitORParts(parts []string) ([][]string, error) {
|
||||
var ORparts [][]string
|
||||
last := 0
|
||||
for i, p := range parts {
|
||||
if p == "||" {
|
||||
if i == 0 {
|
||||
return nil, fmt.Errorf("First element in range is '||'")
|
||||
}
|
||||
ORparts = append(ORparts, parts[last:i])
|
||||
last = i + 1
|
||||
}
|
||||
}
|
||||
if last == len(parts) {
|
||||
return nil, fmt.Errorf("Last element in range is '||'")
|
||||
}
|
||||
ORparts = append(ORparts, parts[last:])
|
||||
return ORparts, nil
|
||||
}
|
||||
|
||||
// buildVersionRange takes a slice of 2: operator and version
|
||||
// and builds a versionRange, otherwise an error.
|
||||
func buildVersionRange(opStr, vStr string) (*versionRange, error) {
|
||||
c := parseComparator(opStr)
|
||||
if c == nil {
|
||||
return nil, fmt.Errorf("Could not parse comparator %q in %q", opStr, strings.Join([]string{opStr, vStr}, ""))
|
||||
}
|
||||
v, err := Parse(vStr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Could not parse version %q in %q: %s", vStr, strings.Join([]string{opStr, vStr}, ""), err)
|
||||
}
|
||||
|
||||
return &versionRange{
|
||||
v: v,
|
||||
c: c,
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
// inArray checks if a byte is contained in an array of bytes
|
||||
func inArray(s byte, list []byte) bool {
|
||||
for _, el := range list {
|
||||
if el == s {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// splitAndTrim splits a range string by spaces and cleans whitespaces
|
||||
func splitAndTrim(s string) (result []string) {
|
||||
last := 0
|
||||
var lastChar byte
|
||||
excludeFromSplit := []byte{'>', '<', '='}
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] == ' ' && !inArray(lastChar, excludeFromSplit) {
|
||||
if last < i-1 {
|
||||
result = append(result, s[last:i])
|
||||
}
|
||||
last = i + 1
|
||||
} else if s[i] != ' ' {
|
||||
lastChar = s[i]
|
||||
}
|
||||
}
|
||||
if last < len(s)-1 {
|
||||
result = append(result, s[last:])
|
||||
}
|
||||
|
||||
for i, v := range result {
|
||||
result[i] = strings.Replace(v, " ", "", -1)
|
||||
}
|
||||
|
||||
// parts := strings.Split(s, " ")
|
||||
// for _, x := range parts {
|
||||
// if s := strings.TrimSpace(x); len(s) != 0 {
|
||||
// result = append(result, s)
|
||||
// }
|
||||
// }
|
||||
return
|
||||
}
|
||||
|
||||
// splitComparatorVersion splits the comparator from the version.
|
||||
// Input must be free of leading or trailing spaces.
|
||||
func splitComparatorVersion(s string) (string, string, error) {
|
||||
i := strings.IndexFunc(s, unicode.IsDigit)
|
||||
if i == -1 {
|
||||
return "", "", fmt.Errorf("Could not get version from string: %q", s)
|
||||
}
|
||||
return strings.TrimSpace(s[0:i]), s[i:], nil
|
||||
}
|
||||
|
||||
// getWildcardType will return the type of wildcard that the
|
||||
// passed version contains
|
||||
func getWildcardType(vStr string) wildcardType {
|
||||
parts := strings.Split(vStr, ".")
|
||||
nparts := len(parts)
|
||||
wildcard := parts[nparts-1]
|
||||
|
||||
possibleWildcardType := wildcardTypefromInt(nparts)
|
||||
if wildcard == "x" {
|
||||
return possibleWildcardType
|
||||
}
|
||||
|
||||
return noneWildcard
|
||||
}
|
||||
|
||||
// createVersionFromWildcard will convert a wildcard version
|
||||
// into a regular version, replacing 'x's with '0's, handling
|
||||
// special cases like '1.x.x' and '1.x'
|
||||
func createVersionFromWildcard(vStr string) string {
|
||||
// handle 1.x.x
|
||||
vStr2 := strings.Replace(vStr, ".x.x", ".x", 1)
|
||||
vStr2 = strings.Replace(vStr2, ".x", ".0", 1)
|
||||
parts := strings.Split(vStr2, ".")
|
||||
|
||||
// handle 1.x
|
||||
if len(parts) == 2 {
|
||||
return vStr2 + ".0"
|
||||
}
|
||||
|
||||
return vStr2
|
||||
}
|
||||
|
||||
// incrementMajorVersion will increment the major version
|
||||
// of the passed version
|
||||
func incrementMajorVersion(vStr string) (string, error) {
|
||||
parts := strings.Split(vStr, ".")
|
||||
i, err := strconv.Atoi(parts[0])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
parts[0] = strconv.Itoa(i + 1)
|
||||
|
||||
return strings.Join(parts, "."), nil
|
||||
}
|
||||
|
||||
// incrementMajorVersion will increment the minor version
|
||||
// of the passed version
|
||||
func incrementMinorVersion(vStr string) (string, error) {
|
||||
parts := strings.Split(vStr, ".")
|
||||
i, err := strconv.Atoi(parts[1])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
parts[1] = strconv.Itoa(i + 1)
|
||||
|
||||
return strings.Join(parts, "."), nil
|
||||
}
|
||||
|
||||
// expandWildcardVersion will expand wildcards inside versions
|
||||
// following these rules:
|
||||
//
|
||||
// * when dealing with patch wildcards:
|
||||
// >= 1.2.x will become >= 1.2.0
|
||||
// <= 1.2.x will become < 1.3.0
|
||||
// > 1.2.x will become >= 1.3.0
|
||||
// < 1.2.x will become < 1.2.0
|
||||
// != 1.2.x will become < 1.2.0 >= 1.3.0
|
||||
//
|
||||
// * when dealing with minor wildcards:
|
||||
// >= 1.x will become >= 1.0.0
|
||||
// <= 1.x will become < 2.0.0
|
||||
// > 1.x will become >= 2.0.0
|
||||
// < 1.0 will become < 1.0.0
|
||||
// != 1.x will become < 1.0.0 >= 2.0.0
|
||||
//
|
||||
// * when dealing with wildcards without
|
||||
// version operator:
|
||||
// 1.2.x will become >= 1.2.0 < 1.3.0
|
||||
// 1.x will become >= 1.0.0 < 2.0.0
|
||||
func expandWildcardVersion(parts [][]string) ([][]string, error) {
|
||||
var expandedParts [][]string
|
||||
for _, p := range parts {
|
||||
var newParts []string
|
||||
for _, ap := range p {
|
||||
if strings.Index(ap, "x") != -1 {
|
||||
opStr, vStr, err := splitComparatorVersion(ap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
versionWildcardType := getWildcardType(vStr)
|
||||
flatVersion := createVersionFromWildcard(vStr)
|
||||
|
||||
var resultOperator string
|
||||
var shouldIncrementVersion bool
|
||||
switch opStr {
|
||||
case ">":
|
||||
resultOperator = ">="
|
||||
shouldIncrementVersion = true
|
||||
case ">=":
|
||||
resultOperator = ">="
|
||||
case "<":
|
||||
resultOperator = "<"
|
||||
case "<=":
|
||||
resultOperator = "<"
|
||||
shouldIncrementVersion = true
|
||||
case "", "=", "==":
|
||||
newParts = append(newParts, ">="+flatVersion)
|
||||
resultOperator = "<"
|
||||
shouldIncrementVersion = true
|
||||
case "!=", "!":
|
||||
newParts = append(newParts, "<"+flatVersion)
|
||||
resultOperator = ">="
|
||||
shouldIncrementVersion = true
|
||||
}
|
||||
|
||||
var resultVersion string
|
||||
if shouldIncrementVersion {
|
||||
switch versionWildcardType {
|
||||
case patchWildcard:
|
||||
resultVersion, _ = incrementMinorVersion(flatVersion)
|
||||
case minorWildcard:
|
||||
resultVersion, _ = incrementMajorVersion(flatVersion)
|
||||
}
|
||||
} else {
|
||||
resultVersion = flatVersion
|
||||
}
|
||||
|
||||
ap = resultOperator + resultVersion
|
||||
}
|
||||
newParts = append(newParts, ap)
|
||||
}
|
||||
expandedParts = append(expandedParts, newParts)
|
||||
}
|
||||
|
||||
return expandedParts, nil
|
||||
}
|
||||
|
||||
func parseComparator(s string) comparator {
|
||||
switch s {
|
||||
case "==":
|
||||
fallthrough
|
||||
case "":
|
||||
fallthrough
|
||||
case "=":
|
||||
return compEQ
|
||||
case ">":
|
||||
return compGT
|
||||
case ">=":
|
||||
return compGE
|
||||
case "<":
|
||||
return compLT
|
||||
case "<=":
|
||||
return compLE
|
||||
case "!":
|
||||
fallthrough
|
||||
case "!=":
|
||||
return compNE
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MustParseRange is like ParseRange but panics if the range cannot be parsed.
|
||||
func MustParseRange(s string) Range {
|
||||
r, err := ParseRange(s)
|
||||
if err != nil {
|
||||
panic(`semver: ParseRange(` + s + `): ` + err.Error())
|
||||
}
|
||||
return r
|
||||
}
|
581
src/vendor/github.com/blang/semver/range_test.go
generated
vendored
Normal file
581
src/vendor/github.com/blang/semver/range_test.go
generated
vendored
Normal file
@ -0,0 +1,581 @@
|
||||
package semver
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type wildcardTypeTest struct {
|
||||
input string
|
||||
wildcardType wildcardType
|
||||
}
|
||||
|
||||
type comparatorTest struct {
|
||||
input string
|
||||
comparator func(comparator) bool
|
||||
}
|
||||
|
||||
func TestParseComparator(t *testing.T) {
|
||||
compatorTests := []comparatorTest{
|
||||
{">", testGT},
|
||||
{">=", testGE},
|
||||
{"<", testLT},
|
||||
{"<=", testLE},
|
||||
{"", testEQ},
|
||||
{"=", testEQ},
|
||||
{"==", testEQ},
|
||||
{"!=", testNE},
|
||||
{"!", testNE},
|
||||
{"-", nil},
|
||||
{"<==", nil},
|
||||
{"<<", nil},
|
||||
{">>", nil},
|
||||
}
|
||||
|
||||
for _, tc := range compatorTests {
|
||||
if c := parseComparator(tc.input); c == nil {
|
||||
if tc.comparator != nil {
|
||||
t.Errorf("Comparator nil for case %q\n", tc.input)
|
||||
}
|
||||
} else if !tc.comparator(c) {
|
||||
t.Errorf("Invalid comparator for case %q\n", tc.input)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
v1 = MustParse("1.2.2")
|
||||
v2 = MustParse("1.2.3")
|
||||
v3 = MustParse("1.2.4")
|
||||
)
|
||||
|
||||
func testEQ(f comparator) bool {
|
||||
return f(v1, v1) && !f(v1, v2)
|
||||
}
|
||||
|
||||
func testNE(f comparator) bool {
|
||||
return !f(v1, v1) && f(v1, v2)
|
||||
}
|
||||
|
||||
func testGT(f comparator) bool {
|
||||
return f(v2, v1) && f(v3, v2) && !f(v1, v2) && !f(v1, v1)
|
||||
}
|
||||
|
||||
func testGE(f comparator) bool {
|
||||
return f(v2, v1) && f(v3, v2) && !f(v1, v2)
|
||||
}
|
||||
|
||||
func testLT(f comparator) bool {
|
||||
return f(v1, v2) && f(v2, v3) && !f(v2, v1) && !f(v1, v1)
|
||||
}
|
||||
|
||||
func testLE(f comparator) bool {
|
||||
return f(v1, v2) && f(v2, v3) && !f(v2, v1)
|
||||
}
|
||||
|
||||
func TestSplitAndTrim(t *testing.T) {
|
||||
tests := []struct {
|
||||
i string
|
||||
s []string
|
||||
}{
|
||||
{"1.2.3 1.2.3", []string{"1.2.3", "1.2.3"}},
|
||||
{" 1.2.3 1.2.3 ", []string{"1.2.3", "1.2.3"}}, // Spaces
|
||||
{" >= 1.2.3 <= 1.2.3 ", []string{">=1.2.3", "<=1.2.3"}}, // Spaces between operator and version
|
||||
{"1.2.3 || >=1.2.3 <1.2.3", []string{"1.2.3", "||", ">=1.2.3", "<1.2.3"}},
|
||||
{" 1.2.3 || >=1.2.3 <1.2.3 ", []string{"1.2.3", "||", ">=1.2.3", "<1.2.3"}},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
p := splitAndTrim(tc.i)
|
||||
if !reflect.DeepEqual(p, tc.s) {
|
||||
t.Errorf("Invalid for case %q: Expected %q, got: %q", tc.i, tc.s, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSplitComparatorVersion(t *testing.T) {
|
||||
tests := []struct {
|
||||
i string
|
||||
p []string
|
||||
}{
|
||||
{">1.2.3", []string{">", "1.2.3"}},
|
||||
{">=1.2.3", []string{">=", "1.2.3"}},
|
||||
{"<1.2.3", []string{"<", "1.2.3"}},
|
||||
{"<=1.2.3", []string{"<=", "1.2.3"}},
|
||||
{"1.2.3", []string{"", "1.2.3"}},
|
||||
{"=1.2.3", []string{"=", "1.2.3"}},
|
||||
{"==1.2.3", []string{"==", "1.2.3"}},
|
||||
{"!=1.2.3", []string{"!=", "1.2.3"}},
|
||||
{"!1.2.3", []string{"!", "1.2.3"}},
|
||||
{"error", nil},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
if op, v, err := splitComparatorVersion(tc.i); err != nil {
|
||||
if tc.p != nil {
|
||||
t.Errorf("Invalid for case %q: Expected %q, got error %q", tc.i, tc.p, err)
|
||||
}
|
||||
} else if op != tc.p[0] {
|
||||
t.Errorf("Invalid operator for case %q: Expected %q, got: %q", tc.i, tc.p[0], op)
|
||||
} else if v != tc.p[1] {
|
||||
t.Errorf("Invalid version for case %q: Expected %q, got: %q", tc.i, tc.p[1], v)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildVersionRange(t *testing.T) {
|
||||
tests := []struct {
|
||||
opStr string
|
||||
vStr string
|
||||
c func(comparator) bool
|
||||
v string
|
||||
}{
|
||||
{">", "1.2.3", testGT, "1.2.3"},
|
||||
{">=", "1.2.3", testGE, "1.2.3"},
|
||||
{"<", "1.2.3", testLT, "1.2.3"},
|
||||
{"<=", "1.2.3", testLE, "1.2.3"},
|
||||
{"", "1.2.3", testEQ, "1.2.3"},
|
||||
{"=", "1.2.3", testEQ, "1.2.3"},
|
||||
{"==", "1.2.3", testEQ, "1.2.3"},
|
||||
{"!=", "1.2.3", testNE, "1.2.3"},
|
||||
{"!", "1.2.3", testNE, "1.2.3"},
|
||||
{">>", "1.2.3", nil, ""}, // Invalid comparator
|
||||
{"=", "invalid", nil, ""}, // Invalid version
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
if r, err := buildVersionRange(tc.opStr, tc.vStr); err != nil {
|
||||
if tc.c != nil {
|
||||
t.Errorf("Invalid for case %q: Expected %q, got error %q", strings.Join([]string{tc.opStr, tc.vStr}, ""), tc.v, err)
|
||||
}
|
||||
} else if r == nil {
|
||||
t.Errorf("Invalid for case %q: got nil", strings.Join([]string{tc.opStr, tc.vStr}, ""))
|
||||
} else {
|
||||
// test version
|
||||
if tv := MustParse(tc.v); !r.v.EQ(tv) {
|
||||
t.Errorf("Invalid for case %q: Expected version %q, got: %q", strings.Join([]string{tc.opStr, tc.vStr}, ""), tv, r.v)
|
||||
}
|
||||
// test comparator
|
||||
if r.c == nil {
|
||||
t.Errorf("Invalid for case %q: got nil comparator", strings.Join([]string{tc.opStr, tc.vStr}, ""))
|
||||
continue
|
||||
}
|
||||
if !tc.c(r.c) {
|
||||
t.Errorf("Invalid comparator for case %q\n", strings.Join([]string{tc.opStr, tc.vStr}, ""))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestSplitORParts(t *testing.T) {
|
||||
tests := []struct {
|
||||
i []string
|
||||
o [][]string
|
||||
}{
|
||||
{[]string{">1.2.3", "||", "<1.2.3", "||", "=1.2.3"}, [][]string{
|
||||
[]string{">1.2.3"},
|
||||
[]string{"<1.2.3"},
|
||||
[]string{"=1.2.3"},
|
||||
}},
|
||||
{[]string{">1.2.3", "<1.2.3", "||", "=1.2.3"}, [][]string{
|
||||
[]string{">1.2.3", "<1.2.3"},
|
||||
[]string{"=1.2.3"},
|
||||
}},
|
||||
{[]string{">1.2.3", "||"}, nil},
|
||||
{[]string{"||", ">1.2.3"}, nil},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
o, err := splitORParts(tc.i)
|
||||
if err != nil && tc.o != nil {
|
||||
t.Errorf("Unexpected error for case %q: %s", tc.i, err)
|
||||
}
|
||||
if !reflect.DeepEqual(tc.o, o) {
|
||||
t.Errorf("Invalid for case %q: Expected %q, got: %q", tc.i, tc.o, o)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetWildcardType(t *testing.T) {
|
||||
wildcardTypeTests := []wildcardTypeTest{
|
||||
{"x", majorWildcard},
|
||||
{"1.x", minorWildcard},
|
||||
{"1.2.x", patchWildcard},
|
||||
{"fo.o.b.ar", noneWildcard},
|
||||
}
|
||||
|
||||
for _, tc := range wildcardTypeTests {
|
||||
o := getWildcardType(tc.input)
|
||||
if o != tc.wildcardType {
|
||||
t.Errorf("Invalid for case: %q: Expected %q, got: %q", tc.input, tc.wildcardType, o)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateVersionFromWildcard(t *testing.T) {
|
||||
tests := []struct {
|
||||
i string
|
||||
s string
|
||||
}{
|
||||
{"1.2.x", "1.2.0"},
|
||||
{"1.x", "1.0.0"},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
p := createVersionFromWildcard(tc.i)
|
||||
if p != tc.s {
|
||||
t.Errorf("Invalid for case %q: Expected %q, got: %q", tc.i, tc.s, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIncrementMajorVersion(t *testing.T) {
|
||||
tests := []struct {
|
||||
i string
|
||||
s string
|
||||
}{
|
||||
{"1.2.3", "2.2.3"},
|
||||
{"1.2", "2.2"},
|
||||
{"foo.bar", ""},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
p, _ := incrementMajorVersion(tc.i)
|
||||
if p != tc.s {
|
||||
t.Errorf("Invalid for case %q: Expected %q, got: %q", tc.i, tc.s, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIncrementMinorVersion(t *testing.T) {
|
||||
tests := []struct {
|
||||
i string
|
||||
s string
|
||||
}{
|
||||
{"1.2.3", "1.3.3"},
|
||||
{"1.2", "1.3"},
|
||||
{"foo.bar", ""},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
p, _ := incrementMinorVersion(tc.i)
|
||||
if p != tc.s {
|
||||
t.Errorf("Invalid for case %q: Expected %q, got: %q", tc.i, tc.s, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExpandWildcardVersion(t *testing.T) {
|
||||
tests := []struct {
|
||||
i [][]string
|
||||
o [][]string
|
||||
}{
|
||||
{[][]string{[]string{"foox"}}, nil},
|
||||
{[][]string{[]string{">=1.2.x"}}, [][]string{[]string{">=1.2.0"}}},
|
||||
{[][]string{[]string{"<=1.2.x"}}, [][]string{[]string{"<1.3.0"}}},
|
||||
{[][]string{[]string{">1.2.x"}}, [][]string{[]string{">=1.3.0"}}},
|
||||
{[][]string{[]string{"<1.2.x"}}, [][]string{[]string{"<1.2.0"}}},
|
||||
{[][]string{[]string{"!=1.2.x"}}, [][]string{[]string{"<1.2.0", ">=1.3.0"}}},
|
||||
{[][]string{[]string{">=1.x"}}, [][]string{[]string{">=1.0.0"}}},
|
||||
{[][]string{[]string{"<=1.x"}}, [][]string{[]string{"<2.0.0"}}},
|
||||
{[][]string{[]string{">1.x"}}, [][]string{[]string{">=2.0.0"}}},
|
||||
{[][]string{[]string{"<1.x"}}, [][]string{[]string{"<1.0.0"}}},
|
||||
{[][]string{[]string{"!=1.x"}}, [][]string{[]string{"<1.0.0", ">=2.0.0"}}},
|
||||
{[][]string{[]string{"1.2.x"}}, [][]string{[]string{">=1.2.0", "<1.3.0"}}},
|
||||
{[][]string{[]string{"1.x"}}, [][]string{[]string{">=1.0.0", "<2.0.0"}}},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
o, _ := expandWildcardVersion(tc.i)
|
||||
if !reflect.DeepEqual(tc.o, o) {
|
||||
t.Errorf("Invalid for case %q: Expected %q, got: %q", tc.i, tc.o, o)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestVersionRangeToRange(t *testing.T) {
|
||||
vr := versionRange{
|
||||
v: MustParse("1.2.3"),
|
||||
c: compLT,
|
||||
}
|
||||
rf := vr.rangeFunc()
|
||||
if !rf(MustParse("1.2.2")) || rf(MustParse("1.2.3")) {
|
||||
t.Errorf("Invalid conversion to range func")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRangeAND(t *testing.T) {
|
||||
v := MustParse("1.2.2")
|
||||
v1 := MustParse("1.2.1")
|
||||
v2 := MustParse("1.2.3")
|
||||
rf1 := Range(func(v Version) bool {
|
||||
return v.GT(v1)
|
||||
})
|
||||
rf2 := Range(func(v Version) bool {
|
||||
return v.LT(v2)
|
||||
})
|
||||
rf := rf1.AND(rf2)
|
||||
if rf(v1) {
|
||||
t.Errorf("Invalid rangefunc, accepted: %s", v1)
|
||||
}
|
||||
if rf(v2) {
|
||||
t.Errorf("Invalid rangefunc, accepted: %s", v2)
|
||||
}
|
||||
if !rf(v) {
|
||||
t.Errorf("Invalid rangefunc, did not accept: %s", v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRangeOR(t *testing.T) {
|
||||
tests := []struct {
|
||||
v Version
|
||||
b bool
|
||||
}{
|
||||
{MustParse("1.2.0"), true},
|
||||
{MustParse("1.2.2"), false},
|
||||
{MustParse("1.2.4"), true},
|
||||
}
|
||||
v1 := MustParse("1.2.1")
|
||||
v2 := MustParse("1.2.3")
|
||||
rf1 := Range(func(v Version) bool {
|
||||
return v.LT(v1)
|
||||
})
|
||||
rf2 := Range(func(v Version) bool {
|
||||
return v.GT(v2)
|
||||
})
|
||||
rf := rf1.OR(rf2)
|
||||
for _, tc := range tests {
|
||||
if r := rf(tc.v); r != tc.b {
|
||||
t.Errorf("Invalid for case %q: Expected %t, got %t", tc.v, tc.b, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRange(t *testing.T) {
|
||||
type tv struct {
|
||||
v string
|
||||
b bool
|
||||
}
|
||||
tests := []struct {
|
||||
i string
|
||||
t []tv
|
||||
}{
|
||||
// Simple expressions
|
||||
{">1.2.3", []tv{
|
||||
{"1.2.2", false},
|
||||
{"1.2.3", false},
|
||||
{"1.2.4", true},
|
||||
}},
|
||||
{">=1.2.3", []tv{
|
||||
{"1.2.3", true},
|
||||
{"1.2.4", true},
|
||||
{"1.2.2", false},
|
||||
}},
|
||||
{"<1.2.3", []tv{
|
||||
{"1.2.2", true},
|
||||
{"1.2.3", false},
|
||||
{"1.2.4", false},
|
||||
}},
|
||||
{"<=1.2.3", []tv{
|
||||
{"1.2.2", true},
|
||||
{"1.2.3", true},
|
||||
{"1.2.4", false},
|
||||
}},
|
||||
{"1.2.3", []tv{
|
||||
{"1.2.2", false},
|
||||
{"1.2.3", true},
|
||||
{"1.2.4", false},
|
||||
}},
|
||||
{"=1.2.3", []tv{
|
||||
{"1.2.2", false},
|
||||
{"1.2.3", true},
|
||||
{"1.2.4", false},
|
||||
}},
|
||||
{"==1.2.3", []tv{
|
||||
{"1.2.2", false},
|
||||
{"1.2.3", true},
|
||||
{"1.2.4", false},
|
||||
}},
|
||||
{"!=1.2.3", []tv{
|
||||
{"1.2.2", true},
|
||||
{"1.2.3", false},
|
||||
{"1.2.4", true},
|
||||
}},
|
||||
{"!1.2.3", []tv{
|
||||
{"1.2.2", true},
|
||||
{"1.2.3", false},
|
||||
{"1.2.4", true},
|
||||
}},
|
||||
// Simple Expression errors
|
||||
{">>1.2.3", nil},
|
||||
{"!1.2.3", nil},
|
||||
{"1.0", nil},
|
||||
{"string", nil},
|
||||
{"", nil},
|
||||
{"fo.ob.ar.x", nil},
|
||||
// AND Expressions
|
||||
{">1.2.2 <1.2.4", []tv{
|
||||
{"1.2.2", false},
|
||||
{"1.2.3", true},
|
||||
{"1.2.4", false},
|
||||
}},
|
||||
{"<1.2.2 <1.2.4", []tv{
|
||||
{"1.2.1", true},
|
||||
{"1.2.2", false},
|
||||
{"1.2.3", false},
|
||||
{"1.2.4", false},
|
||||
}},
|
||||
{">1.2.2 <1.2.5 !=1.2.4", []tv{
|
||||
{"1.2.2", false},
|
||||
{"1.2.3", true},
|
||||
{"1.2.4", false},
|
||||
{"1.2.5", false},
|
||||
}},
|
||||
{">1.2.2 <1.2.5 !1.2.4", []tv{
|
||||
{"1.2.2", false},
|
||||
{"1.2.3", true},
|
||||
{"1.2.4", false},
|
||||
{"1.2.5", false},
|
||||
}},
|
||||
// OR Expressions
|
||||
{">1.2.2 || <1.2.4", []tv{
|
||||
{"1.2.2", true},
|
||||
{"1.2.3", true},
|
||||
{"1.2.4", true},
|
||||
}},
|
||||
{"<1.2.2 || >1.2.4", []tv{
|
||||
{"1.2.2", false},
|
||||
{"1.2.3", false},
|
||||
{"1.2.4", false},
|
||||
}},
|
||||
// Wildcard expressions
|
||||
{">1.x", []tv{
|
||||
{"0.1.9", false},
|
||||
{"1.2.6", false},
|
||||
{"1.9.0", false},
|
||||
{"2.0.0", true},
|
||||
}},
|
||||
{">1.2.x", []tv{
|
||||
{"1.1.9", false},
|
||||
{"1.2.6", false},
|
||||
{"1.3.0", true},
|
||||
}},
|
||||
// Combined Expressions
|
||||
{">1.2.2 <1.2.4 || >=2.0.0", []tv{
|
||||
{"1.2.2", false},
|
||||
{"1.2.3", true},
|
||||
{"1.2.4", false},
|
||||
{"2.0.0", true},
|
||||
{"2.0.1", true},
|
||||
}},
|
||||
{"1.x || >=2.0.x <2.2.x", []tv{
|
||||
{"0.9.2", false},
|
||||
{"1.2.2", true},
|
||||
{"2.0.0", true},
|
||||
{"2.1.8", true},
|
||||
{"2.2.0", false},
|
||||
}},
|
||||
{">1.2.2 <1.2.4 || >=2.0.0 <3.0.0", []tv{
|
||||
{"1.2.2", false},
|
||||
{"1.2.3", true},
|
||||
{"1.2.4", false},
|
||||
{"2.0.0", true},
|
||||
{"2.0.1", true},
|
||||
{"2.9.9", true},
|
||||
{"3.0.0", false},
|
||||
}},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
r, err := ParseRange(tc.i)
|
||||
if err != nil && tc.t != nil {
|
||||
t.Errorf("Error parsing range %q: %s", tc.i, err)
|
||||
continue
|
||||
}
|
||||
for _, tvc := range tc.t {
|
||||
v := MustParse(tvc.v)
|
||||
if res := r(v); res != tvc.b {
|
||||
t.Errorf("Invalid for case %q matching %q: Expected %t, got: %t", tc.i, tvc.v, tvc.b, res)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestMustParseRange(t *testing.T) {
|
||||
testCase := ">1.2.2 <1.2.4 || >=2.0.0 <3.0.0"
|
||||
r := MustParseRange(testCase)
|
||||
if !r(MustParse("1.2.3")) {
|
||||
t.Errorf("Unexpected range behavior on MustParseRange")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMustParseRange_panic(t *testing.T) {
|
||||
defer func() {
|
||||
if recover() == nil {
|
||||
t.Errorf("Should have panicked")
|
||||
}
|
||||
}()
|
||||
_ = MustParseRange("invalid version")
|
||||
}
|
||||
|
||||
func BenchmarkRangeParseSimple(b *testing.B) {
|
||||
const VERSION = ">1.0.0"
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
ParseRange(VERSION)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRangeParseAverage(b *testing.B) {
|
||||
const VERSION = ">=1.0.0 <2.0.0"
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
ParseRange(VERSION)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRangeParseComplex(b *testing.B) {
|
||||
const VERSION = ">=1.0.0 <2.0.0 || >=3.0.1 <4.0.0 !=3.0.3 || >=5.0.0"
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
ParseRange(VERSION)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRangeMatchSimple(b *testing.B) {
|
||||
const VERSION = ">1.0.0"
|
||||
r, _ := ParseRange(VERSION)
|
||||
v := MustParse("2.0.0")
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
r(v)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRangeMatchAverage(b *testing.B) {
|
||||
const VERSION = ">=1.0.0 <2.0.0"
|
||||
r, _ := ParseRange(VERSION)
|
||||
v := MustParse("1.2.3")
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
r(v)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRangeMatchComplex(b *testing.B) {
|
||||
const VERSION = ">=1.0.0 <2.0.0 || >=3.0.1 <4.0.0 !=3.0.3 || >=5.0.0"
|
||||
r, _ := ParseRange(VERSION)
|
||||
v := MustParse("5.0.1")
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
r(v)
|
||||
}
|
||||
}
|
418
src/vendor/github.com/blang/semver/semver.go
generated
vendored
Normal file
418
src/vendor/github.com/blang/semver/semver.go
generated
vendored
Normal file
@ -0,0 +1,418 @@
|
||||
package semver
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
numbers string = "0123456789"
|
||||
alphas = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-"
|
||||
alphanum = alphas + numbers
|
||||
)
|
||||
|
||||
// SpecVersion is the latest fully supported spec version of semver
|
||||
var SpecVersion = Version{
|
||||
Major: 2,
|
||||
Minor: 0,
|
||||
Patch: 0,
|
||||
}
|
||||
|
||||
// Version represents a semver compatible version
|
||||
type Version struct {
|
||||
Major uint64
|
||||
Minor uint64
|
||||
Patch uint64
|
||||
Pre []PRVersion
|
||||
Build []string //No Precendence
|
||||
}
|
||||
|
||||
// Version to string
|
||||
func (v Version) String() string {
|
||||
b := make([]byte, 0, 5)
|
||||
b = strconv.AppendUint(b, v.Major, 10)
|
||||
b = append(b, '.')
|
||||
b = strconv.AppendUint(b, v.Minor, 10)
|
||||
b = append(b, '.')
|
||||
b = strconv.AppendUint(b, v.Patch, 10)
|
||||
|
||||
if len(v.Pre) > 0 {
|
||||
b = append(b, '-')
|
||||
b = append(b, v.Pre[0].String()...)
|
||||
|
||||
for _, pre := range v.Pre[1:] {
|
||||
b = append(b, '.')
|
||||
b = append(b, pre.String()...)
|
||||
}
|
||||
}
|
||||
|
||||
if len(v.Build) > 0 {
|
||||
b = append(b, '+')
|
||||
b = append(b, v.Build[0]...)
|
||||
|
||||
for _, build := range v.Build[1:] {
|
||||
b = append(b, '.')
|
||||
b = append(b, build...)
|
||||
}
|
||||
}
|
||||
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// Equals checks if v is equal to o.
|
||||
func (v Version) Equals(o Version) bool {
|
||||
return (v.Compare(o) == 0)
|
||||
}
|
||||
|
||||
// EQ checks if v is equal to o.
|
||||
func (v Version) EQ(o Version) bool {
|
||||
return (v.Compare(o) == 0)
|
||||
}
|
||||
|
||||
// NE checks if v is not equal to o.
|
||||
func (v Version) NE(o Version) bool {
|
||||
return (v.Compare(o) != 0)
|
||||
}
|
||||
|
||||
// GT checks if v is greater than o.
|
||||
func (v Version) GT(o Version) bool {
|
||||
return (v.Compare(o) == 1)
|
||||
}
|
||||
|
||||
// GTE checks if v is greater than or equal to o.
|
||||
func (v Version) GTE(o Version) bool {
|
||||
return (v.Compare(o) >= 0)
|
||||
}
|
||||
|
||||
// GE checks if v is greater than or equal to o.
|
||||
func (v Version) GE(o Version) bool {
|
||||
return (v.Compare(o) >= 0)
|
||||
}
|
||||
|
||||
// LT checks if v is less than o.
|
||||
func (v Version) LT(o Version) bool {
|
||||
return (v.Compare(o) == -1)
|
||||
}
|
||||
|
||||
// LTE checks if v is less than or equal to o.
|
||||
func (v Version) LTE(o Version) bool {
|
||||
return (v.Compare(o) <= 0)
|
||||
}
|
||||
|
||||
// LE checks if v is less than or equal to o.
|
||||
func (v Version) LE(o Version) bool {
|
||||
return (v.Compare(o) <= 0)
|
||||
}
|
||||
|
||||
// Compare compares Versions v to o:
|
||||
// -1 == v is less than o
|
||||
// 0 == v is equal to o
|
||||
// 1 == v is greater than o
|
||||
func (v Version) Compare(o Version) int {
|
||||
if v.Major != o.Major {
|
||||
if v.Major > o.Major {
|
||||
return 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
if v.Minor != o.Minor {
|
||||
if v.Minor > o.Minor {
|
||||
return 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
if v.Patch != o.Patch {
|
||||
if v.Patch > o.Patch {
|
||||
return 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// Quick comparison if a version has no prerelease versions
|
||||
if len(v.Pre) == 0 && len(o.Pre) == 0 {
|
||||
return 0
|
||||
} else if len(v.Pre) == 0 && len(o.Pre) > 0 {
|
||||
return 1
|
||||
} else if len(v.Pre) > 0 && len(o.Pre) == 0 {
|
||||
return -1
|
||||
}
|
||||
|
||||
i := 0
|
||||
for ; i < len(v.Pre) && i < len(o.Pre); i++ {
|
||||
if comp := v.Pre[i].Compare(o.Pre[i]); comp == 0 {
|
||||
continue
|
||||
} else if comp == 1 {
|
||||
return 1
|
||||
} else {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
// If all pr versions are the equal but one has further prversion, this one greater
|
||||
if i == len(v.Pre) && i == len(o.Pre) {
|
||||
return 0
|
||||
} else if i == len(v.Pre) && i < len(o.Pre) {
|
||||
return -1
|
||||
} else {
|
||||
return 1
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Validate validates v and returns error in case
|
||||
func (v Version) Validate() error {
|
||||
// Major, Minor, Patch already validated using uint64
|
||||
|
||||
for _, pre := range v.Pre {
|
||||
if !pre.IsNum { //Numeric prerelease versions already uint64
|
||||
if len(pre.VersionStr) == 0 {
|
||||
return fmt.Errorf("Prerelease can not be empty %q", pre.VersionStr)
|
||||
}
|
||||
if !containsOnly(pre.VersionStr, alphanum) {
|
||||
return fmt.Errorf("Invalid character(s) found in prerelease %q", pre.VersionStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, build := range v.Build {
|
||||
if len(build) == 0 {
|
||||
return fmt.Errorf("Build meta data can not be empty %q", build)
|
||||
}
|
||||
if !containsOnly(build, alphanum) {
|
||||
return fmt.Errorf("Invalid character(s) found in build meta data %q", build)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// New is an alias for Parse and returns a pointer, parses version string and returns a validated Version or error
|
||||
func New(s string) (vp *Version, err error) {
|
||||
v, err := Parse(s)
|
||||
vp = &v
|
||||
return
|
||||
}
|
||||
|
||||
// Make is an alias for Parse, parses version string and returns a validated Version or error
|
||||
func Make(s string) (Version, error) {
|
||||
return Parse(s)
|
||||
}
|
||||
|
||||
// ParseTolerant allows for certain version specifications that do not strictly adhere to semver
|
||||
// specs to be parsed by this library. It does so by normalizing versions before passing them to
|
||||
// Parse(). It currently trims spaces, removes a "v" prefix, and adds a 0 patch number to versions
|
||||
// with only major and minor components specified
|
||||
func ParseTolerant(s string) (Version, error) {
|
||||
s = strings.TrimSpace(s)
|
||||
s = strings.TrimPrefix(s, "v")
|
||||
|
||||
// Split into major.minor.(patch+pr+meta)
|
||||
parts := strings.SplitN(s, ".", 3)
|
||||
if len(parts) < 3 {
|
||||
if strings.ContainsAny(parts[len(parts)-1], "+-") {
|
||||
return Version{}, errors.New("Short version cannot contain PreRelease/Build meta data")
|
||||
}
|
||||
for len(parts) < 3 {
|
||||
parts = append(parts, "0")
|
||||
}
|
||||
s = strings.Join(parts, ".")
|
||||
}
|
||||
|
||||
return Parse(s)
|
||||
}
|
||||
|
||||
// Parse parses version string and returns a validated Version or error
|
||||
func Parse(s string) (Version, error) {
|
||||
if len(s) == 0 {
|
||||
return Version{}, errors.New("Version string empty")
|
||||
}
|
||||
|
||||
// Split into major.minor.(patch+pr+meta)
|
||||
parts := strings.SplitN(s, ".", 3)
|
||||
if len(parts) != 3 {
|
||||
return Version{}, errors.New("No Major.Minor.Patch elements found")
|
||||
}
|
||||
|
||||
// Major
|
||||
if !containsOnly(parts[0], numbers) {
|
||||
return Version{}, fmt.Errorf("Invalid character(s) found in major number %q", parts[0])
|
||||
}
|
||||
if hasLeadingZeroes(parts[0]) {
|
||||
return Version{}, fmt.Errorf("Major number must not contain leading zeroes %q", parts[0])
|
||||
}
|
||||
major, err := strconv.ParseUint(parts[0], 10, 64)
|
||||
if err != nil {
|
||||
return Version{}, err
|
||||
}
|
||||
|
||||
// Minor
|
||||
if !containsOnly(parts[1], numbers) {
|
||||
return Version{}, fmt.Errorf("Invalid character(s) found in minor number %q", parts[1])
|
||||
}
|
||||
if hasLeadingZeroes(parts[1]) {
|
||||
return Version{}, fmt.Errorf("Minor number must not contain leading zeroes %q", parts[1])
|
||||
}
|
||||
minor, err := strconv.ParseUint(parts[1], 10, 64)
|
||||
if err != nil {
|
||||
return Version{}, err
|
||||
}
|
||||
|
||||
v := Version{}
|
||||
v.Major = major
|
||||
v.Minor = minor
|
||||
|
||||
var build, prerelease []string
|
||||
patchStr := parts[2]
|
||||
|
||||
if buildIndex := strings.IndexRune(patchStr, '+'); buildIndex != -1 {
|
||||
build = strings.Split(patchStr[buildIndex+1:], ".")
|
||||
patchStr = patchStr[:buildIndex]
|
||||
}
|
||||
|
||||
if preIndex := strings.IndexRune(patchStr, '-'); preIndex != -1 {
|
||||
prerelease = strings.Split(patchStr[preIndex+1:], ".")
|
||||
patchStr = patchStr[:preIndex]
|
||||
}
|
||||
|
||||
if !containsOnly(patchStr, numbers) {
|
||||
return Version{}, fmt.Errorf("Invalid character(s) found in patch number %q", patchStr)
|
||||
}
|
||||
if hasLeadingZeroes(patchStr) {
|
||||
return Version{}, fmt.Errorf("Patch number must not contain leading zeroes %q", patchStr)
|
||||
}
|
||||
patch, err := strconv.ParseUint(patchStr, 10, 64)
|
||||
if err != nil {
|
||||
return Version{}, err
|
||||
}
|
||||
|
||||
v.Patch = patch
|
||||
|
||||
// Prerelease
|
||||
for _, prstr := range prerelease {
|
||||
parsedPR, err := NewPRVersion(prstr)
|
||||
if err != nil {
|
||||
return Version{}, err
|
||||
}
|
||||
v.Pre = append(v.Pre, parsedPR)
|
||||
}
|
||||
|
||||
// Build meta data
|
||||
for _, str := range build {
|
||||
if len(str) == 0 {
|
||||
return Version{}, errors.New("Build meta data is empty")
|
||||
}
|
||||
if !containsOnly(str, alphanum) {
|
||||
return Version{}, fmt.Errorf("Invalid character(s) found in build meta data %q", str)
|
||||
}
|
||||
v.Build = append(v.Build, str)
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// MustParse is like Parse but panics if the version cannot be parsed.
|
||||
func MustParse(s string) Version {
|
||||
v, err := Parse(s)
|
||||
if err != nil {
|
||||
panic(`semver: Parse(` + s + `): ` + err.Error())
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// PRVersion represents a PreRelease Version
|
||||
type PRVersion struct {
|
||||
VersionStr string
|
||||
VersionNum uint64
|
||||
IsNum bool
|
||||
}
|
||||
|
||||
// NewPRVersion creates a new valid prerelease version
|
||||
func NewPRVersion(s string) (PRVersion, error) {
|
||||
if len(s) == 0 {
|
||||
return PRVersion{}, errors.New("Prerelease is empty")
|
||||
}
|
||||
v := PRVersion{}
|
||||
if containsOnly(s, numbers) {
|
||||
if hasLeadingZeroes(s) {
|
||||
return PRVersion{}, fmt.Errorf("Numeric PreRelease version must not contain leading zeroes %q", s)
|
||||
}
|
||||
num, err := strconv.ParseUint(s, 10, 64)
|
||||
|
||||
// Might never be hit, but just in case
|
||||
if err != nil {
|
||||
return PRVersion{}, err
|
||||
}
|
||||
v.VersionNum = num
|
||||
v.IsNum = true
|
||||
} else if containsOnly(s, alphanum) {
|
||||
v.VersionStr = s
|
||||
v.IsNum = false
|
||||
} else {
|
||||
return PRVersion{}, fmt.Errorf("Invalid character(s) found in prerelease %q", s)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// IsNumeric checks if prerelease-version is numeric
|
||||
func (v PRVersion) IsNumeric() bool {
|
||||
return v.IsNum
|
||||
}
|
||||
|
||||
// Compare compares two PreRelease Versions v and o:
|
||||
// -1 == v is less than o
|
||||
// 0 == v is equal to o
|
||||
// 1 == v is greater than o
|
||||
func (v PRVersion) Compare(o PRVersion) int {
|
||||
if v.IsNum && !o.IsNum {
|
||||
return -1
|
||||
} else if !v.IsNum && o.IsNum {
|
||||
return 1
|
||||
} else if v.IsNum && o.IsNum {
|
||||
if v.VersionNum == o.VersionNum {
|
||||
return 0
|
||||
} else if v.VersionNum > o.VersionNum {
|
||||
return 1
|
||||
} else {
|
||||
return -1
|
||||
}
|
||||
} else { // both are Alphas
|
||||
if v.VersionStr == o.VersionStr {
|
||||
return 0
|
||||
} else if v.VersionStr > o.VersionStr {
|
||||
return 1
|
||||
} else {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PreRelease version to string
|
||||
func (v PRVersion) String() string {
|
||||
if v.IsNum {
|
||||
return strconv.FormatUint(v.VersionNum, 10)
|
||||
}
|
||||
return v.VersionStr
|
||||
}
|
||||
|
||||
func containsOnly(s string, set string) bool {
|
||||
return strings.IndexFunc(s, func(r rune) bool {
|
||||
return !strings.ContainsRune(set, r)
|
||||
}) == -1
|
||||
}
|
||||
|
||||
func hasLeadingZeroes(s string) bool {
|
||||
return len(s) > 1 && s[0] == '0'
|
||||
}
|
||||
|
||||
// NewBuildVersion creates a new valid build version
|
||||
func NewBuildVersion(s string) (string, error) {
|
||||
if len(s) == 0 {
|
||||
return "", errors.New("Buildversion is empty")
|
||||
}
|
||||
if !containsOnly(s, alphanum) {
|
||||
return "", fmt.Errorf("Invalid character(s) found in build meta data %q", s)
|
||||
}
|
||||
return s, nil
|
||||
}
|
458
src/vendor/github.com/blang/semver/semver_test.go
generated
vendored
Normal file
458
src/vendor/github.com/blang/semver/semver_test.go
generated
vendored
Normal file
@ -0,0 +1,458 @@
|
||||
package semver
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func prstr(s string) PRVersion {
|
||||
return PRVersion{s, 0, false}
|
||||
}
|
||||
|
||||
func prnum(i uint64) PRVersion {
|
||||
return PRVersion{"", i, true}
|
||||
}
|
||||
|
||||
type formatTest struct {
|
||||
v Version
|
||||
result string
|
||||
}
|
||||
|
||||
var formatTests = []formatTest{
|
||||
{Version{1, 2, 3, nil, nil}, "1.2.3"},
|
||||
{Version{0, 0, 1, nil, nil}, "0.0.1"},
|
||||
{Version{0, 0, 1, []PRVersion{prstr("alpha"), prstr("preview")}, []string{"123", "456"}}, "0.0.1-alpha.preview+123.456"},
|
||||
{Version{1, 2, 3, []PRVersion{prstr("alpha"), prnum(1)}, []string{"123", "456"}}, "1.2.3-alpha.1+123.456"},
|
||||
{Version{1, 2, 3, []PRVersion{prstr("alpha"), prnum(1)}, nil}, "1.2.3-alpha.1"},
|
||||
{Version{1, 2, 3, nil, []string{"123", "456"}}, "1.2.3+123.456"},
|
||||
// Prereleases and build metadata hyphens
|
||||
{Version{1, 2, 3, []PRVersion{prstr("alpha"), prstr("b-eta")}, []string{"123", "b-uild"}}, "1.2.3-alpha.b-eta+123.b-uild"},
|
||||
{Version{1, 2, 3, nil, []string{"123", "b-uild"}}, "1.2.3+123.b-uild"},
|
||||
{Version{1, 2, 3, []PRVersion{prstr("alpha"), prstr("b-eta")}, nil}, "1.2.3-alpha.b-eta"},
|
||||
}
|
||||
|
||||
var tolerantFormatTests = []formatTest{
|
||||
{Version{1, 2, 3, nil, nil}, "v1.2.3"},
|
||||
{Version{1, 2, 3, nil, nil}, " 1.2.3 "},
|
||||
{Version{1, 2, 0, nil, nil}, "1.2"},
|
||||
{Version{1, 0, 0, nil, nil}, "1"},
|
||||
}
|
||||
|
||||
func TestStringer(t *testing.T) {
|
||||
for _, test := range formatTests {
|
||||
if res := test.v.String(); res != test.result {
|
||||
t.Errorf("Stringer, expected %q but got %q", test.result, res)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
for _, test := range formatTests {
|
||||
if v, err := Parse(test.result); err != nil {
|
||||
t.Errorf("Error parsing %q: %q", test.result, err)
|
||||
} else if comp := v.Compare(test.v); comp != 0 {
|
||||
t.Errorf("Parsing, expected %q but got %q, comp: %d ", test.v, v, comp)
|
||||
} else if err := v.Validate(); err != nil {
|
||||
t.Errorf("Error validating parsed version %q: %q", test.v, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseTolerant(t *testing.T) {
|
||||
for _, test := range tolerantFormatTests {
|
||||
if v, err := ParseTolerant(test.result); err != nil {
|
||||
t.Errorf("Error parsing %q: %q", test.result, err)
|
||||
} else if comp := v.Compare(test.v); comp != 0 {
|
||||
t.Errorf("Parsing, expected %q but got %q, comp: %d ", test.v, v, comp)
|
||||
} else if err := v.Validate(); err != nil {
|
||||
t.Errorf("Error validating parsed version %q: %q", test.v, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMustParse(t *testing.T) {
|
||||
_ = MustParse("32.2.1-alpha")
|
||||
}
|
||||
|
||||
func TestMustParse_panic(t *testing.T) {
|
||||
defer func() {
|
||||
if recover() == nil {
|
||||
t.Errorf("Should have panicked")
|
||||
}
|
||||
}()
|
||||
_ = MustParse("invalid version")
|
||||
}
|
||||
|
||||
func TestValidate(t *testing.T) {
|
||||
for _, test := range formatTests {
|
||||
if err := test.v.Validate(); err != nil {
|
||||
t.Errorf("Error validating %q: %q", test.v, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type compareTest struct {
|
||||
v1 Version
|
||||
v2 Version
|
||||
result int
|
||||
}
|
||||
|
||||
var compareTests = []compareTest{
|
||||
{Version{1, 0, 0, nil, nil}, Version{1, 0, 0, nil, nil}, 0},
|
||||
{Version{2, 0, 0, nil, nil}, Version{1, 0, 0, nil, nil}, 1},
|
||||
{Version{0, 1, 0, nil, nil}, Version{0, 1, 0, nil, nil}, 0},
|
||||
{Version{0, 2, 0, nil, nil}, Version{0, 1, 0, nil, nil}, 1},
|
||||
{Version{0, 0, 1, nil, nil}, Version{0, 0, 1, nil, nil}, 0},
|
||||
{Version{0, 0, 2, nil, nil}, Version{0, 0, 1, nil, nil}, 1},
|
||||
{Version{1, 2, 3, nil, nil}, Version{1, 2, 3, nil, nil}, 0},
|
||||
{Version{2, 2, 4, nil, nil}, Version{1, 2, 4, nil, nil}, 1},
|
||||
{Version{1, 3, 3, nil, nil}, Version{1, 2, 3, nil, nil}, 1},
|
||||
{Version{1, 2, 4, nil, nil}, Version{1, 2, 3, nil, nil}, 1},
|
||||
|
||||
// Spec Examples #11
|
||||
{Version{1, 0, 0, nil, nil}, Version{2, 0, 0, nil, nil}, -1},
|
||||
{Version{2, 0, 0, nil, nil}, Version{2, 1, 0, nil, nil}, -1},
|
||||
{Version{2, 1, 0, nil, nil}, Version{2, 1, 1, nil, nil}, -1},
|
||||
|
||||
// Spec Examples #9
|
||||
{Version{1, 0, 0, nil, nil}, Version{1, 0, 0, []PRVersion{prstr("alpha")}, nil}, 1},
|
||||
{Version{1, 0, 0, []PRVersion{prstr("alpha")}, nil}, Version{1, 0, 0, []PRVersion{prstr("alpha"), prnum(1)}, nil}, -1},
|
||||
{Version{1, 0, 0, []PRVersion{prstr("alpha"), prnum(1)}, nil}, Version{1, 0, 0, []PRVersion{prstr("alpha"), prstr("beta")}, nil}, -1},
|
||||
{Version{1, 0, 0, []PRVersion{prstr("alpha"), prstr("beta")}, nil}, Version{1, 0, 0, []PRVersion{prstr("beta")}, nil}, -1},
|
||||
{Version{1, 0, 0, []PRVersion{prstr("beta")}, nil}, Version{1, 0, 0, []PRVersion{prstr("beta"), prnum(2)}, nil}, -1},
|
||||
{Version{1, 0, 0, []PRVersion{prstr("beta"), prnum(2)}, nil}, Version{1, 0, 0, []PRVersion{prstr("beta"), prnum(11)}, nil}, -1},
|
||||
{Version{1, 0, 0, []PRVersion{prstr("beta"), prnum(11)}, nil}, Version{1, 0, 0, []PRVersion{prstr("rc"), prnum(1)}, nil}, -1},
|
||||
{Version{1, 0, 0, []PRVersion{prstr("rc"), prnum(1)}, nil}, Version{1, 0, 0, nil, nil}, -1},
|
||||
|
||||
// Ignore Build metadata
|
||||
{Version{1, 0, 0, nil, []string{"1", "2", "3"}}, Version{1, 0, 0, nil, nil}, 0},
|
||||
}
|
||||
|
||||
func TestCompare(t *testing.T) {
|
||||
for _, test := range compareTests {
|
||||
if res := test.v1.Compare(test.v2); res != test.result {
|
||||
t.Errorf("Comparing %q : %q, expected %d but got %d", test.v1, test.v2, test.result, res)
|
||||
}
|
||||
//Test counterpart
|
||||
if res := test.v2.Compare(test.v1); res != -test.result {
|
||||
t.Errorf("Comparing %q : %q, expected %d but got %d", test.v2, test.v1, -test.result, res)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type wrongformatTest struct {
|
||||
v *Version
|
||||
str string
|
||||
}
|
||||
|
||||
var wrongformatTests = []wrongformatTest{
|
||||
{nil, ""},
|
||||
{nil, "."},
|
||||
{nil, "1."},
|
||||
{nil, ".1"},
|
||||
{nil, "a.b.c"},
|
||||
{nil, "1.a.b"},
|
||||
{nil, "1.1.a"},
|
||||
{nil, "1.a.1"},
|
||||
{nil, "a.1.1"},
|
||||
{nil, ".."},
|
||||
{nil, "1.."},
|
||||
{nil, "1.1."},
|
||||
{nil, "1..1"},
|
||||
{nil, "1.1.+123"},
|
||||
{nil, "1.1.-beta"},
|
||||
{nil, "-1.1.1"},
|
||||
{nil, "1.-1.1"},
|
||||
{nil, "1.1.-1"},
|
||||
// giant numbers
|
||||
{nil, "20000000000000000000.1.1"},
|
||||
{nil, "1.20000000000000000000.1"},
|
||||
{nil, "1.1.20000000000000000000"},
|
||||
{nil, "1.1.1-20000000000000000000"},
|
||||
// Leading zeroes
|
||||
{nil, "01.1.1"},
|
||||
{nil, "001.1.1"},
|
||||
{nil, "1.01.1"},
|
||||
{nil, "1.001.1"},
|
||||
{nil, "1.1.01"},
|
||||
{nil, "1.1.001"},
|
||||
{nil, "1.1.1-01"},
|
||||
{nil, "1.1.1-001"},
|
||||
{nil, "1.1.1-beta.01"},
|
||||
{nil, "1.1.1-beta.001"},
|
||||
{&Version{0, 0, 0, []PRVersion{prstr("!")}, nil}, "0.0.0-!"},
|
||||
{&Version{0, 0, 0, nil, []string{"!"}}, "0.0.0+!"},
|
||||
// empty prversion
|
||||
{&Version{0, 0, 0, []PRVersion{prstr(""), prstr("alpha")}, nil}, "0.0.0-.alpha"},
|
||||
// empty build meta data
|
||||
{&Version{0, 0, 0, []PRVersion{prstr("alpha")}, []string{""}}, "0.0.0-alpha+"},
|
||||
{&Version{0, 0, 0, []PRVersion{prstr("alpha")}, []string{"test", ""}}, "0.0.0-alpha+test."},
|
||||
}
|
||||
|
||||
func TestWrongFormat(t *testing.T) {
|
||||
for _, test := range wrongformatTests {
|
||||
|
||||
if res, err := Parse(test.str); err == nil {
|
||||
t.Errorf("Parsing wrong format version %q, expected error but got %q", test.str, res)
|
||||
}
|
||||
|
||||
if test.v != nil {
|
||||
if err := test.v.Validate(); err == nil {
|
||||
t.Errorf("Validating wrong format version %q (%q), expected error", test.v, test.str)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var wrongTolerantFormatTests = []wrongformatTest{
|
||||
{nil, "1.0+abc"},
|
||||
{nil, "1.0-rc.1"},
|
||||
}
|
||||
|
||||
func TestWrongTolerantFormat(t *testing.T) {
|
||||
for _, test := range wrongTolerantFormatTests {
|
||||
if res, err := ParseTolerant(test.str); err == nil {
|
||||
t.Errorf("Parsing wrong format version %q, expected error but got %q", test.str, res)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompareHelper(t *testing.T) {
|
||||
v := Version{1, 0, 0, []PRVersion{prstr("alpha")}, nil}
|
||||
v1 := Version{1, 0, 0, nil, nil}
|
||||
if !v.EQ(v) {
|
||||
t.Errorf("%q should be equal to %q", v, v)
|
||||
}
|
||||
if !v.Equals(v) {
|
||||
t.Errorf("%q should be equal to %q", v, v)
|
||||
}
|
||||
if !v1.NE(v) {
|
||||
t.Errorf("%q should not be equal to %q", v1, v)
|
||||
}
|
||||
if !v.GTE(v) {
|
||||
t.Errorf("%q should be greater than or equal to %q", v, v)
|
||||
}
|
||||
if !v.LTE(v) {
|
||||
t.Errorf("%q should be less than or equal to %q", v, v)
|
||||
}
|
||||
if !v.LT(v1) {
|
||||
t.Errorf("%q should be less than %q", v, v1)
|
||||
}
|
||||
if !v.LTE(v1) {
|
||||
t.Errorf("%q should be less than or equal %q", v, v1)
|
||||
}
|
||||
if !v.LE(v1) {
|
||||
t.Errorf("%q should be less than or equal %q", v, v1)
|
||||
}
|
||||
if !v1.GT(v) {
|
||||
t.Errorf("%q should be greater than %q", v1, v)
|
||||
}
|
||||
if !v1.GTE(v) {
|
||||
t.Errorf("%q should be greater than or equal %q", v1, v)
|
||||
}
|
||||
if !v1.GE(v) {
|
||||
t.Errorf("%q should be greater than or equal %q", v1, v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPreReleaseVersions(t *testing.T) {
|
||||
p1, err := NewPRVersion("123")
|
||||
if !p1.IsNumeric() {
|
||||
t.Errorf("Expected numeric prversion, got %q", p1)
|
||||
}
|
||||
if p1.VersionNum != 123 {
|
||||
t.Error("Wrong prversion number")
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("Not expected error %q", err)
|
||||
}
|
||||
p2, err := NewPRVersion("alpha")
|
||||
if p2.IsNumeric() {
|
||||
t.Errorf("Expected non-numeric prversion, got %q", p2)
|
||||
}
|
||||
if p2.VersionStr != "alpha" {
|
||||
t.Error("Wrong prversion string")
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("Not expected error %q", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildMetaDataVersions(t *testing.T) {
|
||||
_, err := NewBuildVersion("123")
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error %q", err)
|
||||
}
|
||||
|
||||
_, err = NewBuildVersion("build")
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error %q", err)
|
||||
}
|
||||
|
||||
_, err = NewBuildVersion("test?")
|
||||
if err == nil {
|
||||
t.Error("Expected error, got none")
|
||||
}
|
||||
|
||||
_, err = NewBuildVersion("")
|
||||
if err == nil {
|
||||
t.Error("Expected error, got none")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewHelper(t *testing.T) {
|
||||
v, err := New("1.2.3")
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error %q", err)
|
||||
}
|
||||
|
||||
// New returns pointer
|
||||
if v == nil {
|
||||
t.Fatal("Version is nil")
|
||||
}
|
||||
if v.Compare(Version{1, 2, 3, nil, nil}) != 0 {
|
||||
t.Fatal("Unexpected comparison problem")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMakeHelper(t *testing.T) {
|
||||
v, err := Make("1.2.3")
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error %q", err)
|
||||
}
|
||||
if v.Compare(Version{1, 2, 3, nil, nil}) != 0 {
|
||||
t.Fatal("Unexpected comparison problem")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkParseSimple(b *testing.B) {
|
||||
const VERSION = "0.0.1"
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
Parse(VERSION)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkParseComplex(b *testing.B) {
|
||||
const VERSION = "0.0.1-alpha.preview+123.456"
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
Parse(VERSION)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkParseAverage(b *testing.B) {
|
||||
l := len(formatTests)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
Parse(formatTests[n%l].result)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkParseTolerantAverage(b *testing.B) {
|
||||
l := len(tolerantFormatTests)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
ParseTolerant(tolerantFormatTests[n%l].result)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkStringSimple(b *testing.B) {
|
||||
const VERSION = "0.0.1"
|
||||
v, _ := Parse(VERSION)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
v.String()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkStringLarger(b *testing.B) {
|
||||
const VERSION = "11.15.2012"
|
||||
v, _ := Parse(VERSION)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
v.String()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkStringComplex(b *testing.B) {
|
||||
const VERSION = "0.0.1-alpha.preview+123.456"
|
||||
v, _ := Parse(VERSION)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
v.String()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkStringAverage(b *testing.B) {
|
||||
l := len(formatTests)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
formatTests[n%l].v.String()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkValidateSimple(b *testing.B) {
|
||||
const VERSION = "0.0.1"
|
||||
v, _ := Parse(VERSION)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
v.Validate()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkValidateComplex(b *testing.B) {
|
||||
const VERSION = "0.0.1-alpha.preview+123.456"
|
||||
v, _ := Parse(VERSION)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
v.Validate()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkValidateAverage(b *testing.B) {
|
||||
l := len(formatTests)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
formatTests[n%l].v.Validate()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCompareSimple(b *testing.B) {
|
||||
const VERSION = "0.0.1"
|
||||
v, _ := Parse(VERSION)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
v.Compare(v)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCompareComplex(b *testing.B) {
|
||||
const VERSION = "0.0.1-alpha.preview+123.456"
|
||||
v, _ := Parse(VERSION)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
v.Compare(v)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCompareAverage(b *testing.B) {
|
||||
l := len(compareTests)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
compareTests[n%l].v1.Compare((compareTests[n%l].v2))
|
||||
}
|
||||
}
|
28
src/vendor/github.com/blang/semver/sort.go
generated
vendored
Normal file
28
src/vendor/github.com/blang/semver/sort.go
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
package semver
|
||||
|
||||
import (
|
||||
"sort"
|
||||
)
|
||||
|
||||
// Versions represents multiple versions.
|
||||
type Versions []Version
|
||||
|
||||
// Len returns length of version collection
|
||||
func (s Versions) Len() int {
|
||||
return len(s)
|
||||
}
|
||||
|
||||
// Swap swaps two versions inside the collection by its indices
|
||||
func (s Versions) Swap(i, j int) {
|
||||
s[i], s[j] = s[j], s[i]
|
||||
}
|
||||
|
||||
// Less checks if version at index i is less than version at index j
|
||||
func (s Versions) Less(i, j int) bool {
|
||||
return s[i].LT(s[j])
|
||||
}
|
||||
|
||||
// Sort sorts a slice of versions
|
||||
func Sort(versions []Version) {
|
||||
sort.Sort(Versions(versions))
|
||||
}
|
30
src/vendor/github.com/blang/semver/sort_test.go
generated
vendored
Normal file
30
src/vendor/github.com/blang/semver/sort_test.go
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
package semver
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSort(t *testing.T) {
|
||||
v100, _ := Parse("1.0.0")
|
||||
v010, _ := Parse("0.1.0")
|
||||
v001, _ := Parse("0.0.1")
|
||||
versions := []Version{v010, v100, v001}
|
||||
Sort(versions)
|
||||
|
||||
correct := []Version{v001, v010, v100}
|
||||
if !reflect.DeepEqual(versions, correct) {
|
||||
t.Fatalf("Sort returned wrong order: %s", versions)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSort(b *testing.B) {
|
||||
v100, _ := Parse("1.0.0")
|
||||
v010, _ := Parse("0.1.0")
|
||||
v001, _ := Parse("0.0.1")
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
Sort([]Version{v010, v100, v001})
|
||||
}
|
||||
}
|
30
src/vendor/github.com/blang/semver/sql.go
generated
vendored
Normal file
30
src/vendor/github.com/blang/semver/sql.go
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
package semver
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Scan implements the database/sql.Scanner interface.
|
||||
func (v *Version) Scan(src interface{}) (err error) {
|
||||
var str string
|
||||
switch src := src.(type) {
|
||||
case string:
|
||||
str = src
|
||||
case []byte:
|
||||
str = string(src)
|
||||
default:
|
||||
return fmt.Errorf("Version.Scan: cannot convert %T to string.", src)
|
||||
}
|
||||
|
||||
if t, err := Parse(str); err == nil {
|
||||
*v = t
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver.Valuer interface.
|
||||
func (v Version) Value() (driver.Value, error) {
|
||||
return v.String(), nil
|
||||
}
|
38
src/vendor/github.com/blang/semver/sql_test.go
generated
vendored
Normal file
38
src/vendor/github.com/blang/semver/sql_test.go
generated
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
package semver
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
type scanTest struct {
|
||||
val interface{}
|
||||
shouldError bool
|
||||
expected string
|
||||
}
|
||||
|
||||
var scanTests = []scanTest{
|
||||
{"1.2.3", false, "1.2.3"},
|
||||
{[]byte("1.2.3"), false, "1.2.3"},
|
||||
{7, true, ""},
|
||||
{7e4, true, ""},
|
||||
{true, true, ""},
|
||||
}
|
||||
|
||||
func TestScanString(t *testing.T) {
|
||||
for _, tc := range scanTests {
|
||||
s := &Version{}
|
||||
err := s.Scan(tc.val)
|
||||
if tc.shouldError {
|
||||
if err == nil {
|
||||
t.Fatalf("Scan did not return an error on %v (%T)", tc.val, tc.val)
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Fatalf("Scan returned an unexpected error: %s (%T) on %v (%T)", tc.val, tc.val, tc.val, tc.val)
|
||||
}
|
||||
if val, _ := s.Value(); val != tc.expected {
|
||||
t.Errorf("Wrong Value returned, expected %q, got %q", tc.expected, val)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user