Permalink
Cannot retrieve contributors at this time
Fetching contributors…
| // Copyright 2012, 2013 Canonical Ltd. | |
| // Licensed under the AGPLv3, see LICENCE file for details. | |
| // Package version implements version parsing. | |
| package version | |
| import ( | |
| "encoding/json" | |
| "fmt" | |
| "regexp" | |
| "strconv" | |
| "strings" | |
| "gopkg.in/mgo.v2/bson" | |
| ) | |
| // Number represents a version number. | |
| type Number struct { | |
| Major int | |
| Minor int | |
| Tag string | |
| Patch int | |
| Build int | |
| } | |
| // Zero is occasionally convenient and readable. | |
| // Please don't change its value. | |
| var Zero = Number{} | |
| // Binary specifies a binary version of juju.v | |
| type Binary struct { | |
| Number | |
| Series string | |
| Arch string | |
| } | |
| // String returns the string representation of the binary version. | |
| func (b Binary) String() string { | |
| return fmt.Sprintf("%v-%s-%s", b.Number, b.Series, b.Arch) | |
| } | |
| // GetBSON implements bson.Getter. | |
| func (b Binary) GetBSON() (interface{}, error) { | |
| return b.String(), nil | |
| } | |
| // SetBSON implements bson.Setter. | |
| func (b *Binary) SetBSON(raw bson.Raw) error { | |
| var s string | |
| err := raw.Unmarshal(&s) | |
| if err != nil { | |
| return err | |
| } | |
| v, err := ParseBinary(s) | |
| if err != nil { | |
| return err | |
| } | |
| *b = v | |
| return nil | |
| } | |
| // MarshalJSON implements json.Marshaler. | |
| func (b Binary) MarshalJSON() ([]byte, error) { | |
| return json.Marshal(b.String()) | |
| } | |
| // UnmarshalJSON implements json.Unmarshaler. | |
| func (b *Binary) UnmarshalJSON(data []byte) error { | |
| var s string | |
| if err := json.Unmarshal(data, &s); err != nil { | |
| return err | |
| } | |
| v, err := ParseBinary(s) | |
| if err != nil { | |
| return err | |
| } | |
| *b = v | |
| return nil | |
| } | |
| // MarshalYAML implements yaml.v2.Marshaller interface. | |
| func (b Binary) MarshalYAML() (interface{}, error) { | |
| return b.String(), nil | |
| } | |
| // UnmarshalYAML implements the yaml.Unmarshaller interface. | |
| func (b *Binary) UnmarshalYAML(unmarshal func(interface{}) error) error { | |
| var vstr string | |
| err := unmarshal(&vstr) | |
| if err != nil { | |
| return err | |
| } | |
| v, err := ParseBinary(vstr) | |
| if err != nil { | |
| return err | |
| } | |
| *b = v | |
| return nil | |
| } | |
| var ( | |
| binaryPat = regexp.MustCompile(`^(\d{1,9})\.(\d{1,9})(?:\.|-([a-z]+))(\d{1,9})(\.\d{1,9})?-([^-]+)-([^-]+)$`) | |
| numberPat = regexp.MustCompile(`^(\d{1,9})\.(\d{1,9})(?:\.|-([a-z]+))(\d{1,9})(\.\d{1,9})?$`) | |
| ) | |
| // MustParse parses a version and panics if it does | |
| // not parse correctly. | |
| func MustParse(s string) Number { | |
| v, err := Parse(s) | |
| if err != nil { | |
| panic(err) | |
| } | |
| return v | |
| } | |
| // MustParseBinary parses a binary version and panics if it does | |
| // not parse correctly. | |
| func MustParseBinary(s string) Binary { | |
| b, err := ParseBinary(s) | |
| if err != nil { | |
| panic(err) | |
| } | |
| return b | |
| } | |
| // ParseBinary parses a binary version of the form "1.2.3-series-arch". | |
| func ParseBinary(s string) (Binary, error) { | |
| m := binaryPat.FindStringSubmatch(s) | |
| if m == nil { | |
| return Binary{}, fmt.Errorf("invalid binary version %q", s) | |
| } | |
| var b Binary | |
| b.Major = atoi(m[1]) | |
| b.Minor = atoi(m[2]) | |
| b.Tag = m[3] | |
| b.Patch = atoi(m[4]) | |
| if m[5] != "" { | |
| b.Build = atoi(m[5][1:]) | |
| } | |
| b.Series = m[6] | |
| b.Arch = m[7] | |
| return b, nil | |
| } | |
| // Parse parses the version, which is of the form 1.2.3 | |
| // giving the major, minor and release versions | |
| // respectively. | |
| func Parse(s string) (Number, error) { | |
| m := numberPat.FindStringSubmatch(s) | |
| if m == nil { | |
| return Number{}, fmt.Errorf("invalid version %q", s) | |
| } | |
| var n Number | |
| n.Major = atoi(m[1]) | |
| n.Minor = atoi(m[2]) | |
| n.Tag = m[3] | |
| n.Patch = atoi(m[4]) | |
| if m[5] != "" { | |
| n.Build = atoi(m[5][1:]) | |
| } | |
| return n, nil | |
| } | |
| // atoi is the same as strconv.Atoi but assumes that | |
| // the string has been verified to be a valid integer. | |
| func atoi(s string) int { | |
| n, err := strconv.Atoi(s) | |
| if err != nil { | |
| panic(err) | |
| } | |
| return n | |
| } | |
| // String returns the string representation of this Number. | |
| func (n Number) String() string { | |
| var s string | |
| if n.Tag == "" { | |
| s = fmt.Sprintf("%d.%d.%d", n.Major, n.Minor, n.Patch) | |
| } else { | |
| s = fmt.Sprintf("%d.%d-%s%d", n.Major, n.Minor, n.Tag, n.Patch) | |
| } | |
| if n.Build > 0 { | |
| s += fmt.Sprintf(".%d", n.Build) | |
| } | |
| return s | |
| } | |
| // Compare returns -1, 0 or 1 depending on whether | |
| // n is less than, equal to or greater than other. | |
| // The comparison compares Major, then Minor, then Patch, then Build, using the first difference as | |
| func (n Number) Compare(other Number) int { | |
| if n == other { | |
| return 0 | |
| } | |
| less := false | |
| switch { | |
| case n.Major != other.Major: | |
| less = n.Major < other.Major | |
| case n.Minor != other.Minor: | |
| less = n.Minor < other.Minor | |
| case n.Tag != other.Tag: | |
| switch { | |
| case n.Tag == "": | |
| less = false | |
| case other.Tag == "": | |
| less = true | |
| default: | |
| less = n.Tag < other.Tag | |
| } | |
| case n.Patch != other.Patch: | |
| less = n.Patch < other.Patch | |
| case n.Build != other.Build: | |
| less = n.Build < other.Build | |
| } | |
| if less { | |
| return -1 | |
| } | |
| return 1 | |
| } | |
| // GetBSON implements bson.Getter. | |
| func (n Number) GetBSON() (interface{}, error) { | |
| return n.String(), nil | |
| } | |
| // SetBSON implements bson.Setter. | |
| func (n *Number) SetBSON(raw bson.Raw) error { | |
| var s string | |
| err := raw.Unmarshal(&s) | |
| if err != nil { | |
| return err | |
| } | |
| v, err := Parse(s) | |
| if err != nil { | |
| return err | |
| } | |
| *n = v | |
| return nil | |
| } | |
| // MarshalJSON implements json.Marshaler. | |
| func (n Number) MarshalJSON() ([]byte, error) { | |
| return json.Marshal(n.String()) | |
| } | |
| // UnmarshalJSON implements json.Unmarshaler. | |
| func (n *Number) UnmarshalJSON(data []byte) error { | |
| var s string | |
| if err := json.Unmarshal(data, &s); err != nil { | |
| return err | |
| } | |
| v, err := Parse(s) | |
| if err != nil { | |
| return err | |
| } | |
| *n = v | |
| return nil | |
| } | |
| // MarshalYAML implements yaml.v2.Marshaller interface | |
| func (n Number) MarshalYAML() (interface{}, error) { | |
| return n.String(), nil | |
| } | |
| // UnmarshalYAML implements the yaml.Unmarshaller interface | |
| func (n *Number) UnmarshalYAML(unmarshal func(interface{}) error) error { | |
| var vstr string | |
| err := unmarshal(&vstr) | |
| if err != nil { | |
| return err | |
| } | |
| v, err := Parse(vstr) | |
| if err != nil { | |
| return err | |
| } | |
| *n = v | |
| return nil | |
| } | |
| // ParseMajorMinor takes an argument of the form "major.minor" and returns ints major and minor. | |
| func ParseMajorMinor(vers string) (int, int, error) { | |
| parts := strings.Split(vers, ".") | |
| major, err := strconv.Atoi(parts[0]) | |
| minor := -1 | |
| if err != nil { | |
| return -1, -1, fmt.Errorf("invalid major version number %s: %v", parts[0], err) | |
| } | |
| if len(parts) == 2 { | |
| minor, err = strconv.Atoi(parts[1]) | |
| if err != nil { | |
| return -1, -1, fmt.Errorf("invalid minor version number %s: %v", parts[1], err) | |
| } | |
| } else if len(parts) > 2 { | |
| return -1, -1, fmt.Errorf("invalid major.minor version number %s", vers) | |
| } | |
| return major, minor, nil | |
| } |