From b25fc05e59e6c04614617b59ce082091219a301a Mon Sep 17 00:00:00 2001 From: Roger Peppe Date: Mon, 12 Dec 2022 09:33:01 +0000 Subject: [PATCH] cue: support decoding into cue.Value fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #2029. Signed-off-by: Roger Peppe Change-Id: Ibe178f5428b2e97b04a1af9d9d9b631fea0a604c Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/547369 TryBot-Result: CUEcueckoo Unity-Result: CUE porcuepine Reviewed-by: Daniel Martí --- cue/decode.go | 10 ++++++++++ cue/decode_test.go | 21 +++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/cue/decode.go b/cue/decode.go index f7a26afbbcd..d81c92a7ae7 100644 --- a/cue/decode.go +++ b/cue/decode.go @@ -34,6 +34,10 @@ import ( // An error is returned if x is nil or not a pointer. // // If x is a struct, Decode will validate the constraints specified in the field tags. +// +// If x contains a [Value], that part of x will be set to the value +// at the corresponding part of v. This allows decoding values +// that aren't entirely concrete into a Go type. func (v Value) Decode(x interface{}) error { var d decoder w := reflect.ValueOf(x) @@ -71,6 +75,8 @@ func (d *decoder) clear(x reflect.Value) { } } +var valueType = reflect.TypeOf(Value{}) + func (d *decoder) decode(x reflect.Value, v Value, isPtr bool) { if !x.IsValid() { d.addErr(errors.Newf(v.Pos(), "cannot decode into invalid value")) @@ -87,6 +93,10 @@ func (d *decoder) decode(x reflect.Value, v Value, isPtr bool) { d.addErr(err) return } + if x.Type() == valueType { + x.Set(reflect.ValueOf(v)) + return + } switch x.Kind() { case reflect.Ptr, reflect.Map, reflect.Slice, reflect.Interface: diff --git a/cue/decode_test.go b/cue/decode_test.go index acb435fd0a6..4782595ed67 100644 --- a/cue/decode_test.go +++ b/cue/decode_test.go @@ -15,10 +15,12 @@ package cue import ( + "fmt" "reflect" "testing" "time" + "github.com/go-quicktest/qt" "github.com/google/go-cmp/cmp" ) @@ -250,6 +252,25 @@ func TestDecode(t *testing.T) { } } +func TestDecodeIntoCUEValue(t *testing.T) { + // We should be able to decode into a CUE value so we can + // decode partially incomplete values into Go. + // This test doesn't fit within the table used by TestDecode + // because cue values aren't easily comparable with cmp.Diff. + var st struct { + X Value `json:"x"` + } + err := getInstance(t, `x: string`).Value().Decode(&st) + qt.Assert(t, qt.IsNil(err)) + qt.Assert(t, qt.Equals(fmt.Sprint(st.X), "string")) + + // Check we can decode into a top level value. + var v Value + err = getInstance(t, `int`).Value().Decode(&v) + qt.Assert(t, qt.IsNil(err)) + qt.Assert(t, qt.Equals(fmt.Sprint(v), "int")) +} + type Duration struct { D time.Duration }