github.com/cmalfait/terraform@v0.11.12-beta1/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  var closerType = reflect.TypeOf((*io.Closer)(nil)).Elem()
    43  
    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  
    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  		}
    70  	}
    71  
    72  	if closer == nil {
    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  }