Chromium Code Reviews| Index: vpython/python/version.go |
| diff --git a/vpython/python/version.go b/vpython/python/version.go |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..5d4639773bfc0166487c5b0c0333fd833a3dfc6a |
| --- /dev/null |
| +++ b/vpython/python/version.go |
| @@ -0,0 +1,144 @@ |
| +// Copyright 2017 The LUCI Authors. All rights reserved. |
| +// Use of this source code is governed under the Apache License, Version 2.0 |
| +// that can be found in the LICENSE file. |
| + |
| +package python |
| + |
| +import ( |
| + "fmt" |
| + "strconv" |
| + "strings" |
| + |
| + "github.com/luci/luci-go/common/errors" |
| +) |
| + |
| +// Version is a Python interpreter version. |
| +type Version struct { |
| + Major int |
| + Minor int |
| + Patch int |
| +} |
| + |
| +// ParseVersion parses a Python version from a version string (e.g., "1.2.3"). |
| +func ParseVersion(s string) (Version, error) { |
| + if len(s) == 0 { |
| + return Version{}, nil |
| + } |
| + |
| + parseVersion := func(value string) (int, error) { |
| + version, err := strconv.Atoi(value) |
| + if err != nil { |
| + return 0, errors.Annotate(err).Reason("invalid number value: %(value)q"). |
| + D("value", value). |
| + Err() |
| + } |
| + if version < 0 { |
| + return 0, errors.Reason("version (%(version)d) must not be negative"). |
| + D("version", version). |
| + Err() |
| + } |
| + return version, nil |
| + } |
| + |
| + var v Version |
| + parts := strings.Split(s, ".") |
| + var err error |
|
iannucci
2017/02/21 10:08:16
named return values would simplify this code I thi
dnj
2017/02/21 23:21:35
I dunno, i don't really mind it as-is.
|
| + switch l := len(parts); l { |
| + case 3: |
| + if v.Patch, err = parseVersion(parts[2]); err != nil { |
| + return v, errors.Annotate(err).Reason("invalid patch value").Err() |
| + } |
| + fallthrough |
| + |
| + case 2: |
| + if v.Minor, err = parseVersion(parts[1]); err != nil { |
| + return v, errors.Annotate(err).Reason("invalid minor value").Err() |
| + } |
| + fallthrough |
| + |
| + case 1: |
| + if v.Major, err = parseVersion(parts[0]); err != nil { |
| + return v, errors.Annotate(err).Reason("invalid major value").Err() |
| + } |
| + if v.IsZero() { |
| + return v, errors.Reason("version is incomplete").Err() |
| + } |
| + return v, nil |
| + |
| + default: |
| + return v, errors.Reason("unsupported number of parts (%(count)d)"). |
| + D("count", l). |
| + Err() |
| + } |
| +} |
| + |
| +func (v Version) String() string { |
| + if v.IsZero() { |
| + return "" |
| + } |
| + return fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch) |
| +} |
| + |
| +// IsZero returns true if the Version is empty. This is true if the Major field, |
| +// which must be set, is empty. |
| +func (v *Version) IsZero() bool { return v.Major <= 0 } |
| + |
| +// PythonBase returns the base Python interpreter name for this version. |
| +func (v *Version) PythonBase() string { |
| + switch { |
| + case v.IsZero(): |
| + return "python" |
| + case v.Minor > 0: |
| + return fmt.Sprintf("python%d.%d", v.Major, v.Minor) |
| + default: |
| + return fmt.Sprintf("python%d", v.Major) |
| + } |
| +} |
| + |
| +// IsSatisfiedBy returns true if "other" is a suitable match for this version. A |
| +// suitable match: |
| +// |
| +// - MUST have a Major version. |
| +// - If v is zero, other is automatically suitable. |
| +// - If v is non-zero, other must have the same Major version as v, and a |
| +// minor/patch version that is >= v's. |
| +func (v *Version) IsSatisfiedBy(other Version) bool { |
| + switch { |
| + case other.Major <= 0: |
| + // "other" must have a Major version. |
| + return false |
| + case v.IsZero(): |
| + // "v" is zero (anything), so "other" satisfies it. |
| + return true |
| + case v.Major != other.Major: |
| + // "other" must match "v"'s Major version precisely. |
| + return false |
| + case v.Minor > other.Minor: |
| + // "v" requires a Minor version that is greater than "other"'s. |
| + return false |
| + case v.Minor < other.Minor: |
| + // "v" requires a Minor version that is less than "other"'s. |
| + return true |
| + case v.Patch > other.Patch: |
| + // "v" requires a Patch version that is greater than "other"'s. |
| + return false |
| + default: |
| + return true |
| + } |
| +} |
| + |
| +// Less returns true if "v"'s Version semantically precedes "other". |
| +func (v *Version) Less(other *Version) bool { |
| + switch { |
| + case v.Major < other.Major: |
| + return true |
| + case v.Major > other.Major: |
| + return false |
| + case v.Minor < other.Minor: |
| + return true |
| + case v.Minor > other.Minor: |
| + return false |
| + default: |
| + return (v.Patch < other.Patch) |
| + } |
| +} |