github.com/nathanielks/terraform@v0.6.1-0.20170509030759-13e1a62319dc/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  }