diff --git a/region/region.go b/region/region.go index 200101e..b8c4adf 100644 --- a/region/region.go +++ b/region/region.go @@ -31,13 +31,13 @@ type Regions []Region // which all of the other region information can be derived. The other environment // variables are optional and can be used to override the default values. type Info struct { - ID Region `json:"region" msg:"region" env:"REGION_INFO_ID" split_words:"true" desc:"the r8l specific region identifier code (must be valid if not empty)"` - Name string `json:"name" msg:"name" env:"REGION_INFO_NAME" split_words:"true" desc:"the human readable name of the region"` - CountryCode string `json:"country_code" msg:"country_code" env:"REGION_INFO_COUNTRY" split_words:"true" desc:"the ISO 3166-1 country code for the region"` - Zone string `json:"zone" msg:"zone" env:"REGION_INFO_ZONE" split_words:"true" desc:"the zone of the datacenter for the region"` - Cloud string `json:"cloud" msg:"cloud" env:"REGION_INFO_CLOUD" split_words:"true" desc:"the cloud provider for the region"` - Cluster string `json:"cluster" msg:"cluster" env:"REGION_INFO_CLUSTER" split_words:"true" desc:"the r8l cluster name for the region"` - country *country.Country `json:"-" msg:"-" env:"-"` + ID Region `json:"region" yaml:"region" msg:"region" env:"REGION_INFO_ID" split_words:"true" desc:"the r8l specific region identifier code (must be valid if not empty)"` + Name string `json:"name" yaml:"name" msg:"name" env:"REGION_INFO_NAME" split_words:"true" desc:"the human readable name of the region"` + CountryCode string `json:"country_code" yaml:"country_code" msg:"country_code" env:"REGION_INFO_COUNTRY" split_words:"true" desc:"the ISO 3166-1 country code for the region"` + Zone string `json:"zone" yaml:"zone" msg:"zone" env:"REGION_INFO_ZONE" split_words:"true" desc:"the zone of the datacenter for the region"` + Cloud string `json:"cloud" yaml:"cloud" msg:"cloud" env:"REGION_INFO_CLOUD" split_words:"true" desc:"the cloud provider for the region"` + Cluster string `json:"cluster" yaml:"cluster" msg:"cluster" env:"REGION_INFO_CLUSTER" split_words:"true" desc:"the r8l cluster name for the region"` + country *country.Country `json:"-" yaml:"-" msg:"-" env:"-"` } // Returns a list of all available regions known by the Rotational system. diff --git a/semver/errors.go b/semver/errors.go index 243ced6..e5fbc26 100644 --- a/semver/errors.go +++ b/semver/errors.go @@ -6,4 +6,5 @@ var ( ErrInvalidSemVer = errors.New("invalid semantic version") ErrInvalidRange = errors.New("invalid semantic range") ErrScanValue = errors.New("could not scan source value") + ErrDataSize = errors.New("invalid size for data") ) diff --git a/semver/semver.go b/semver/semver.go index f497238..f7de7fd 100644 --- a/semver/semver.go +++ b/semver/semver.go @@ -14,6 +14,7 @@ package semver import ( + "encoding/binary" "encoding/json" "fmt" "regexp" @@ -229,6 +230,90 @@ func isNumeric(s string) (v uint16, ok bool) { // Serialization and Deserialization //=========================================================================== +func (v Version) MarshalBinary() (data []byte, err error) { + pr := []byte(v.PreRelease) + bm := []byte(v.BuildMeta) + + size := 5*binary.MaxVarintLen16 + len(pr) + len(bm) + data = make([]byte, size) + + i := binary.PutUvarint(data, uint64(v.Major)) + i += binary.PutUvarint(data[i:], uint64(v.Minor)) + i += binary.PutUvarint(data[i:], uint64(v.Patch)) + i += binary.PutUvarint(data[i:], uint64(len(pr))) + i += copy(data[i:], pr) + i += binary.PutUvarint(data[i:], uint64(len(bm))) + i += copy(data[i:], bm) + + data = data[:i:i] + return data, nil +} + +func (v *Version) UnmarshalBinary(data []byte) error { + if len(data) < 5 { + return ErrDataSize + } + + var ( + i, j int + c uint64 + ) + + c, j = binary.Uvarint(data[i:]) + if j <= 0 { + return ErrDataSize + } + v.Major = uint16(c) + + i += j + c, j = binary.Uvarint(data[i:]) + if j <= 0 { + return ErrDataSize + } + v.Minor = uint16(c) + + i += j + c, j = binary.Uvarint(data[i:]) + if j <= 0 { + return ErrDataSize + } + v.Patch = uint16(c) + + i += j + c, j = binary.Uvarint(data[i:]) + if j <= 0 { + return ErrDataSize + } + + i += j + if c > 0 { + if int(c) > len(data[i:]) { + return ErrDataSize + } + v.PreRelease = string(data[i : i+int(c)]) + } else { + v.PreRelease = "" + } + + i += int(c) + c, j = binary.Uvarint(data[i:]) + if j <= 0 { + return ErrDataSize + } + + i += j + if c > 0 { + if int(c) > len(data[i:]) { + return ErrDataSize + } + v.BuildMeta = string(data[i : i+int(c)]) + } else { + v.BuildMeta = "" + } + + return nil +} + func (v Version) MarshalText() ([]byte, error) { return []byte(v.String()), nil } @@ -262,6 +347,25 @@ func (v *Version) UnmarshalJSON(data []byte) error { return nil } +func (v Version) MarshalYAML() (interface{}, error) { + return v.String(), nil +} + +func (v *Version) UnmarshalYAML(unmarshal func(interface{}) error) error { + var vers string + if err := unmarshal(&vers); err != nil { + return err + } + + parsed, err := Parse(vers) + if err != nil { + return err + } + + *v = parsed + return nil +} + //=========================================================================== // SQL Interfaces //=========================================================================== diff --git a/semver/semver_test.go b/semver/semver_test.go index 823ec6e..4710264 100644 --- a/semver/semver_test.go +++ b/semver/semver_test.go @@ -143,8 +143,22 @@ func TestSpecifies(t *testing.T) { } func TestMarshal(t *testing.T) { + t.Run("Binary", func(t *testing.T) { + for i := 0; i < 128; i++ { + a := randVersion() + data, err := a.MarshalBinary() + assert.Ok(t, err) + + b := Version{} + err = b.UnmarshalBinary(data) + assert.Ok(t, err) + + assert.Equal(t, a, b) + } + }) + t.Run("Text", func(t *testing.T) { - for i := 0; i < 100; i++ { + for i := 0; i < 128; i++ { a := randVersion() text, err := a.MarshalText() assert.Ok(t, err) @@ -159,19 +173,39 @@ func TestMarshal(t *testing.T) { }) t.Run("JSON", func(t *testing.T) { - a := randVersion() - text, err := json.Marshal(a) - assert.Ok(t, err) + for i := 0; i < 128; i++ { + a := randVersion() + text, err := json.Marshal(a) + assert.Ok(t, err) - var s string - assert.Ok(t, json.Unmarshal(text, &s)) - assert.True(t, Valid(s)) + var s string + assert.Ok(t, json.Unmarshal(text, &s)) + assert.True(t, Valid(s)) - b := Version{} - err = json.Unmarshal(text, &b) - assert.Ok(t, err) + b := Version{} + err = json.Unmarshal(text, &b) + assert.Ok(t, err) - assert.Equal(t, a, b) + assert.Equal(t, a, b) + } + }) + t.Run("YAML", func(t *testing.T) { + t.Skip("yaml is an external dependency so is not tested") + // for i := 0; i < 128; i++ { + // a := randVersion() + // text, err := yaml.Marshal(a) + // assert.Ok(t, err) + + // var s string + // assert.Ok(t, yaml.Unmarshal(text, &s)) + // assert.True(t, Valid(s)) + + // b := Version{} + // err = yaml.Unmarshal(text, &b) + // assert.Ok(t, err) + + // assert.Equal(t, a, b) + // } }) }