From aa79e12a973495d00b01f2353330e5275d084731 Mon Sep 17 00:00:00 2001 From: Andriy Senyshyn Date: Mon, 12 Nov 2018 19:02:56 +0200 Subject: [PATCH] Support time.duration (#248) --- marshal.go | 10 ++++ marshal_test.go | 118 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+) diff --git a/marshal.go b/marshal.go index 3687fa2..b6e8ec1 100644 --- a/marshal.go +++ b/marshal.go @@ -352,6 +352,9 @@ func (e *Encoder) valueToToml(mtype reflect.Type, mval reflect.Value) (interface case reflect.Bool: return mval.Bool(), nil case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + if mtype.Kind() == reflect.Int64 && mtype == reflect.TypeOf(time.Duration(1)) { + return fmt.Sprint(mval), nil + } return mval.Int(), nil case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: return mval.Uint(), nil @@ -566,6 +569,13 @@ func (d *Decoder) valueFromToml(mtype reflect.Type, tval interface{}) (reflect.V return val.Convert(mtype), nil case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: val := reflect.ValueOf(tval) + if mtype.Kind() == reflect.Int64 && mtype == reflect.TypeOf(time.Duration(1)) && val.Kind() == reflect.String { + d, err := time.ParseDuration(val.String()) + if err != nil { + return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to %v. %s", tval, tval, mtype.String(), err) + } + return reflect.ValueOf(d), nil + } if !val.Type().ConvertibleTo(mtype) { return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to %v", tval, tval, mtype.String()) } diff --git a/marshal_test.go b/marshal_test.go index 09b42b4..8d00322 100644 --- a/marshal_test.go +++ b/marshal_test.go @@ -1002,3 +1002,121 @@ func TestMarshalMap(t *testing.T) { t.Fail() } } + +type testDuration struct { + Nanosec time.Duration `toml:"nanosec"` + Microsec1 time.Duration `toml:"microsec1"` + Microsec2 *time.Duration `toml:"microsec2"` + Millisec time.Duration `toml:"millisec"` + Sec time.Duration `toml:"sec"` + Min time.Duration `toml:"min"` + Hour time.Duration `toml:"hour"` + Mixed time.Duration `toml:"mixed"` + AString string `toml:"a_string"` +} + +var testDurationToml = []byte(` +nanosec = "1ns" +microsec1 = "1us" +microsec2 = "1µs" +millisec = "1ms" +sec = "1s" +min = "1m" +hour = "1h" +mixed = "1h1m1s1ms1µs1ns" +a_string = "15s" +`) + +func TestUnmarshalDuration(t *testing.T) { + buf := bytes.NewBuffer(testDurationToml) + + result := testDuration{} + err := NewDecoder(buf).Decode(&result) + if err != nil { + t.Fatal(err) + } + ms := time.Duration(1) * time.Microsecond + expected := testDuration{ + Nanosec: 1, + Microsec1: time.Microsecond, + Microsec2: &ms, + Millisec: time.Millisecond, + Sec: time.Second, + Min: time.Minute, + Hour: time.Hour, + Mixed: time.Hour + + time.Minute + + time.Second + + time.Millisecond + + time.Microsecond + + time.Nanosecond, + AString: "15s", + } + if !reflect.DeepEqual(result, expected) { + resStr, _ := json.MarshalIndent(result, "", " ") + expStr, _ := json.MarshalIndent(expected, "", " ") + t.Errorf("Bad unmarshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expStr, resStr) + + } +} + +var testDurationToml2 = []byte(`a_string = "15s" +hour = "1h0m0s" +microsec1 = "1µs" +microsec2 = "1µs" +millisec = "1ms" +min = "1m0s" +mixed = "1h1m1.001001001s" +nanosec = "1ns" +sec = "1s" +`) + +func TestMarshalDuration(t *testing.T) { + ms := time.Duration(1) * time.Microsecond + data := testDuration{ + Nanosec: 1, + Microsec1: time.Microsecond, + Microsec2: &ms, + Millisec: time.Millisecond, + Sec: time.Second, + Min: time.Minute, + Hour: time.Hour, + Mixed: time.Hour + + time.Minute + + time.Second + + time.Millisecond + + time.Microsecond + + time.Nanosecond, + AString: "15s", + } + + var buf bytes.Buffer + err := NewEncoder(&buf).Encode(data) + if err != nil { + t.Fatal(err) + } + expected := testDurationToml2 + result := buf.Bytes() + if !bytes.Equal(result, expected) { + t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result) + } +} + +type testBadDuration struct { + Val time.Duration `toml:"val"` +} + +var testBadDurationToml = []byte(`val = "1z"`) + +func TestUnmarshalBadDuration(t *testing.T) { + buf := bytes.NewBuffer(testBadDurationToml) + + result := testBadDuration{} + err := NewDecoder(buf).Decode(&result) + if err == nil { + t.Fatal() + } + if err.Error() != "(1, 1): Can't convert 1z(string) to time.Duration. time: unknown unit z in duration 1z" { + t.Fatalf("unexpected error: %s", err) + } +}