github.com/dahs81/otto@v0.2.1-0.20160126165905-6400716cf085/appfile/load/loader.go (about)

     1  package load
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"os"
     7  	"path/filepath"
     8  
     9  	"github.com/hashicorp/otto/app"
    10  	"github.com/hashicorp/otto/appfile"
    11  	"github.com/hashicorp/otto/appfile/detect"
    12  	"github.com/hashicorp/otto/otto"
    13  )
    14  
    15  // Loader is used to load an Appfile.
    16  //
    17  // This logic used to live in the "compile" command directly but was
    18  // extracted so it could be tested in isolation better and in case there
    19  // is any interest in reusability.
    20  //
    21  // The purpose of the loader is to do the multi-step Appfile load outlined
    22  // below. As input, we have the "real" Appfile, which is the real physical
    23  // Appfile if it exists (or nil if it doesn't).
    24  //
    25  //   1.) Detect the type, known as the "detected" Appfile
    26  //
    27  //   2.) Merge with the detected Appfile with the real Appfile. If
    28  //       the "detect" setting is set to "false", then we're done. Otherwise,
    29  //       continue.
    30  //
    31  //   3.) Instantiate the proper plugin for the app type and call the Implicit
    32  //       API to get an Appfile known as the "implicit" Appfile.
    33  //
    34  //   4.) Merge in the order: detected, implicit, real. Return the real
    35  //       Appfile.
    36  //
    37  type Loader struct {
    38  	// Detector is the detector configuration. If this is nil then
    39  	// no type detection will be done.
    40  	Detector *detect.Config
    41  
    42  	// Compiler is the appfile compiler that we're using. This is used
    43  	// to do a minimal compile (MinCompile) to realize imports of
    44  	// Appfiles prior to implicit loading.
    45  	Compiler *appfile.Compiler
    46  
    47  	// Apps will be used to load the proper app implementation for
    48  	// implicit loading.
    49  	Apps map[app.Tuple]app.Factory
    50  }
    51  
    52  func (l *Loader) Load(f *appfile.File, dir string) (*appfile.File, error) {
    53  	realFile := f
    54  
    55  	// Load the "detected" Appfile
    56  	appDef, err := appfile.Default(dir, l.Detector)
    57  	if err != nil {
    58  		return nil, fmt.Errorf("Error detecting app type: %s", err)
    59  	}
    60  
    61  	// Merge the detected Appfile with the real Appfile
    62  	var merged appfile.File
    63  	if err := merged.Merge(appDef); err != nil {
    64  		return nil, fmt.Errorf("Error loading Appfile: %s", err)
    65  	}
    66  	if realFile != nil {
    67  		if err := merged.Merge(realFile); err != nil {
    68  			return nil, fmt.Errorf("Error loading Appfile: %s", err)
    69  		}
    70  	}
    71  	realFile = &merged
    72  
    73  	// If we have no application type, there is nothing more to do
    74  	if realFile == nil || realFile.Application.Type == "" {
    75  		return realFile, nil
    76  	}
    77  
    78  	// If we're not configured to do any further detection, return it
    79  	if !realFile.Application.Detect {
    80  		return realFile, nil
    81  	}
    82  
    83  	// Minimally compile the file that we can use to create a core
    84  	compiled, err := l.Compiler.MinCompile(realFile)
    85  	if err != nil {
    86  		return nil, err
    87  	}
    88  
    89  	// Create a temporary directory we use for the core
    90  	td, err := ioutil.TempDir("", "otto")
    91  	if err != nil {
    92  		return nil, err
    93  	}
    94  	defer os.RemoveAll(td)
    95  
    96  	// Create a core
    97  	core, err := otto.NewCore(&otto.CoreConfig{
    98  		DataDir:    filepath.Join(td, "data"),
    99  		LocalDir:   filepath.Join(td, "local"),
   100  		CompileDir: filepath.Join(td, "compile"),
   101  		Appfile:    compiled,
   102  		Apps:       l.Apps,
   103  	})
   104  	if err != nil {
   105  		return nil, err
   106  	}
   107  
   108  	// Get the app implementation
   109  	appImpl, appCtx, err := core.App()
   110  	if err != nil {
   111  		return nil, err
   112  	}
   113  	defer app.Close(appImpl)
   114  
   115  	// Load the implicit Appfile
   116  	implicit, err := appImpl.Implicit(appCtx)
   117  	if err != nil {
   118  		return nil, err
   119  	}
   120  
   121  	var final appfile.File
   122  	if err := final.Merge(appDef); err != nil {
   123  		return nil, fmt.Errorf("Error loading Appfile: %s", err)
   124  	}
   125  	if implicit != nil {
   126  		if err := final.Merge(implicit); err != nil {
   127  			return nil, fmt.Errorf("Error loading Appfile: %s", err)
   128  		}
   129  	}
   130  	if f != nil {
   131  		if err := final.Merge(f); err != nil {
   132  			return nil, fmt.Errorf("Error loading Appfile: %s", err)
   133  		}
   134  	}
   135  
   136  	return &final, nil
   137  }