terraform/helper/shadow/closer.go

84 lines
1.8 KiB
Go

package shadow
import (
"fmt"
"io"
"reflect"
"github.com/hashicorp/go-multierror"
"github.com/mitchellh/reflectwalk"
)
// Close will close all shadow values within the given structure.
//
// This uses reflection to walk the structure, find all shadow elements,
// and close them. Currently this will only find struct fields that are
// shadow values, and not slice elements, etc.
func Close(v interface{}) error {
// We require a pointer so we can address the internal fields
val := reflect.ValueOf(v)
if val.Kind() != reflect.Ptr {
return fmt.Errorf("value must be a pointer")
}
// Walk and close
var w closeWalker
if err := reflectwalk.Walk(v, &w); err != nil {
return err
}
return w.Err
}
type closeWalker struct {
Err error
}
func (w *closeWalker) Struct(reflect.Value) error {
// Do nothing. We implement this for reflectwalk.StructWalker
return nil
}
var closerType = reflect.TypeOf((*io.Closer)(nil)).Elem()
func (w *closeWalker) StructField(f reflect.StructField, v reflect.Value) error {
// Not sure why this would be but lets avoid some panics
if !v.IsValid() {
return nil
}
// Empty for exported, so don't check unexported fields
if f.PkgPath != "" {
return nil
}
// Verify the io.Closer is in this package
typ := v.Type()
if typ.PkgPath() != "github.com/hashicorp/terraform/helper/shadow" {
return nil
}
var closer io.Closer
if v.Type().Implements(closerType) {
closer = v.Interface().(io.Closer)
} else if v.CanAddr() {
// The Close method may require a pointer receiver, but we only have a value.
v := v.Addr()
if v.Type().Implements(closerType) {
closer = v.Interface().(io.Closer)
}
}
if closer == nil {
return reflectwalk.SkipEntry
}
// Close it
if err := closer.Close(); err != nil {
w.Err = multierror.Append(w.Err, err)
}
// Don't go into the struct field
return reflectwalk.SkipEntry
}