@@ -2,10 +2,14 @@ package stores
22
33import (
44 "fmt"
5+ "reflect"
6+ "slices"
57 "strconv"
68 "strings"
79
810 "github.com/go-viper/mapstructure/v2"
11+
12+ "github.com/getsops/sops/v3"
913)
1014
1115const mapSeparator = "__map_"
@@ -270,3 +274,154 @@ func EncodeNonStrings(m map[string]interface{}) {
270274 }
271275 }
272276}
277+
278+ func descendMap (branch sops.TreeBranch , key string ) (sops.TreeBranch , reflect.Value ) {
279+ for idx , elt := range branch {
280+ if elt .Key == key {
281+ return branch , reflect .ValueOf (& branch [idx ].Value )
282+ }
283+ }
284+ newBranch := append (branch , sops.TreeItem {
285+ Key : key ,
286+ Value : nil ,
287+ })
288+ return newBranch , reflect .ValueOf (& newBranch [len (newBranch )- 1 ].Value )
289+ }
290+
291+ func descendList (array []interface {}, index int ) ([]interface {}, reflect.Value ) {
292+ if index >= len (array ) {
293+ if index >= cap (array ) {
294+ array = slices .Grow (array , index + 1 - cap (array ))
295+ }
296+ array = array [:index + 1 ]
297+ }
298+ return array , reflect .ValueOf (& array [index ])
299+ }
300+
301+ func descend (value reflect.Value , t token ) (reflect.Value , error ) {
302+ interfaceType := reflect .TypeOf ((* interface {})(nil ))
303+ switch currToken := t .(type ) {
304+ case mapToken :
305+ // This special case is only needed for the root
306+ treeBranchPtrType := reflect .TypeOf ((* sops .TreeBranch )(nil ))
307+ if value .Type () == treeBranchPtrType {
308+ v := * value .Interface ().(* sops.TreeBranch )
309+ v , nextVal := descendMap (v , currToken .key )
310+ reflect .Indirect (value ).Set (reflect .ValueOf (v ))
311+ return nextVal , nil
312+ }
313+ if value .Type () == interfaceType {
314+ val := reflect .Indirect (value )
315+ if val .IsNil () {
316+ v , nextVal := descendMap (nil , currToken .key )
317+ val .Set (reflect .ValueOf (v ))
318+ return nextVal , nil
319+ }
320+ if v , ok := val .Interface ().(sops.TreeBranch ); ok {
321+ v , nextVal := descendMap (v , currToken .key )
322+ val .Set (reflect .ValueOf (v ))
323+ return nextVal , nil
324+ }
325+ }
326+ return reflect.Value {}, fmt .Errorf ("Type mismatch: can only use string key for map" )
327+ case listToken :
328+ if value .Type () == interfaceType {
329+ val := reflect .Indirect (value )
330+ if val .IsNil () {
331+ v , nextVal := descendList (nil , currToken .position )
332+ val .Set (reflect .ValueOf (v ))
333+ return nextVal , nil
334+ }
335+ if v , ok := val .Interface ().([]interface {}); ok {
336+ v , nextVal := descendList (v , currToken .position )
337+ val .Set (reflect .ValueOf (v ))
338+ return nextVal , nil
339+ }
340+ }
341+ return reflect.Value {}, fmt .Errorf ("Type mismatch: can only use integer key for list" )
342+ default :
343+ return reflect.Value {}, fmt .Errorf ("Internal error: unknown token %q" , t )
344+ }
345+ }
346+
347+ func unflattenTreeBranch (branch sops.TreeBranch ) (sops.TreeBranch , error ) {
348+ var result sops.TreeBranch
349+ for _ , item := range branch {
350+ if _ , ok := item .Key .(sops.Comment ); ok {
351+ continue
352+ }
353+ if key , ok := item .Key .(string ); ok {
354+ current := reflect .ValueOf (& result )
355+ tokens := tokenize (key )
356+ for _ , token := range tokens {
357+ var err error
358+ current , err = descend (current , token )
359+ if err != nil {
360+ return nil , fmt .Errorf ("Error while unflattening %q: %w" , key , err )
361+ }
362+ }
363+ reflect .Indirect (current ).Set (reflect .ValueOf (item .Value ))
364+ continue
365+ } else {
366+ return nil , fmt .Errorf ("Found non-string key %q when unflattening" , item .Key )
367+ }
368+ }
369+ return result , nil
370+ }
371+
372+ func flattenDescendValue (value interface {}, key string , destination sops.TreeBranch , destinationMap * map [string ]bool ) (sops.TreeBranch , error ) {
373+ switch value := value .(type ) {
374+ case sops.TreeBranch :
375+ return flattenDescendMap (value , key + mapSeparator , destination , destinationMap )
376+ case []interface {}:
377+ return flattenDescendArray (value , key + listSeparator , destination , destinationMap )
378+ }
379+ if _ , ok := (* destinationMap )[key ]; ok {
380+ return nil , fmt .Errorf ("Found key collision %q while flattening" , key )
381+ }
382+ destination = append (destination , sops.TreeItem {
383+ Key : key ,
384+ Value : value ,
385+ })
386+ (* destinationMap )[key ] = true
387+ return destination , nil
388+ }
389+
390+ func flattenDescendMap (branch sops.TreeBranch , prefix string , destination sops.TreeBranch , destinationMap * map [string ]bool ) (sops.TreeBranch , error ) {
391+ for _ , item := range branch {
392+ if _ , ok := item .Key .(sops.Comment ); ok {
393+ continue
394+ }
395+ if key , ok := item .Key .(string ); ok {
396+ var err error
397+ destination , err = flattenDescendValue (item .Value , prefix + key , destination , destinationMap )
398+ if err != nil {
399+ return nil , err
400+ }
401+ } else {
402+ return nil , fmt .Errorf ("Found non-string key %q when flattening" , item .Key )
403+ }
404+ }
405+ return destination , nil
406+ }
407+
408+ func flattenDescendArray (array []interface {}, prefix string , destination sops.TreeBranch , destinationMap * map [string ]bool ) (sops.TreeBranch , error ) {
409+ i := 0
410+ for _ , item := range array {
411+ if _ , ok := item .(sops.Comment ); ok {
412+ continue
413+ }
414+ var err error
415+ destination , err = flattenDescendValue (item , fmt .Sprintf ("%s%d" , prefix , i ), destination , destinationMap )
416+ if err != nil {
417+ return nil , err
418+ }
419+ i += 1
420+ }
421+ return destination , nil
422+ }
423+
424+ func flattenTreeBranch (branch sops.TreeBranch , prefix string ) (sops.TreeBranch , error ) {
425+ destinationMap := map [string ]bool {}
426+ return flattenDescendMap (branch , prefix , nil , & destinationMap )
427+ }
0 commit comments