You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
clair/ext/versionfmt/driver.go

152 lines
4.4 KiB

// Copyright 2016 clair authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package versionfmt exposes functions to dynamically register formats used to
// parse Feature Versions.
package versionfmt
import (
"errors"
"sync"
log "github.com/sirupsen/logrus"
)
const (
// MinVersion is a special package version which is always sorted first.
MinVersion = "#MINV#"
// MaxVersion is a special package version which is always sorted last.
MaxVersion = "#MAXV#"
)
var (
// ErrUnknownVersionFormat is returned when a function does not have enough
// context to determine the format of a version.
ErrUnknownVersionFormat = errors.New("unknown version format")
// ErrInvalidVersion is returned when a function needs to validate a version,
// but should return an error in the case where the version is invalid.
ErrInvalidVersion = errors.New("invalid version")
parsersM sync.Mutex
parsers = make(map[string]Parser)
)
// Parser represents any format that can compare two version strings.
type Parser interface {
// Valid attempts to parse a version string and returns its success.
Valid(string) bool
// Compare parses two different version strings.
// Returns 0 when equal, -1 when a < b, 1 when b < a.
Compare(a, b string) (int, error)
// InRange computes if a is in range of b
//
// NOTE(Sida): For legacy version formats, rangeB is a version and
// always use if versionA < rangeB as threshold.
InRange(versionA, rangeB string) (bool, error)
// GetFixedIn computes a fixed in version for a certain version range.
//
// NOTE(Sida): For legacy version formats, rangeA is a version and
// be returned directly becuase it was considered fixed in version.
GetFixedIn(rangeA string) (string, error)
}
// RegisterParser provides a way to dynamically register an implementation of a
// Parser.
//
// If RegisterParser is called twice with the same name, the name is blank, or
// if the provided Parser is nil, this function panics.
func RegisterParser(name string, p Parser) {
if name == "" {
panic("versionfmt: could not register a Parser with an empty name")
}
if p == nil {
panic("versionfmt: could not register a nil Parser")
}
parsersM.Lock()
defer parsersM.Unlock()
if _, dup := parsers[name]; dup {
panic("versionfmt: RegisterParser called twice for " + name)
}
parsers[name] = p
}
// GetParser returns the registered Parser with a provided name.
func GetParser(name string) (p Parser, exists bool) {
parsersM.Lock()
defer parsersM.Unlock()
p, exists = parsers[name]
return
}
// Valid is a helper function that will return an error if the version fails to
// validate with a given format.
func Valid(format, version string) error {
versionParser, exists := GetParser(format)
if !exists {
return ErrUnknownVersionFormat
}
if !versionParser.Valid(version) {
return ErrInvalidVersion
}
return nil
}
// Compare is a helper function that will compare two versions with a given
// format and return an error if there are any failures.
func Compare(format, versionA, versionB string) (int, error) {
versionParser, exists := GetParser(format)
if !exists {
return 0, ErrUnknownVersionFormat
}
return versionParser.Compare(versionA, versionB)
}
// InRange is a helper function that checks if `versionA` is in `rangeB`
func InRange(format, version, versionRange string) (bool, error) {
versionParser, exists := GetParser(format)
if !exists {
return false, ErrUnknownVersionFormat
}
in, err := versionParser.InRange(version, versionRange)
if err != nil {
log.WithFields(log.Fields{"Format": format, "Version": version, "Range": versionRange}).Error(err)
}
return in, err
}
// GetFixedIn is a helper function that computes the next fixed in version given
// a affected version range `rangeA`.
func GetFixedIn(format, rangeA string) (string, error) {
versionParser, exists := GetParser(format)
if !exists {
return "", ErrUnknownVersionFormat
}
return versionParser.GetFixedIn(rangeA)
}