]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
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 | ||
c680a8e1 RS |
42 | var closerType = reflect.TypeOf((*io.Closer)(nil)).Elem() |
43 | ||
bae9f6d2 JC |
44 | func (w *closeWalker) StructField(f reflect.StructField, v reflect.Value) error { |
45 | // Not sure why this would be but lets avoid some panics | |
46 | if !v.IsValid() { | |
47 | return nil | |
48 | } | |
49 | ||
50 | // Empty for exported, so don't check unexported fields | |
51 | if f.PkgPath != "" { | |
52 | return nil | |
53 | } | |
54 | ||
55 | // Verify the io.Closer is in this package | |
56 | typ := v.Type() | |
57 | if typ.PkgPath() != "github.com/hashicorp/terraform/helper/shadow" { | |
58 | return nil | |
59 | } | |
60 | ||
c680a8e1 RS |
61 | var closer io.Closer |
62 | if v.Type().Implements(closerType) { | |
63 | closer = v.Interface().(io.Closer) | |
64 | } else if v.CanAddr() { | |
65 | // The Close method may require a pointer receiver, but we only have a value. | |
66 | v := v.Addr() | |
67 | if v.Type().Implements(closerType) { | |
68 | closer = v.Interface().(io.Closer) | |
69 | } | |
bae9f6d2 JC |
70 | } |
71 | ||
c680a8e1 | 72 | if closer == nil { |
bae9f6d2 JC |
73 | return reflectwalk.SkipEntry |
74 | } | |
75 | ||
76 | // Close it | |
77 | if err := closer.Close(); err != nil { | |
78 | w.Err = multierror.Append(w.Err, err) | |
79 | } | |
80 | ||
81 | // Don't go into the struct field | |
82 | return reflectwalk.SkipEntry | |
83 | } |