Skip to content

Commit

Permalink
feat: support duration and time string format
Browse files Browse the repository at this point in the history
  • Loading branch information
siyul-park committed Nov 16, 2024
1 parent c330540 commit ef173c2
Show file tree
Hide file tree
Showing 5 changed files with 183 additions and 2 deletions.
2 changes: 1 addition & 1 deletion cmd/pkg/cli/debug_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -637,7 +637,7 @@ func TestDebugModel_Update(t *testing.T) {
m.Update(tea.KeyMsg{Type: tea.KeyEnter})

data, _ := json.Marshal(types.InterfaceOf(payload))
assert.Contains(t, m.View(), data)
assert.Contains(t, m.View(), string(data))

d.RemoveBreakpoint(d.Breakpoint())
})
Expand Down
4 changes: 4 additions & 0 deletions pkg/types/encoding.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ func init() {
Encoder.Add(newBinaryEncoder())
Encoder.Add(newStringEncoder())
Encoder.Add(newErrorEncoder())
Encoder.Add(newTimeEncoder())
Encoder.Add(newDurationEncoder())
Encoder.Add(newExpandedEncoder())
Encoder.Add(newShortcutEncoder())

Expand All @@ -49,6 +51,8 @@ func init() {
Decoder.Add(newBinaryDecoder())
Decoder.Add(newStringDecoder())
Decoder.Add(newErrorDecoder())
Decoder.Add(newTimeDecoder())
Decoder.Add(newDurationDecoder())
Decoder.Add(newExpandedDecoder())
Decoder.Add(newShortcutDecoder())
}
Expand Down
20 changes: 19 additions & 1 deletion pkg/types/encoding_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"reflect"
"testing"
"time"

"github.com/samber/lo"
"github.com/siyul-park/uniflow/pkg/encoding"
Expand All @@ -30,7 +31,7 @@ func TestMarshal(t *testing.T) {
expect: True,
},
{
when: int(0),
when: 0,
expect: NewInt(0),
},
{
Expand All @@ -45,6 +46,14 @@ func TestMarshal(t *testing.T) {
when: "a",
expect: NewString("a"),
},
{
when: time.Date(2024, time.November, 16, 12, 0, 0, 0, time.UTC),
expect: NewInt64(1731758400000),
},
{
when: time.Millisecond * 1500,
expect: NewInt64(1500),
},
{
when: errors.New("error"),
expect: NewError(errors.New("error")),
Expand Down Expand Up @@ -97,6 +106,15 @@ func TestUnmarshal(t *testing.T) {
when: NewString("a"),
expect: "a",
},
{
when: NewInt64(1731758400000),
expect: time.Date(2024, time.November, 16, 12, 0, 0, 0, time.UTC),
},

{
when: NewInt64(1500),
expect: time.Millisecond * 1500,
},
{
when: NewError(errors.New("error")),
expect: errors.New("error"),
Expand Down
99 changes: 99 additions & 0 deletions pkg/types/time.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package types

import (
"github.com/pkg/errors"
"github.com/siyul-park/uniflow/pkg/encoding"
"reflect"
"time"
"unsafe"
)

func newTimeEncoder() encoding.EncodeCompiler[any, Value] {
typeTime := reflect.TypeOf((*time.Time)(nil)).Elem()

return encoding.EncodeCompilerFunc[any, Value](func(typ reflect.Type) (encoding.Encoder[any, Value], error) {
if typ != nil && typ == typeTime {
return encoding.EncodeFunc(func(source any) (Value, error) {
s := source.(time.Time)
return NewInt64(s.UnixMilli()), nil
}), nil
}
return nil, errors.WithStack(encoding.ErrUnsupportedType)
})
}

func newTimeDecoder() encoding.DecodeCompiler[Value] {
typeTime := reflect.TypeOf((*time.Time)(nil)).Elem()

return encoding.DecodeCompilerFunc[Value](func(typ reflect.Type) (encoding.Decoder[Value, unsafe.Pointer], error) {
if typ != nil && typ.Kind() == reflect.Pointer {
if typ.Elem() == typeTime {
return encoding.DecodeFunc(func(source Value, target unsafe.Pointer) error {
var v time.Time
var err error
if s, ok := source.(String); ok {
v, err = time.Parse(time.RFC3339, s.String())
} else if s, ok := source.(Integer); ok {
v = time.UnixMilli(s.Int()).UTC()
} else if s, ok := source.(Float); ok {
v = time.UnixMilli(int64(s.Float())).UTC()
} else {
err = errors.WithStack(encoding.ErrUnsupportedType)
}
if err != nil {
return err
}
t := reflect.NewAt(typ.Elem(), target)
t.Elem().Set(reflect.ValueOf(v))
return nil
}), nil
}
}
return nil, errors.WithStack(encoding.ErrUnsupportedType)
})
}

func newDurationEncoder() encoding.EncodeCompiler[any, Value] {
typeDuration := reflect.TypeOf((*time.Duration)(nil)).Elem()

return encoding.EncodeCompilerFunc[any, Value](func(typ reflect.Type) (encoding.Encoder[any, Value], error) {
if typ != nil && typ == typeDuration {
return encoding.EncodeFunc(func(source any) (Value, error) {
s := source.(time.Duration)
return NewInt64(s.Milliseconds()), nil
}), nil
}
return nil, errors.WithStack(encoding.ErrUnsupportedType)
})
}

func newDurationDecoder() encoding.DecodeCompiler[Value] {
typeDuration := reflect.TypeOf((*time.Duration)(nil)).Elem()

return encoding.DecodeCompilerFunc[Value](func(typ reflect.Type) (encoding.Decoder[Value, unsafe.Pointer], error) {
if typ != nil && typ.Kind() == reflect.Pointer {
if typ.Elem() == typeDuration {
return encoding.DecodeFunc(func(source Value, target unsafe.Pointer) error {
var v time.Duration
var err error
if s, ok := source.(String); ok {
v, err = time.ParseDuration(s.String())
} else if s, ok := source.(Integer); ok {
v = time.Millisecond * (time.Duration)(s.Int())
} else if s, ok := source.(Float); ok {
v = time.Millisecond * (time.Duration)(s.Float())
} else {
err = errors.WithStack(encoding.ErrUnsupportedType)
}
if err != nil {
return err
}
t := reflect.NewAt(typ.Elem(), target)
t.Elem().Set(reflect.ValueOf(v))
return nil
}), nil
}
}
return nil, errors.WithStack(encoding.ErrUnsupportedType)
})
}
60 changes: 60 additions & 0 deletions pkg/types/time_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package types

import (
"github.com/siyul-park/uniflow/pkg/encoding"
"github.com/stretchr/testify/assert"
"testing"
"time"
)

func TestTime_Encode(t *testing.T) {
enc := encoding.NewEncodeAssembler[any, Value]()
enc.Add(newTimeEncoder())

timestamp := time.Date(2024, time.November, 16, 12, 0, 0, 0, time.UTC)

encoded, err := enc.Encode(timestamp)
assert.NoError(t, err)

expected := NewInt64(timestamp.UnixMilli())
assert.Equal(t, expected, encoded)
}

func TestTime_Decode(t *testing.T) {
dec := encoding.NewDecodeAssembler[Value, any]()
dec.Add(newTimeDecoder())

timestamp := time.Date(2024, time.November, 16, 12, 0, 0, 0, time.UTC)
encoded := NewInt64(timestamp.UnixMilli())

var decoded time.Time
err := dec.Decode(encoded, &decoded)
assert.NoError(t, err)
assert.Equal(t, timestamp, decoded)
}

func TestDuration_Encode(t *testing.T) {
enc := encoding.NewEncodeAssembler[any, Value]()
enc.Add(newDurationEncoder())

duration := 1500 * time.Millisecond

encoded, err := enc.Encode(duration)
assert.NoError(t, err)

expected := NewInt64(1500)
assert.Equal(t, expected, encoded)
}

func TestDuration_Decode(t *testing.T) {
dec := encoding.NewDecodeAssembler[Value, any]()
dec.Add(newDurationDecoder())

duration := 1500 * time.Millisecond
encoded := NewInt64(1500)

var decoded time.Duration
err := dec.Decode(encoded, &decoded)
assert.NoError(t, err)
assert.Equal(t, duration, decoded)
}

0 comments on commit ef173c2

Please sign in to comment.