diff --git a/constraint.go b/constraint.go index 96f6d83..c2099f9 100644 --- a/constraint.go +++ b/constraint.go @@ -1,6 +1,7 @@ package version import ( + "encoding/json" "fmt" "regexp" "strings" @@ -156,3 +157,51 @@ func constraintPessimistic(v, c *Version) bool { return true } + +// MarshalJSON - implement the json-Marshaler interface +func (c *Constraints) MarshalJSON() ([]byte, error) { + return json.Marshal(c.String()) +} + +// UnmarshalJSON - implement the json-Unmarshaler interface +func (c *Constraints) UnmarshalJSON(data []byte) (err error) { + var constraintStr string + var nc Constraints + + err = json.Unmarshal(data, &constraintStr) + if err != nil { + return + } + + nc, err = NewConstraint(constraintStr) + if err != nil { + return + } + *c = nc + + return +} + +// MarshalYAML - implement the YAML-Marshaler interface (gopkg.in/yaml.v2) +func (c *Constraints) MarshalYAML() (str interface{}, err error) { + str = c.String() + return +} + +// UnmarshalYAML - implement the yaml-Unmarshaler interface (gopkg.in/yaml.v2) +func (c *Constraints) UnmarshalYAML(unmarshal func(interface{}) error) (err error) { + var constraintStr string + var nc Constraints + + if err = unmarshal(&constraintStr); err != nil { + return + } + + nc, err = NewConstraint(constraintStr) + if err != nil { + return + } + *c = nc + + return +} diff --git a/constraint_test.go b/constraint_test.go index 6581df0..18a2b77 100644 --- a/constraint_test.go +++ b/constraint_test.go @@ -1,6 +1,8 @@ package version import ( + "bytes" + "encoding/json" "testing" ) @@ -98,3 +100,28 @@ func TestConstraintsString(t *testing.T) { } } } + +func TestConstraintsJson(t *testing.T) { + type MyStruct struct { + MustVer Constraints + } + var ( + vc MyStruct + err error + ) + jsBytes := []byte(`{"MustVer":"=1.2, =1.3"}`) + // data -> struct + err = json.Unmarshal(jsBytes, &vc) + if err != nil { + t.Fatalf("expected: json.Unmarshal to succeed\nactual: failed with error %v", err) + } + // struct -> data + data, err := json.Marshal(&vc) + if err != nil { + t.Fatalf("expected: json.Marshal to succeed\nactual: failed with error %v", err) + } + + if !bytes.Equal(data, jsBytes) { + t.Fatalf("expected: %s\nactual: %s", jsBytes, data) + } +} diff --git a/version.go b/version.go index d0e0b0c..cd217c6 100644 --- a/version.go +++ b/version.go @@ -2,6 +2,7 @@ package version import ( "bytes" + "encoding/json" "fmt" "reflect" "regexp" @@ -19,6 +20,20 @@ const VersionRegexpRaw string = `([0-9]+(\.[0-9]+){0,2})` + `(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` + `?` +type VersionPart int + +const ( + MajorPart VersionPart = iota + MinorPart + PatchPart + PreReleasePart + MetadataPart +) + +var partNames = [...]string{ + "major", "minor", "patch", "prerelease", "metadata", +} + // Version represents a single version. type Version struct { metadata string @@ -249,3 +264,61 @@ func (v *Version) String() string { return buf.String() } + +// BumpVersion - increment the indicated part by one +// part may be one of: MajorPart, MinorPart or PatchPart +func (v *Version) BumpVersion(part VersionPart) (err error) { + switch part { + case MajorPart, MinorPart, PatchPart: + v.segments[part]++ + default: + err = fmt.Errorf("unable to bump version part %s", partNames[part]) + } + return +} + +// MarshalJSON - implement the json-Marshaler interface +func (v *Version) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, v.String())), nil +} + +// UnmarshalJSON - implement the json-Unmarshaler interface +func (v *Version) UnmarshalJSON(data []byte) (err error) { + var verStr string + var nv *Version + + err = json.Unmarshal(data, &verStr) + if err != nil { + return + } + + nv, err = NewVersion(verStr) + if err != nil { + return + } + *v = *nv + + return +} + +// MarshalYAML - implement the YAML-Marshaler interface (gopkg.in/yaml.v2) +func (v *Version) MarshalYAML() (str interface{}, err error) { + str = v.String() + return +} + +// UnmarshalYAML - implement the yaml-Unmarshaler interface (gopkg.in/yaml.v2) +func (v *Version) UnmarshalYAML(unmarshal func(interface{}) error) (err error) { + var ( + verStr string + nv *Version + ) + if err = unmarshal(&verStr); err != nil { + return + } + if nv, err = NewVersion(verStr); err != nil { + return + } + *v = *nv + return +} diff --git a/version_test.go b/version_test.go index cc96a04..5caba17 100644 --- a/version_test.go +++ b/version_test.go @@ -1,6 +1,8 @@ package version import ( + "bytes" + "encoding/json" "reflect" "testing" ) @@ -206,3 +208,64 @@ func TestVersionString(t *testing.T) { } } } + +func TestBumpVersion(t *testing.T) { + cases := []struct { + version string + part VersionPart + result string + err bool + }{ + {"1.1.1", MajorPart, "2.1.1", false}, + {"1.1.1", MinorPart, "1.2.1", false}, + {"1.1.1", PatchPart, "1.1.2", false}, + {"2", MinorPart, "2.1.0", false}, + {"2.2", PatchPart, "2.2.1", false}, + {"1.1.0-beta1", MinorPart, "1.2.0-beta1", false}, + {"1.1.0-beta1", PreReleasePart, "", true}, + {"1.1.0-beta1+foo", MetadataPart, "", true}, + } + + for _, tc := range cases { + v, err := NewVersion(tc.version) + if err != nil { + t.Fatalf("error parsing version %s", tc.version) + } + err = v.BumpVersion(tc.part) + if tc.err && err == nil { + t.Fatalf("expected error for version: %s", tc.version) + } else if !tc.err && err != nil { + t.Fatalf("error for version %s: %s", tc.version, err) + } + if !tc.err { + if v.String() != tc.result { + t.Fatalf("BumpVersion %d, expecting: %s\nfound %s", tc.part, tc.result, v.String()) + } + } + } +} + +func TestVersionJSON(t *testing.T) { + type MyStruct struct { + Ver *Version + } + var ( + ver MyStruct + err error + ) + jsBytes := []byte(`{"Ver":"1.2.3"}`) + // data -> struct + err = json.Unmarshal(jsBytes, &ver) + if err != nil { + t.Fatalf("expected: json.Unmarshal to succeed\nactual: failed with error %v", err) + } + // struct -> data + data, err := json.Marshal(&ver) + if err != nil { + t.Fatalf("expected: json.Marshal to succeed\nactual: failed with error %v", err) + } + + if !bytes.Equal(data, jsBytes) { + t.Fatalf("expected: %s\nactual: %s", jsBytes, data) + } +}