]>
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 | ||
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 | } |