Skip to content

Commit

Permalink
Add initial well-known types support package.
Browse files Browse the repository at this point in the history
This is initially only Duration helper funcs;
more will follow.
  • Loading branch information
dsymonds committed Feb 14, 2016
1 parent b3392a2 commit 89238a3
Show file tree
Hide file tree
Showing 4 changed files with 257 additions and 7 deletions.
6 changes: 2 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,11 @@
all: install

install:
go install ./proto
go install ./jsonpb
go install ./proto ./jsonpb ./types
go install ./protoc-gen-go

test:
go test ./proto
go test ./jsonpb
go test ./proto ./jsonpb ./types
make -C protoc-gen-go/testdata test

clean:
Expand Down
35 changes: 32 additions & 3 deletions types/doc.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,35 @@
/*
Package types will contain code for interacting with well-known types.
// Go support for Protocol Buffers - Google's data interchange format
//
// Copyright 2016 The Go Authors. All rights reserved.
// https://github.com/golang/protobuf
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

For now, it is a placeholder; see its subdirectories for the protos themselves.
/*
Package types contains code for interacting with well-known types.
*/
package types
102 changes: 102 additions & 0 deletions types/duration.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// Go support for Protocol Buffers - Google's data interchange format
//
// Copyright 2016 The Go Authors. All rights reserved.
// https://github.com/golang/protobuf
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package types

// This file implements conversions between google.protobuf.Duration
// and time.Duration.

import (
"errors"
"fmt"
"time"

durpb "github.com/golang/protobuf/types/duration"
)

const (
// Range of a durpb.Duration in seconds, as specified in
// google/protobuf/duration.proto. This is about 10,000 years in seconds.
maxSeconds = int64(10000 * 365.25 * 24 * 60 * 60)
minSeconds = -maxSeconds
)

// ValidateDuration determines whether the durpb.Duration is valid according to the
// definition in google/protobuf/duration.proto. A valid durpb.Duration
// may still be too large to fit into a time.Duration (the range of durpb.Duration
// is about 10,000 years, and the range of time.Duration is about 290).
func ValidateDuration(d *durpb.Duration) error {
if d == nil {
return errors.New("duration: nil Duration")
}
if d.Seconds < minSeconds || d.Seconds > maxSeconds {
return fmt.Errorf("duration: %v: seconds out of range", d)
}
if d.Nanos <= -1e9 || d.Nanos >= 1e9 {
return fmt.Errorf("duration: %v: nanos out of range", d)
}
// Seconds and Nanos must have the same sign, unless d.Nanos is zero.
if (d.Seconds < 0 && d.Nanos > 0) || (d.Seconds > 0 && d.Nanos < 0) {
return fmt.Errorf("duration: %v: seconds and nanos have different signs", d)
}
return nil
}

// DurationFromProto attempts to convert a durpb.Duration to a time.Duration. DurationFromProto
// returns an error if the durpb.Duration is invalid or is too large to be
// represented in a time.Duration.
func DurationFromProto(p *durpb.Duration) (time.Duration, error) {
if err := ValidateDuration(p); err != nil {
return 0, err
}
d := time.Duration(p.Seconds) * time.Second
if int64(d/time.Second) != p.Seconds {
return 0, fmt.Errorf("duration: %v is out of range for time.Duration", p)
}
if p.Nanos != 0 {
d += time.Duration(p.Nanos)
if (d < 0) != (p.Nanos < 0) {
return 0, fmt.Errorf("duration: %v is out of range for time.Duration", p)
}
}
return d, nil
}

// DurationProto converts a time.Duration to a durpb.Duration.
func DurationProto(d time.Duration) *durpb.Duration {
nanos := d.Nanoseconds()
secs := nanos / 1e9
nanos -= secs * 1e9
return &durpb.Duration{
Seconds: secs,
Nanos: int32(nanos),
}
}
121 changes: 121 additions & 0 deletions types/duration_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// Go support for Protocol Buffers - Google's data interchange format
//
// Copyright 2016 The Go Authors. All rights reserved.
// https://github.com/golang/protobuf
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package types

import (
"math"
"testing"
"time"

"github.com/golang/protobuf/proto"
durpb "github.com/golang/protobuf/types/duration"
)

const (
minGoSeconds = math.MinInt64 / int64(1e9)
maxGoSeconds = math.MaxInt64 / int64(1e9)
)

var durationTests = []struct {
proto *durpb.Duration
isValid bool
inRange bool
dur time.Duration
}{
// The zero duration.
{&durpb.Duration{0, 0}, true, true, 0},
// Some ordinary non-zero durations.
{&durpb.Duration{100, 0}, true, true, 100 * time.Second},
{&durpb.Duration{-100, 0}, true, true, -100 * time.Second},
{&durpb.Duration{100, 987}, true, true, 100*time.Second + 987},
{&durpb.Duration{-100, -987}, true, true, -(100*time.Second + 987)},
// The largest duration representable in Go.
{&durpb.Duration{maxGoSeconds, int32(math.MaxInt64 - 1e9*maxGoSeconds)}, true, true, math.MaxInt64},
// The smallest duration representable in Go.
{&durpb.Duration{minGoSeconds, int32(math.MinInt64 - 1e9*minGoSeconds)}, true, true, math.MinInt64},
{nil, false, false, 0},
{&durpb.Duration{-100, 987}, false, false, 0},
{&durpb.Duration{100, -987}, false, false, 0},
{&durpb.Duration{math.MinInt64, 0}, false, false, 0},
{&durpb.Duration{math.MaxInt64, 0}, false, false, 0},
// The largest valid duration.
{&durpb.Duration{maxSeconds, 1e9 - 1}, true, false, 0},
// The smallest valid duration.
{&durpb.Duration{minSeconds, -(1e9 - 1)}, true, false, 0},
// The smallest invalid duration above the valid range.
{&durpb.Duration{maxSeconds + 1, 0}, false, false, 0},
// The largest invalid duration below the valid range.
{&durpb.Duration{minSeconds - 1, -(1e9 - 1)}, false, false, 0},
// One nanosecond past the largest duration representable in Go.
{&durpb.Duration{maxGoSeconds, int32(math.MaxInt64-1e9*maxGoSeconds) + 1}, true, false, 0},
// One nanosecond past the smallest duration representable in Go.
{&durpb.Duration{minGoSeconds, int32(math.MinInt64-1e9*minGoSeconds) - 1}, true, false, 0},
// One second past the largest duration representable in Go.
{&durpb.Duration{maxGoSeconds + 1, int32(math.MaxInt64 - 1e9*maxGoSeconds)}, true, false, 0},
// One second past the smallest duration representable in Go.
{&durpb.Duration{minGoSeconds - 1, int32(math.MinInt64 - 1e9*minGoSeconds)}, true, false, 0},
}

func TestValidateDuration(t *testing.T) {
for _, test := range durationTests {
err := ValidateDuration(test.proto)
gotValid := (err == nil)
if gotValid != test.isValid {
t.Errorf("ValidateDuration(%v) = %t, want %t", test.proto, gotValid, test.isValid)
}
}
}

func TestDurationFromProto(t *testing.T) {
for _, test := range durationTests {
got, err := DurationFromProto(test.proto)
gotOK := (err == nil)
wantOK := test.isValid && test.inRange
if gotOK != wantOK {
t.Errorf("DurationFromProto(%v) ok = %t, want %t", test.proto, gotOK, wantOK)
}
if err == nil && got != test.dur {
t.Errorf("DurationFromProto(%v) = %v, want %v", test.proto, got, test.dur)
}
}
}

func TestDurationProto(t *testing.T) {
for _, test := range durationTests {
if test.isValid && test.inRange {
got := DurationProto(test.dur)
if !proto.Equal(got, test.proto) {
t.Errorf("DurationProto(%v) = %v, want %v", test.dur, got, test.proto)
}
}
}
}

0 comments on commit 89238a3

Please sign in to comment.