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 }