Browse Source

README, examples, benchmarks

Benedikt Lang 12 years ago
parent
commit
58fd7ff9c3
4 changed files with 333 additions and 2 deletions
  1. +126
    -0
      README.md
  2. +83
    -0
      examples/main.go
  3. +16
    -0
      semver.go
  4. +108
    -2
      semver_test.go

+ 126
- 0
README.md View File

@ -0,0 +1,126 @@
semver for golang
======
semver is a Semantic Versioning library written in golang. It fully covers spec version `2.0.0`.
Usage
-----
$ go get github.com/blang/semver
import github.com/blang/semver
v1, err := semver.New("1.0.0-beta")
v2, err := semver.New("2.0.0-beta")
v1.Compare(v2)
Why should I use this lib?
-----
- Fully spec compatible
- No reflection
- No regex
- Fully tested (Coverage >99%)
- Readable parsing/validation errors
- Fast (See [Benchmarks](#benchmarks))
- Only Stdlib
Features
-----
- Parsing and validation at all levels
- Comparator-like comparisons
- Compare Helper Methods
- InPlace manipulation
Example
-----
Have a look at full examples in [examples/main.go](examples/main.go)
import github.com/blang/semver
v, err := semver.New("0.0.1-alpha.preview+123.github")
fmt.Printf("Major: %d\n", v.Major)
fmt.Printf("Minor: %d\n", v.Minor)
fmt.Printf("Patch: %d\n", v.Patch)
fmt.Printf("Pre: %s\n", v.Pre)
fmt.Printf("Build: %s\n", v.Build)
// Prerelease versions array
if len(v.Pre) > 0 {
fmt.Println("Prerelease versions:")
for i, pre := range v.Pre {
fmt.Printf("%d: %q\n", i, pre)
}
}
// Build meta data array
if len(v.Build) > 0 {
fmt.Println("Build meta data:")
for i, build := range v.Build {
fmt.Printf("%d: %q\n", i, build)
}
}
v001, err := semver.New("0.0.1")
// Compare using helpers: v.GT(v2), v.LT, v.GTE, v.LTE
v001.GT(v) == true
v.LT(v001) == true
v.GTE(v) == true
v.LTE(v) == true
// Or use v.Compare(v2) for comparisons (-1, 0, 1):
v001.Compare(v) == 1
v.Compare(v001) == -1
v.Compare(v) == 0
// Manipulate Version in place:
v.Pre[0], err = semver.NewPRVersion("beta")
if err != nil {
fmt.Printf("Error parsing pre release version: %q", err)
}
fmt.Println("\nValidate versions:")
v.Build[0] = "?"
err = v.Validate()
if err != nil {
fmt.Printf("Validation failed: %s\n", err)
}
Benchmarks
-----
BenchmarkParseSimple 5000000 442 ns/op
BenchmarkParseComplex 1000000 2441 ns/op
BenchmarkParseAverage 1000000 1497 ns/op
BenchmarkValidateSimple 500000000 4.83 ns/op
BenchmarkValidateComplex 1000000 1236 ns/op
BenchmarkValidateAverage 5000000 580 ns/op
BenchmarkCompareSimple 500000000 5.43 ns/op
BenchmarkCompareComplex 100000000 26.3 ns/op
BenchmarkCompareAverage 100000000 29.6 ns/op
See benchmark cases at [semver_test.go](semver_test.go)
Motivation
-----
I simply couldn't find any lib supporting the full spec. Others were just wrong or used reflection and regex which i don't like.
Contribution
-----
Feel free to make a pull request. For bigger changes create a issue first to discuss about it.
License
-----
See [LICENSE](LICENSE) file.

+ 83
- 0
examples/main.go View File

@ -0,0 +1,83 @@
package main
import (
"fmt"
"github.com/blang/semver"
)
func main() {
v, err := semver.Parse("0.0.1-alpha.preview.222+123.github")
if err != nil {
fmt.Printf("Error while parsing (not valid): %q", err)
}
fmt.Printf("Version to string: %q\n", v)
fmt.Printf("Major: %d\n", v.Major)
fmt.Printf("Minor: %d\n", v.Minor)
fmt.Printf("Patch: %d\n", v.Patch)
// Prerelease versions
if len(v.Pre) > 0 {
fmt.Println("Prerelease versions:")
for i, pre := range v.Pre {
fmt.Printf("%d: %q\n", i, pre)
}
}
// Build meta data
if len(v.Build) > 0 {
fmt.Println("Build meta data:")
for i, build := range v.Build {
fmt.Printf("%d: %q\n", i, build)
}
}
// New == Parse
v001, err := semver.New("0.0.1")
fmt.Println("\nUse Version.Compare for comparisons (-1, 0, 1):")
fmt.Printf("%q is greater than %q: Compare == %d\n", v001, v, v001.Compare(v))
fmt.Printf("%q is less than %q: Compare == %d\n", v, v001, v.Compare(v001))
fmt.Printf("%q is equal to %q: Compare == %d\n", v, v, v.Compare(v))
fmt.Println("\nUse comparison helpers returning booleans:")
fmt.Printf("%q is greater than %q: %t\n", v001, v, v001.GT(v))
fmt.Printf("%q is greater than equal %q: %t\n", v001, v, v001.GTE(v))
fmt.Printf("%q is greater than equal %q: %t\n", v, v, v.GTE(v))
fmt.Printf("%q is less than %q: %t\n", v, v001, v.LT(v001))
fmt.Printf("%q is less than equal %q: %t\n", v, v001, v.LTE(v001))
fmt.Printf("%q is less than equal %q: %t\n", v, v, v.LTE(v))
fmt.Println("\nManipulate Version in place:")
v.Pre[0], err = semver.NewPRVersion("beta")
if err != nil {
fmt.Printf("Error parsing pre release version: %q", err)
}
fmt.Printf("Version to string: %q\n", v)
fmt.Println("\nCompare Prerelease versions:")
pre1, _ := semver.NewPRVersion("123")
pre2, _ := semver.NewPRVersion("alpha")
pre3, _ := semver.NewPRVersion("124")
fmt.Printf("%q is less than %q: Compare == %d\n", pre1, pre2, pre1.Compare(pre2))
fmt.Printf("%q is greater than %q: Compare == %d\n", pre3, pre1, pre3.Compare(pre1))
fmt.Printf("%q is equal to %q: Compare == %d\n", pre1, pre1, pre1.Compare(pre1))
fmt.Println("\nValidate versions:")
v.Build[0] = "?"
err = v.Validate()
if err != nil {
fmt.Printf("Validation failed: %s\n", err)
}
fmt.Println("Create valid build meta data:")
b1, _ := semver.NewBuildVersion("build123")
v.Build[0] = b1
fmt.Printf("Version with new build version %q\n", v)
_, err = semver.NewBuildVersion("build?123")
if err != nil {
fmt.Printf("Create build version failed: %s\n", err)
}
}

+ 16
- 0
semver.go View File

@ -168,6 +168,11 @@ func (v *Version) Validate() error {
return nil
}
// Alias for Parse
func New(s string) (*Version, error) {
return Parse(s)
}
// Parses a string to version
func Parse(s string) (*Version, error) {
if len(s) == 0 {
@ -344,3 +349,14 @@ func containsOnly(s string, set string) bool {
return !strings.ContainsRune(set, r)
}) == -1
}
// Creates a new valid build version
func NewBuildVersion(s string) (string, error) {
if len(s) == 0 {
return "", errors.New("Buildversion is empty")
}
if !containsOnly(s, ALPHAS+NUMBERS) {
return "", fmt.Errorf("Invalid character(s) found in build meta data %q", s)
}
return s, nil
}

+ 108
- 2
semver_test.go View File

@ -1,7 +1,5 @@
package semver
//TODO: Test incorrect version formats
import (
"testing"
)
@ -200,3 +198,111 @@ func TestPreReleaseVersions(t *testing.T) {
t.Errorf("Not expected error %q", err)
}
}
func TestBuildMetaDataVersions(t *testing.T) {
_, err := NewBuildVersion("123")
if err != nil {
t.Errorf("Unexpected error %q", err)
}
_, err = NewBuildVersion("build")
if err != nil {
t.Errorf("Unexpected error %q", err)
}
_, err = NewBuildVersion("test?")
if err == nil {
t.Error("Expected error, got none")
}
_, err = NewBuildVersion("")
if err == nil {
t.Error("Expected error, got none")
}
}
func TestNewHelper(t *testing.T) {
v, err := New("1.2.3")
if err != nil {
t.Fatalf("Unexpected error %q", err)
}
if v.Compare(&Version{1, 2, 3, nil, nil}) != 0 {
t.Fatal("Unexpected comparison problem")
}
}
func BenchmarkParseSimple(b *testing.B) {
const VERSION = "0.0.1"
b.ResetTimer()
for n := 0; n < b.N; n++ {
New(VERSION)
}
}
func BenchmarkParseComplex(b *testing.B) {
const VERSION = "0.0.1-alpha.preview+123.456"
b.ResetTimer()
for n := 0; n < b.N; n++ {
New(VERSION)
}
}
func BenchmarkParseAverage(b *testing.B) {
l := len(formatTests)
b.ResetTimer()
for n := 0; n < b.N; n++ {
New(formatTests[n%l].result)
}
}
func BenchmarkValidateSimple(b *testing.B) {
const VERSION = "0.0.1"
v, _ := New(VERSION)
b.ResetTimer()
for n := 0; n < b.N; n++ {
v.Validate()
}
}
func BenchmarkValidateComplex(b *testing.B) {
const VERSION = "0.0.1-alpha.preview+123.456"
v, _ := New(VERSION)
b.ResetTimer()
for n := 0; n < b.N; n++ {
v.Validate()
}
}
func BenchmarkValidateAverage(b *testing.B) {
l := len(formatTests)
b.ResetTimer()
for n := 0; n < b.N; n++ {
formatTests[n%l].v.Validate()
}
}
func BenchmarkCompareSimple(b *testing.B) {
const VERSION = "0.0.1"
v, _ := New(VERSION)
b.ResetTimer()
for n := 0; n < b.N; n++ {
v.Compare(v)
}
}
func BenchmarkCompareComplex(b *testing.B) {
const VERSION = "0.0.1-alpha.preview+123.456"
v, _ := New(VERSION)
b.ResetTimer()
for n := 0; n < b.N; n++ {
v.Compare(v)
}
}
func BenchmarkCompareAverage(b *testing.B) {
l := len(compareTests)
b.ResetTimer()
for n := 0; n < b.N; n++ {
compareTests[n%l].v1.Compare(&(compareTests[n%l].v2))
}
}

Loading…
Cancel
Save