aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ccl.go27
-rw-r--r--ccl_test.go19
2 files changed, 37 insertions, 9 deletions
diff --git a/ccl.go b/ccl.go
index 521fbba..fe67a72 100644
--- a/ccl.go
+++ b/ccl.go
@@ -592,6 +592,16 @@ func (p *parser) parse() (map[string]any, error) {
}
}
+func setPtr(val reflect.Value) reflect.Value {
+ if val.Kind() != reflect.Pointer {
+ return val
+ }
+ if val.IsNil() {
+ val.Set(reflect.New(val.Type().Elem()))
+ }
+ return val.Elem()
+}
+
func intLimits(kind reflect.Kind) (min, max uint64, ok bool) {
switch kind {
case reflect.Int:
@@ -622,6 +632,7 @@ func intLimits(kind reflect.Kind) (min, max uint64, ok bool) {
func unpackVal(fieldVal reflect.Value, fieldMap map[structField]int, val any, field string) error {
switch val := val.(type) {
case bool:
+ fieldVal := setPtr(fieldVal)
switch fieldVal.Kind() {
case reflect.Bool:
fieldVal.SetBool(val)
@@ -641,6 +652,7 @@ func unpackVal(fieldVal reflect.Value, fieldMap map[structField]int, val any, fi
return fmt.Errorf("field %q should have type bool", field)
}
case *integer:
+ fieldVal := setPtr(fieldVal)
switch fieldVal.Kind() {
case reflect.Float32, reflect.Float64:
fieldVal.SetFloat(float64(val.sgn) * float64(val.n))
@@ -659,6 +671,7 @@ func unpackVal(fieldVal reflect.Value, fieldMap map[structField]int, val any, fi
fieldVal.SetInt(int64(val.sgn) * int64(val.n))
}
case float64:
+ fieldVal := setPtr(fieldVal)
switch fieldVal.Kind() {
case reflect.Float32, reflect.Float64:
fieldVal.SetFloat(float64(val))
@@ -675,10 +688,11 @@ func unpackVal(fieldVal reflect.Value, fieldMap map[structField]int, val any, fi
if unmarshaler, ok := fieldVal.Addr().Interface().(encoding.TextUnmarshaler); ok {
return unmarshaler.UnmarshalText([]byte(val))
}
+ fieldVal := setPtr(fieldVal)
switch {
case fieldVal.Kind() == reflect.String:
fieldVal.SetString(val)
- case fieldVal.Kind() == reflect.Slice && fieldVal.Type().Elem() == reflect.TypeFor[byte]():
+ case fieldVal.Type() == reflect.TypeFor[[]byte]():
b, err := base64.StdEncoding.DecodeString(val)
if err != nil {
return fmt.Errorf("field %q: bad base64", field)
@@ -688,15 +702,10 @@ func unpackVal(fieldVal reflect.Value, fieldMap map[structField]int, val any, fi
return fmt.Errorf("field %q should have type string (got %s)", field, fieldVal.Type())
}
case map[string]any:
- if !(fieldVal.Kind() == reflect.Struct || fieldVal.Kind() == reflect.Pointer && fieldVal.Type().Elem().Kind() == reflect.Struct) {
+ fieldVal := setPtr(fieldVal)
+ if fieldVal.Kind() != reflect.Struct {
return fmt.Errorf("field %q should have type struct (got %s)", field, fieldVal.Type())
}
- if fieldVal.Kind() == reflect.Pointer {
- if fieldVal.IsNil() {
- fieldVal.Set(reflect.New(fieldVal.Type().Elem()))
- }
- fieldVal = fieldVal.Elem()
- }
if err := unpackStruct(fieldVal, fieldMap, val); err != nil {
return err
}
@@ -715,7 +724,7 @@ func unpackStruct(out reflect.Value, fieldMap map[structField]int, msg map[strin
return fmt.Errorf("no field named %q", field)
}
fieldVal := out.Field(fieldIdx)
- if fieldVal.Kind() == reflect.Slice && fieldVal.Type().Elem() != reflect.TypeFor[byte]() {
+ if fieldVal.Kind() == reflect.Slice && fieldVal.Type() != reflect.TypeFor[[]byte]() {
var vals []any
if l, ok := val.([]any); ok {
vals = l
diff --git a/ccl_test.go b/ccl_test.go
index 938af09..ab4833f 100644
--- a/ccl_test.go
+++ b/ccl_test.go
@@ -11,6 +11,7 @@ import (
func TestUnmarshal(t *testing.T) {
t.Parallel()
+ type byteSliceWrapper []byte
type nestedMessage struct {
Field int64 `ccl:"field"`
}
@@ -34,8 +35,11 @@ func TestUnmarshal(t *testing.T) {
Repeated []int64 `ccl:"repeated"`
RepeatedMessage []*nestedMessage `ccl:"repeated_message"`
Bytes []byte `ccl:"bytes"`
+ BytesWrapper byteSliceWrapper `ccl:"bytes_wrapper"`
Time time.Time `ccl:"time"`
TimePointer *time.Time `ccl:"time_pointer"`
+ IntPointer *int `ccl:"int_pointer"`
+ RepeatedPointer []*int `ccl:"repeated_pointer"`
Ignore map[int]int `ccl:"-,"` // unlike JSON this also means ignore
unexported int64
@@ -330,6 +334,10 @@ from string'`,
msg: `bytes:"dGVzdA=="`,
want: message{Bytes: []byte("test")},
}, {
+ desc: "NotBase64",
+ msg: `bytes_wrapper: [1, 2, 3]`,
+ want: message{BytesWrapper: byteSliceWrapper{1, 2, 3}},
+ }, {
desc: "TextUnmarshaler",
msg: `time:"2025-10-28T07:41:47Z"`,
want: message{Time: time.Date(2025, time.October, 28, 7, 41, 47, 0, time.UTC)},
@@ -337,6 +345,14 @@ from string'`,
desc: "TextUnmarshalerPointer",
msg: `time_pointer:"2025-10-28T07:41:47Z"`,
want: message{TimePointer: &[]time.Time{time.Date(2025, time.October, 28, 7, 41, 47, 0, time.UTC)}[0]},
+ }, {
+ desc: "IntPointer",
+ msg: `int_pointer: 5`,
+ want: message{IntPointer: &[]int{5}[0]},
+ }, {
+ desc: "RepeatedPointer",
+ msg: `repeated_pointer: [1, 2, 3]`,
+ want: message{RepeatedPointer: []*int{&[]int{1}[0], &[]int{2}[0], &[]int{3}[0]}},
}} {
t.Run(tc.desc, func(t *testing.T) {
t.Parallel()
@@ -455,6 +471,9 @@ func TestUnmarshal_Invalid(t *testing.T) {
desc: "Base64",
msg: `bytes:"dGVzdAo"`,
}, {
+ desc: "NotBase64",
+ msg: `bytes:[1,2,3]`,
+ }, {
desc: "BadField",
msg: `asdfasdfasdf:"asdf"`,
}, {