github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/helper/shadow/closer.go (about) 1 package shadow 2 3 import ( 4 "fmt" 5 "io" 6 "reflect" 7 8 "github.com/hashicorp/go-multierror" 9 "github.com/mitchellh/reflectwalk" 10 ) 11 12 // Close will close all shadow values within the given structure. 13 // 14 // This uses reflection to walk the structure, find all shadow elements, 15 // and close them. Currently this will only find struct fields that are 16 // shadow values, and not slice elements, etc. 17 func Close(v interface{}) error { 18 // We require a pointer so we can address the internal fields 19 val := reflect.ValueOf(v) 20 if val.Kind() != reflect.Ptr { 21 return fmt.Errorf("value must be a pointer") 22 } 23 24 // Walk and close 25 var w closeWalker 26 if err := reflectwalk.Walk(v, &w); err != nil { 27 return err 28 } 29 30 return w.Err 31 } 32 33 type closeWalker struct { 34 Err error 35 } 36 37 func (w *closeWalker) Struct(reflect.Value) error { 38 // Do nothing. We implement this for reflectwalk.StructWalker 39 return nil 40 } 41 42 func (w *closeWalker) StructField(f reflect.StructField, v reflect.Value) error { 43 // Not sure why this would be but lets avoid some panics 44 if !v.IsValid() { 45 return nil 46 } 47 48 // Empty for exported, so don't check unexported fields 49 if f.PkgPath != "" { 50 return nil 51 } 52 53 // Verify the io.Closer is in this package 54 typ := v.Type() 55 if typ.PkgPath() != "github.com/hashicorp/terraform/helper/shadow" { 56 return nil 57 } 58 59 // We're looking for an io.Closer 60 raw := v.Interface() 61 if raw == nil { 62 return nil 63 } 64 65 closer, ok := raw.(io.Closer) 66 if !ok && v.CanAddr() { 67 closer, ok = v.Addr().Interface().(io.Closer) 68 } 69 if !ok { 70 return reflectwalk.SkipEntry 71 } 72 73 // Close it 74 if err := closer.Close(); err != nil { 75 w.Err = multierror.Append(w.Err, err) 76 } 77 78 // Don't go into the struct field 79 return reflectwalk.SkipEntry 80 }