Skip to content

Commit

Permalink
Add dgo.Duration type
Browse files Browse the repository at this point in the history
Adds `dgo.Duration` as a `dgo.Value` representation of `time.Duration`.
  • Loading branch information
thallgren committed Nov 23, 2020
1 parent 3cd018a commit c84d466
Show file tree
Hide file tree
Showing 39 changed files with 495 additions and 57 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/dgo_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ jobs:
runs-on: ubuntu-latest
steps:

- name: Set up Go 1.14
- name: Set up Go 1.15
uses: actions/setup-go@v1
with:
go-version: 1.14
go-version: 1.15
id: go

- name: Check out code into the Go module directory
Expand Down Expand Up @@ -42,10 +42,10 @@ jobs:
runs-on: windows-latest
steps:

- name: Set up Go 1.14
- name: Set up Go 1.15
uses: actions/setup-go@v1
with:
go-version: 1.14
go-version: 1.15
id: go

- name: Check out code into the Go module directory
Expand Down
13 changes: 13 additions & 0 deletions dgo/primitive.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,19 @@ type (
GoRegexp() *regexp.Regexp
}

// Duration value is a time.Duration that implements the Value interface
Duration interface {
Value
ReflectedValue

// GoDuration returns the Go native representation of this value
GoDuration() time.Duration

// SecondsWithFraction returns the duration as seconds with a fraction value
// with nanosecond precision.
SecondsWithFraction() float64
}

// Time value is a *time.Time that implements the Value interface
Time interface {
Number
Expand Down
12 changes: 10 additions & 2 deletions dgo/typeidentifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ const (
// TiDgoString is the type identifier for for the DgoString type
TiDgoString

// TiDuration is the type identifier for for the Duration type
TiDuration

// TiError is the type identifier for for the Error type
TiError

Expand Down Expand Up @@ -131,6 +134,9 @@ const (
// TiBooleanExact is the type identifier for the exact Boolean type
TiBooleanExact

// TiDurationExact is the type identifier for the exact Duration type
TiDurationExact

// TiErrorExact is the type identifier for for the exact Error type
TiErrorExact

Expand Down Expand Up @@ -208,14 +214,16 @@ var tiLabels = map[TypeIdentifier]string{
TiNil: `nil`,
TiNot: `not`,
TiOneOf: `one of`,
TiRegexp: `regexp`,
TiRegexpExact: `regexp`,
TiSensitive: `sensitive`,
TiString: `string`,
TiStringExact: `string`,
TiStringPattern: `pattern`,
TiStringSized: `string`,
TiStruct: `struct`,
TiRegexp: `regexp`,
TiRegexpExact: `regexp`,
TiDuration: `duration`,
TiDurationExact: `duration`,
TiTime: `time`,
TiTimeExact: `time`,
TiTuple: `tuple`,
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module github.com/lyraproj/dgo

go 1.14
go 1.15

require github.com/tada/catch v0.0.0-20200501144157-878cbcd22dd2
10 changes: 4 additions & 6 deletions internal/array.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,7 @@ func newArrayType(elementType dgo.Type, min, max int64) dgo.ArrayType {
max = 0
}
if max < min {
t := max
max = min
min = t
max, min = min, max
}
if elementType == nil {
elementType = DefaultAnyType
Expand All @@ -118,7 +116,7 @@ func newArrayType(elementType dgo.Type, min, max int64) dgo.ArrayType {
// Unbounded
return DefaultArrayType
}
if min == 1 && min == max && dgo.IsExact(elementType) {
if min == 1 && max == 1 && dgo.IsExact(elementType) {
return makeFrozenArray([]dgo.Value{elementType}).(dgo.ArrayType)
}
return &sizedArrayType{sizeRange: sizeRange{min: uint32(min), max: uint32(max)}, elementType: elementType}
Expand Down Expand Up @@ -286,8 +284,8 @@ func Array(value interface{}) dgo.Array {
}
}

// arrayFromIterator creates an array from a size and an iterator goFunc. The
// iterator goFunc is expected to call its actor exactly size number of times.
// arrayFromIterator creates an array from a size and an iterator function. The
// iterator function is expected to call its actor exactly size number of times.
func arrayFromIterator(size int, each func(dgo.Consumer)) dgo.Array {
arr := make([]dgo.Value, size)
i := 0
Expand Down
4 changes: 4 additions & 0 deletions internal/array_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -758,6 +758,10 @@ func TestArray_ReflectTo(t *testing.T) {
// test that os and as are different slices
as[0] = vf.String(`a`)
assert.NotEqual(t, os, as)

// Test reflect to nil pointer
var ax *string
assert.Panic(t, func() { a.ReflectTo(reflect.ValueOf(ax)) }, "reflectTo: nil pointer")
}

func TestArray_ReflectTo_nestedStructPointer(t *testing.T) {
Expand Down
4 changes: 1 addition & 3 deletions internal/binary.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,7 @@ func SizedBinaryType(min, max int) dgo.BinaryType {
min = 0
}
if max < min {
tmp := max
max = min
min = tmp
max, min = min, max
}
if min == 0 && max == dgo.UnboundedSize {
return DefaultBinaryType
Expand Down
195 changes: 195 additions & 0 deletions internal/duration.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
package internal

import (
"math/big"
"reflect"
"time"

"github.com/lyraproj/dgo/dgo"
)

type (
durationType int

durationVal time.Duration
)

// DefaultDurationType is the unconstrained Duration type
const DefaultDurationType = durationType(0)

var reflectDurationType = reflect.TypeOf(time.Duration(0))

func (t durationType) Assignable(ot dgo.Type) bool {
switch ot.(type) {
case durationVal, durationType:
return true
}
return CheckAssignableTo(nil, ot, t)
}

func (t durationType) Equals(v interface{}) bool {
return t == Value(v)
}

func (t durationType) HashCode() dgo.Hash {
return dgo.Hash(dgo.TiDuration)
}

func (t durationType) Instance(v interface{}) bool {
switch v.(type) {
case durationVal, time.Duration:
return true
}
return false
}

func (t durationType) New(arg dgo.Value) dgo.Value {
return newDuration(t, arg)
}

func (t durationType) ReflectType() reflect.Type {
return reflectDurationType
}

func (t durationType) String() string {
return TypeString(t)
}

func (t durationType) Type() dgo.Type {
return MetaType(t)
}

func (t durationType) TypeIdentifier() dgo.TypeIdentifier {
return dgo.TiDuration
}

func newDuration(t dgo.Type, arg dgo.Value) dgo.Duration {
if args, ok := arg.(dgo.Arguments); ok {
args.AssertSize(`duration`, 1, 1)
arg = args.Get(0)
}
var tv dgo.Duration
switch arg := arg.(type) {
case dgo.Duration:
tv = arg
case dgo.Integer:
tv = durationVal(arg.GoInt())
case dgo.Float:
tv = durationVal(arg.GoFloat() * 1_000_000_000.0)
case dgo.String:
tv = DurationFromString(arg.GoString())
default:
panic(illegalArgument(`duration`, `duration|int|float|string`, []interface{}{arg}, 0))
}
if !t.Instance(tv) {
panic(IllegalAssignment(t, tv))
}
return tv
}

// Duration returns the given duration as a dgo.Duration
func Duration(ts time.Duration) dgo.Duration {
return durationVal(ts)
}

// DurationFromString returns the given duration string as a dgo.Duration. The string must be parsable
// by the time.ParseDuration() function. The function will panic if the given string cannot be parsed.
func DurationFromString(s string) dgo.Duration {
d, err := time.ParseDuration(s)
if err != nil {
panic(err)
}
return durationVal(d)
}

func (v durationVal) Equals(other interface{}) bool {
switch ov := other.(type) {
case durationVal:
return v == ov
case time.Duration:
return time.Duration(v) == ov
}
return false
}

func (v durationVal) Float() dgo.Float {
return floatVal(v.SecondsWithFraction())
}

func (v durationVal) GoDuration() time.Duration {
return time.Duration(v)
}

func (v durationVal) HashCode() dgo.Hash {
return dgo.Hash(v)
}

func (v durationVal) Integer() dgo.Integer {
return intVal(v)
}

func (v durationVal) ReflectTo(value reflect.Value) {
rv := reflect.ValueOf(time.Duration(v))
value.Set(rv)
}

func (v durationVal) SecondsWithFraction() float64 {
return float64(v) / 1_000_000_000.0
}

func (v durationVal) String() string {
return TypeString(v)
}

func (v durationVal) ToBigFloat() *big.Float {
return big.NewFloat(v.SecondsWithFraction())
}

func (v durationVal) ToBigInt() *big.Int {
return big.NewInt(int64(v))
}

func (v durationVal) ToFloat() (float64, bool) {
return v.SecondsWithFraction(), true
}

func (v durationVal) ToInt() (int64, bool) {
return int64(v), true
}

func (v durationVal) ToUint() (uint64, bool) {
if v >= 0 {
return uint64(v), true
}
return 0, false
}

func (v durationVal) Type() dgo.Type {
return v
}

// Duration exact type implementation

func (v durationVal) Assignable(other dgo.Type) bool {
return v.Equals(other) || CheckAssignableTo(nil, other, v)
}

func (v durationVal) Instance(value interface{}) bool {
return v.Equals(value)
}

func (v durationVal) Generic() dgo.Type {
return DefaultDurationType
}

func (v durationVal) New(arg dgo.Value) dgo.Value {
return newDuration(v, arg)
}

func (v durationVal) ReflectType() reflect.Type {
return reflectDurationType
}

func (v durationVal) TypeIdentifier() dgo.TypeIdentifier {
return dgo.TiDurationExact
}

0 comments on commit c84d466

Please sign in to comment.