github.com/bhameyie/otto@v0.2.1-0.20160406174117-16052efa52ec/appfile/detect/parse.go (about)

     1  package detect
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  	"path/filepath"
     9  	"sort"
    10  
    11  	"github.com/hashicorp/hcl"
    12  	"github.com/hashicorp/hcl/hcl/ast"
    13  	"github.com/mitchellh/mapstructure"
    14  )
    15  
    16  // Parse parses the detector config from the given io.Reader.
    17  //
    18  // Due to current internal limitations, the entire contents of the
    19  // io.Reader will be copied into memory first before parsing.
    20  func Parse(r io.Reader) (*Config, error) {
    21  	// Copy the reader into an in-memory buffer first since HCL requires it.
    22  	var buf bytes.Buffer
    23  	if _, err := io.Copy(&buf, r); err != nil {
    24  		return nil, err
    25  	}
    26  
    27  	// Parse the buffer
    28  	root, err := hcl.Parse(buf.String())
    29  	if err != nil {
    30  		return nil, fmt.Errorf("error parsing: %s", err)
    31  	}
    32  	buf.Reset()
    33  
    34  	// Top-level item should be the object list
    35  	list, ok := root.Node.(*ast.ObjectList)
    36  	if !ok {
    37  		return nil, fmt.Errorf("error parsing: file doesn't contain a root object")
    38  	}
    39  
    40  	var result Config
    41  
    42  	// Parse the detects
    43  	if o := list.Filter("detect"); len(o.Items) > 0 {
    44  		if err := parseDetect(&result, o); err != nil {
    45  			return nil, fmt.Errorf("error parsing 'import': %s", err)
    46  		}
    47  	}
    48  
    49  	return &result, nil
    50  }
    51  
    52  // ParseFile parses the given path as a single detector config.
    53  func ParseFile(path string) (*Config, error) {
    54  	f, err := os.Open(path)
    55  	if err != nil {
    56  		return nil, err
    57  	}
    58  	defer f.Close()
    59  
    60  	return Parse(f)
    61  }
    62  
    63  // ParseDir parses all the files ending in ".hcl" in a directory,
    64  // sorted alphabetically.
    65  func ParseDir(path string) (*Config, error) {
    66  	// Read all the files
    67  	f, err := os.Open(path)
    68  	if err != nil {
    69  		if os.IsNotExist(err) {
    70  			return nil, nil
    71  		}
    72  
    73  		return nil, err
    74  	}
    75  	files, err := f.Readdirnames(-1)
    76  	f.Close()
    77  	if err != nil {
    78  		return nil, err
    79  	}
    80  
    81  	// Sort them
    82  	sort.Strings(files)
    83  
    84  	// Go through each, parse and merge.
    85  	var result Config
    86  	for _, f := range files {
    87  		// We only care if this is an HCL file
    88  		if filepath.Ext(f) != ".hcl" {
    89  			continue
    90  		}
    91  
    92  		// Stat the file. If it is a directory, ignore it
    93  		path := filepath.Join(path, f)
    94  		fi, err := os.Stat(path)
    95  		if err != nil {
    96  			return nil, err
    97  		}
    98  		if fi.IsDir() {
    99  			continue
   100  		}
   101  
   102  		// Parse
   103  		current, err := ParseFile(path)
   104  		if err != nil {
   105  			return nil, fmt.Errorf("error parsing %s: %s", path, err)
   106  		}
   107  
   108  		// Merge
   109  		if err := result.Merge(current); err != nil {
   110  			return nil, fmt.Errorf("error merging %s: %s", path, err)
   111  		}
   112  	}
   113  
   114  	return &result, nil
   115  }
   116  
   117  func parseDetect(result *Config, list *ast.ObjectList) error {
   118  	if len(list.Items) == 0 {
   119  		return nil
   120  	}
   121  
   122  	// Go through each object and turn it into an actual result.
   123  	collection := make([]*Detector, 0, len(list.Items))
   124  	for _, item := range list.Items {
   125  		key := item.Keys[0].Token.Value().(string)
   126  
   127  		var m map[string]interface{}
   128  		if err := hcl.DecodeObject(&m, item.Val); err != nil {
   129  			return err
   130  		}
   131  
   132  		var d Detector
   133  		if err := mapstructure.WeakDecode(m, &d); err != nil {
   134  			return fmt.Errorf(
   135  				"error parsing detector '%s': %s", key, err)
   136  		}
   137  
   138  		d.Type = key
   139  		collection = append(collection, &d)
   140  	}
   141  
   142  	result.Detectors = collection
   143  	return nil
   144  }