Add LocalTime to interface{} decode support (#567)

Co-authored-by: Thomas Pelletier <thomas@pelletier.codes>
This commit is contained in:
kkHAIKE
2021-07-21 23:50:12 +08:00
committed by GitHub
parent a93b34d984
commit 8be357dfa1
6 changed files with 146 additions and 6 deletions
+3 -3
View File
@@ -25,9 +25,9 @@ const (
Float
Integer
LocalDate
LocalTime
LocalDateTime
DateTime
Time
)
func (k Kind) String() string {
@@ -58,12 +58,12 @@ func (k Kind) String() string {
return "Integer"
case LocalDate:
return "LocalDate"
case LocalTime:
return "LocalTime"
case LocalDateTime:
return "LocalDateTime"
case DateTime:
return "DateTime"
case Time:
return "Time"
}
panic(fmt.Errorf("Kind.String() not implemented for '%d'", k))
}
+7
View File
@@ -806,3 +806,10 @@ func ExampleMarshal() {
// Name = 'go-toml'
// Tags = ['go', 'toml']
}
func TestIssue567(t *testing.T) {
var m map[string]interface{}
err := toml.Unmarshal([]byte("A = 12:08:05"), &m)
require.NoError(t, err)
require.IsType(t, m["A"], toml.LocalTime{})
}
+9 -3
View File
@@ -862,6 +862,7 @@ func digitsToInt(b []byte) int {
func (p *parser) scanDateTime(b []byte) (ast.Reference, []byte, error) {
// scans for contiguous characters in [0-9T:Z.+-], and up to one space if
// followed by a digit.
hasDate := false
hasTime := false
hasTz := false
seenSpace := false
@@ -874,6 +875,7 @@ byteLoop:
switch {
case isDigit(c):
case c == '-':
hasDate = true
const minOffsetOfTz = 8
if i >= minOffsetOfTz {
hasTz = true
@@ -898,10 +900,14 @@ byteLoop:
var kind ast.Kind
if hasTime {
if hasTz {
kind = ast.DateTime
if hasDate {
if hasTz {
kind = ast.DateTime
} else {
kind = ast.LocalDateTime
}
} else {
kind = ast.LocalDateTime
kind = ast.LocalTime
}
} else {
kind = ast.LocalDate
+59
View File
@@ -370,3 +370,62 @@ func BenchmarkParseBasicStringWithUnicode(b *testing.B) {
}
})
}
func TestParser_AST_DateTimes(t *testing.T) {
examples := []struct {
desc string
input string
kind ast.Kind
err bool
}{
{
desc: "offset-date-time with delim 'T' and UTC offset",
input: `2021-07-21T12:08:05Z`,
kind: ast.DateTime,
},
{
desc: "offset-date-time with space delim and +8hours offset",
input: `2021-07-21 12:08:05+08:00`,
kind: ast.DateTime,
},
{
desc: "local-date-time with nano second",
input: `2021-07-21T12:08:05.666666666`,
kind: ast.LocalDateTime,
},
{
desc: "local-date-time",
input: `2021-07-21T12:08:05`,
kind: ast.LocalDateTime,
},
{
desc: "local-date",
input: `2021-07-21`,
kind: ast.LocalDate,
},
}
for _, e := range examples {
e := e
t.Run(e.desc, func(t *testing.T) {
p := parser{}
p.Reset([]byte(`A = ` + e.input))
p.NextExpression()
err := p.Error()
if e.err {
require.Error(t, err)
} else {
require.NoError(t, err)
expected := astNode{
Kind: ast.KeyValue,
Children: []astNode{
{Kind: e.kind, Data: []byte(e.input)},
{Kind: ast.Key, Data: []byte(`A`)},
},
}
compareNode(t, expected, p.Expression())
}
})
}
}
+16
View File
@@ -585,6 +585,8 @@ func (d *decoder) handleValue(value *ast.Node, v reflect.Value) error {
return d.unmarshalDateTime(value, v)
case ast.LocalDate:
return d.unmarshalLocalDate(value, v)
case ast.LocalTime:
return d.unmarshalLocalTime(value, v)
case ast.LocalDateTime:
return d.unmarshalLocalDateTime(value, v)
case ast.InlineTable:
@@ -730,6 +732,20 @@ func (d *decoder) unmarshalLocalDate(value *ast.Node, v reflect.Value) error {
return nil
}
func (d *decoder) unmarshalLocalTime(value *ast.Node, v reflect.Value) error {
lt, rest, err := parseLocalTime(value.Data)
if err != nil {
return err
}
if len(rest) > 0 {
return newDecodeError(rest, "extra characters at the end of a local time")
}
v.Set(reflect.ValueOf(lt))
return nil
}
func (d *decoder) unmarshalLocalDateTime(value *ast.Node, v reflect.Value) error {
ldt, rest, err := parseLocalDateTime(value.Data)
if err != nil {
+52
View File
@@ -339,6 +339,58 @@ func TestUnmarshal(t *testing.T) {
}
},
},
{
desc: "local-time with nano second",
input: `a = 12:08:05.666666666`,
gen: func() test {
var v map[string]interface{}
return test{
target: &v,
expected: &map[string]interface{}{
"a": toml.LocalTime{Hour: 12, Minute: 8, Second: 5, Nanosecond: 666666666},
},
}
},
},
{
desc: "local-time",
input: `a = 12:08:05`,
gen: func() test {
var v map[string]interface{}
return test{
target: &v,
expected: &map[string]interface{}{
"a": toml.LocalTime{Hour: 12, Minute: 8, Second: 5},
},
}
},
},
{
desc: "local-time missing digit",
input: `a = 12:08:0`,
gen: func() test {
var v map[string]interface{}
return test{
target: &v,
err: true,
}
},
},
{
desc: "local-time extra digit",
input: `a = 12:08:000`,
gen: func() test {
var v map[string]interface{}
return test{
target: &v,
err: true,
}
},
},
{
desc: "issue 475 - space between dots in key",
input: `fruit. color = "yellow"