@@ -3,13 +3,35 @@ package convert
33import (
44 "fmt"
55 "reflect"
6+ "slices"
67
78 "github.com/databricks/cli/libs/dyn"
89)
910
11+ type fromTypedOptions int
12+
13+ const (
14+ // Use the zero value instead of setting zero values to nil. This is useful
15+ // for types where the zero values and nil are semantically different. That is
16+ // strings, bools, ints, floats.
17+ //
18+ // Note: this is not needed for structs because dyn.NilValue is converted back
19+ // to a zero value when using the convert.ToTyped function.
20+ //
21+ // Values in maps and slices should be set to zero values, and not nil in the
22+ // dynamic representation.
23+ includeZeroValues fromTypedOptions = 1 << iota
24+ )
25+
1026// FromTyped converts changes made in the typed structure w.r.t. the configuration value
1127// back to the configuration value, retaining existing location information where possible.
1228func FromTyped (src any , ref dyn.Value ) (dyn.Value , error ) {
29+ return fromTyped (src , ref )
30+ }
31+
32+ // Private implementation of FromTyped that allows for additional options not exposed
33+ // in the public API.
34+ func fromTyped (src any , ref dyn.Value , options ... fromTypedOptions ) (dyn.Value , error ) {
1335 srcv := reflect .ValueOf (src )
1436
1537 // Dereference pointer if necessary
@@ -28,13 +50,13 @@ func FromTyped(src any, ref dyn.Value) (dyn.Value, error) {
2850 case reflect .Slice :
2951 return fromTypedSlice (srcv , ref )
3052 case reflect .String :
31- return fromTypedString (srcv , ref )
53+ return fromTypedString (srcv , ref , options ... )
3254 case reflect .Bool :
33- return fromTypedBool (srcv , ref )
55+ return fromTypedBool (srcv , ref , options ... )
3456 case reflect .Int , reflect .Int32 , reflect .Int64 :
35- return fromTypedInt (srcv , ref )
57+ return fromTypedInt (srcv , ref , options ... )
3658 case reflect .Float32 , reflect .Float64 :
37- return fromTypedFloat (srcv , ref )
59+ return fromTypedFloat (srcv , ref , options ... )
3860 }
3961
4062 return dyn .NilValue , fmt .Errorf ("unsupported type: %s" , srcv .Kind ())
@@ -52,7 +74,7 @@ func fromTypedStruct(src reflect.Value, ref dyn.Value) (dyn.Value, error) {
5274 info := getStructInfo (src .Type ())
5375 for k , v := range info .FieldValues (src ) {
5476 // Convert the field taking into account the reference value (may be equal to config.NilValue).
55- nv , err := FromTyped (v .Interface (), ref .Get (k ))
77+ nv , err := fromTyped (v .Interface (), ref .Get (k ))
5678 if err != nil {
5779 return dyn.Value {}, err
5880 }
@@ -89,8 +111,8 @@ func fromTypedMap(src reflect.Value, ref dyn.Value) (dyn.Value, error) {
89111 k := iter .Key ().String ()
90112 v := iter .Value ()
91113
92- // Convert entry taking into account the reference value (may be equal to config .NilValue).
93- nv , err := FromTyped (v .Interface (), ref .Get (k ))
114+ // Convert entry taking into account the reference value (may be equal to dyn .NilValue).
115+ nv , err := fromTyped (v .Interface (), ref .Get (k ), includeZeroValues )
94116 if err != nil {
95117 return dyn.Value {}, err
96118 }
@@ -120,8 +142,8 @@ func fromTypedSlice(src reflect.Value, ref dyn.Value) (dyn.Value, error) {
120142 for i := 0 ; i < src .Len (); i ++ {
121143 v := src .Index (i )
122144
123- // Convert entry taking into account the reference value (may be equal to config .NilValue).
124- nv , err := FromTyped (v .Interface (), ref .Index (i ))
145+ // Convert entry taking into account the reference value (may be equal to dyn .NilValue).
146+ nv , err := fromTyped (v .Interface (), ref .Index (i ), includeZeroValues )
125147 if err != nil {
126148 return dyn.Value {}, err
127149 }
@@ -132,7 +154,7 @@ func fromTypedSlice(src reflect.Value, ref dyn.Value) (dyn.Value, error) {
132154 return dyn .NewValue (out , ref .Location ()), nil
133155}
134156
135- func fromTypedString (src reflect.Value , ref dyn.Value ) (dyn.Value , error ) {
157+ func fromTypedString (src reflect.Value , ref dyn.Value , options ... fromTypedOptions ) (dyn.Value , error ) {
136158 switch ref .Kind () {
137159 case dyn .KindString :
138160 value := src .String ()
@@ -142,9 +164,9 @@ func fromTypedString(src reflect.Value, ref dyn.Value) (dyn.Value, error) {
142164
143165 return dyn .V (value ), nil
144166 case dyn .KindNil :
145- // This field is not set in the reference, so we only include it if it has a non- zero value.
146- // Otherwise, we would always include all zero valued fields .
147- if src .IsZero () {
167+ // This field is not set in the reference. We set it to nil if it's zero
168+ // valued in the typed representation and the includeZeroValues option is not set .
169+ if src .IsZero () && ! slices . Contains ( options , includeZeroValues ) {
148170 return dyn .NilValue , nil
149171 }
150172 return dyn .V (src .String ()), nil
@@ -153,7 +175,7 @@ func fromTypedString(src reflect.Value, ref dyn.Value) (dyn.Value, error) {
153175 return dyn.Value {}, fmt .Errorf ("unhandled type: %s" , ref .Kind ())
154176}
155177
156- func fromTypedBool (src reflect.Value , ref dyn.Value ) (dyn.Value , error ) {
178+ func fromTypedBool (src reflect.Value , ref dyn.Value , options ... fromTypedOptions ) (dyn.Value , error ) {
157179 switch ref .Kind () {
158180 case dyn .KindBool :
159181 value := src .Bool ()
@@ -162,9 +184,9 @@ func fromTypedBool(src reflect.Value, ref dyn.Value) (dyn.Value, error) {
162184 }
163185 return dyn .V (value ), nil
164186 case dyn .KindNil :
165- // This field is not set in the reference, so we only include it if it has a non- zero value.
166- // Otherwise, we would always include all zero valued fields .
167- if src .IsZero () {
187+ // This field is not set in the reference. We set it to nil if it's zero
188+ // valued in the typed representation and the includeZeroValues option is not set .
189+ if src .IsZero () && ! slices . Contains ( options , includeZeroValues ) {
168190 return dyn .NilValue , nil
169191 }
170192 return dyn .V (src .Bool ()), nil
@@ -173,7 +195,7 @@ func fromTypedBool(src reflect.Value, ref dyn.Value) (dyn.Value, error) {
173195 return dyn.Value {}, fmt .Errorf ("unhandled type: %s" , ref .Kind ())
174196}
175197
176- func fromTypedInt (src reflect.Value , ref dyn.Value ) (dyn.Value , error ) {
198+ func fromTypedInt (src reflect.Value , ref dyn.Value , options ... fromTypedOptions ) (dyn.Value , error ) {
177199 switch ref .Kind () {
178200 case dyn .KindInt :
179201 value := src .Int ()
@@ -182,9 +204,9 @@ func fromTypedInt(src reflect.Value, ref dyn.Value) (dyn.Value, error) {
182204 }
183205 return dyn .V (value ), nil
184206 case dyn .KindNil :
185- // This field is not set in the reference, so we only include it if it has a non- zero value.
186- // Otherwise, we would always include all zero valued fields .
187- if src .IsZero () {
207+ // This field is not set in the reference. We set it to nil if it's zero
208+ // valued in the typed representation and the includeZeroValues option is not set .
209+ if src .IsZero () && ! slices . Contains ( options , includeZeroValues ) {
188210 return dyn .NilValue , nil
189211 }
190212 return dyn .V (src .Int ()), nil
@@ -193,7 +215,7 @@ func fromTypedInt(src reflect.Value, ref dyn.Value) (dyn.Value, error) {
193215 return dyn.Value {}, fmt .Errorf ("unhandled type: %s" , ref .Kind ())
194216}
195217
196- func fromTypedFloat (src reflect.Value , ref dyn.Value ) (dyn.Value , error ) {
218+ func fromTypedFloat (src reflect.Value , ref dyn.Value , options ... fromTypedOptions ) (dyn.Value , error ) {
197219 switch ref .Kind () {
198220 case dyn .KindFloat :
199221 value := src .Float ()
@@ -202,9 +224,9 @@ func fromTypedFloat(src reflect.Value, ref dyn.Value) (dyn.Value, error) {
202224 }
203225 return dyn .V (value ), nil
204226 case dyn .KindNil :
205- // This field is not set in the reference, so we only include it if it has a non- zero value.
206- // Otherwise, we would always include all zero valued fields .
207- if src .IsZero () {
227+ // This field is not set in the reference. We set it to nil if it's zero
228+ // valued in the typed representation and the includeZeroValues option is not set .
229+ if src .IsZero () && ! slices . Contains ( options , includeZeroValues ) {
208230 return dyn .NilValue , nil
209231 }
210232 return dyn .V (src .Float ()), nil
0 commit comments