github.com/kaituanwang/hyperledger@v2.0.1+incompatible/common/metrics/gendoc/options.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package gendoc 8 9 import ( 10 "fmt" 11 "go/ast" 12 "path" 13 "reflect" 14 "strconv" 15 "strings" 16 17 "github.com/hyperledger/fabric/common/metrics" 18 "golang.org/x/tools/go/packages" 19 ) 20 21 // Options scans the provided list of packages for options structs used when 22 // creating metrics and returns instances that are recreated from the source 23 // tree. 24 func Options(pkgs []*packages.Package) ([]interface{}, error) { 25 var options []interface{} 26 for _, p := range pkgs { 27 for _, f := range p.Syntax { 28 opts, err := FileOptions(f) 29 if err != nil { 30 return nil, err 31 } 32 options = append(options, opts...) 33 } 34 } 35 return options, nil 36 } 37 38 // FileOptions walks the specified ast.File for options structs used when 39 // creating metrics and returns instances that are recreated from the source. 40 func FileOptions(f *ast.File) ([]interface{}, error) { 41 var imports = walkImports(f) 42 var options []interface{} 43 var errors []error 44 45 // If the file contains gendoc:ignore, ignore the file 46 for _, c := range f.Comments { 47 if strings.Contains(c.Text(), "gendoc:ignore") { 48 return nil, nil 49 } 50 } 51 52 // Iterate over declarations 53 for i := range f.Decls { 54 ast.Inspect(f.Decls[i], func(x ast.Node) bool { 55 node, ok := x.(*ast.ValueSpec) 56 if !ok { 57 return true 58 } 59 60 for _, v := range node.Values { 61 value, ok := v.(*ast.CompositeLit) 62 if !ok { 63 continue 64 } 65 literalType, ok := value.Type.(*ast.SelectorExpr) 66 if !ok { 67 continue 68 } 69 ident, ok := literalType.X.(*ast.Ident) 70 if !ok { 71 continue 72 } 73 if imports[ident.Name] != "github.com/hyperledger/fabric/common/metrics" { 74 continue 75 } 76 option, err := createOption(literalType) 77 if err != nil { 78 errors = append(errors, err) 79 break 80 } 81 option, err = populateOption(value, option) 82 if err != nil { 83 errors = append(errors, err) 84 break 85 } 86 options = append(options, option) 87 } 88 return false 89 }) 90 } 91 92 if len(errors) != 0 { 93 return nil, errors[0] 94 } 95 96 return options, nil 97 } 98 99 func walkImports(f *ast.File) map[string]string { 100 imports := map[string]string{} 101 102 for i := range f.Imports { 103 ast.Inspect(f.Imports[i], func(x ast.Node) bool { 104 switch node := x.(type) { 105 case *ast.ImportSpec: 106 importPath, err := strconv.Unquote(node.Path.Value) 107 if err != nil { 108 panic(err) 109 } 110 importName := path.Base(importPath) 111 if node.Name != nil { 112 importName = node.Name.Name 113 } 114 imports[importName] = importPath 115 return false 116 117 default: 118 return true 119 } 120 }) 121 } 122 123 return imports 124 } 125 126 func createOption(lit *ast.SelectorExpr) (interface{}, error) { 127 optionName := lit.Sel.Name 128 switch optionName { 129 case "CounterOpts": 130 return &metrics.CounterOpts{}, nil 131 case "GaugeOpts": 132 return &metrics.GaugeOpts{}, nil 133 case "HistogramOpts": 134 return &metrics.HistogramOpts{}, nil 135 default: 136 return nil, fmt.Errorf("unknown object type: %s", optionName) 137 } 138 } 139 140 func populateOption(lit *ast.CompositeLit, target interface{}) (interface{}, error) { 141 val := reflect.ValueOf(target).Elem() 142 for _, elem := range lit.Elts { 143 if kv, ok := elem.(*ast.KeyValueExpr); ok { 144 name := kv.Key.(*ast.Ident).Name 145 field := val.FieldByName(name) 146 147 switch name { 148 // ignored 149 case "Buckets": 150 151 // map[string]string 152 case "LabelHelp": 153 labelHelp, err := asStringMap(kv.Value.(*ast.CompositeLit)) 154 if err != nil { 155 return nil, err 156 } 157 labelHelpValue := reflect.ValueOf(labelHelp) 158 field.Set(labelHelpValue) 159 160 // slice of strings 161 case "LabelNames": 162 labelNames, err := stringSlice(kv.Value.(*ast.CompositeLit)) 163 if err != nil { 164 return nil, err 165 } 166 labelNamesValue := reflect.ValueOf(labelNames) 167 field.Set(labelNamesValue) 168 169 // simple strings 170 case "Namespace", "Subsystem", "Name", "Help", "StatsdFormat": 171 basicVal := kv.Value.(*ast.BasicLit) 172 val, err := strconv.Unquote(basicVal.Value) 173 if err != nil { 174 return nil, err 175 } 176 field.SetString(val) 177 178 default: 179 return nil, fmt.Errorf("unknown field name: %s", name) 180 } 181 } 182 } 183 return val.Interface(), nil 184 } 185 186 func stringSlice(lit *ast.CompositeLit) ([]string, error) { 187 var slice []string 188 189 for _, elem := range lit.Elts { 190 val, err := strconv.Unquote(elem.(*ast.BasicLit).Value) 191 if err != nil { 192 return nil, err 193 } 194 slice = append(slice, val) 195 } 196 197 return slice, nil 198 } 199 200 func asStringMap(lit *ast.CompositeLit) (map[string]string, error) { 201 m := map[string]string{} 202 203 for _, elem := range lit.Elts { 204 elem := elem.(*ast.KeyValueExpr) 205 key, err := strconv.Unquote(elem.Key.(*ast.BasicLit).Value) 206 if err != nil { 207 return nil, err 208 } 209 value, err := strconv.Unquote(elem.Value.(*ast.BasicLit).Value) 210 if err != nil { 211 return nil, err 212 } 213 m[key] = value 214 } 215 216 return m, nil 217 }