github.com/influxdata/influxdb/v2@v2.7.6/kit/feature/_codegen/main.go (about) 1 package main 2 3 import ( 4 "bytes" 5 "flag" 6 "fmt" 7 "go/format" 8 "io" 9 "os" 10 "strings" 11 "text/template" 12 13 "github.com/Masterminds/sprig" 14 "github.com/influxdata/influxdb/v2/kit/feature" 15 yaml "gopkg.in/yaml.v2" 16 ) 17 18 const tmpl = `// Code generated by the feature package; DO NOT EDIT. 19 20 package feature 21 22 {{ .Qualify | import }} 23 24 {{ range $_, $flag := .Flags }} 25 var {{ $flag.Key }} = {{ $.Qualify | package }}{{ $flag.Default | maker }}( 26 {{ $flag.Name | quote }}, 27 {{ $flag.Key | quote }}, 28 {{ $flag.Contact | quote }}, 29 {{ $flag.Default | conditionalQuote }}, 30 {{ $.Qualify | package }}{{ $flag.Lifetime | lifetime }}, 31 {{ $flag.Expose }}, 32 ) 33 34 // {{ $flag.Name | replace " " "_" | camelcase }} - {{ $flag.Description }} 35 func {{ $flag.Name | replace " " "_" | camelcase }}() {{ $.Qualify | package }}{{ $flag.Default | flagType }} { 36 return {{ $flag.Key }} 37 } 38 {{ end }} 39 40 var all = []{{ .Qualify | package }}Flag{ 41 {{ range $_, $flag := .Flags }} {{ $flag.Key }}, 42 {{ end }}} 43 44 var byKey = map[string]{{ $.Qualify | package }}Flag{ 45 {{ range $_, $flag := .Flags }} {{ $flag.Key | quote }}: {{ $flag.Key }}, 46 {{ end }}} 47 ` 48 49 type flagConfig struct { 50 Name string 51 Description string 52 Key string 53 Default interface{} 54 Contact string 55 Lifetime feature.Lifetime 56 Expose bool 57 } 58 59 func (f flagConfig) Valid() error { 60 var problems []string 61 if f.Key == "" { 62 problems = append(problems, "missing key") 63 } 64 if f.Contact == "" { 65 problems = append(problems, "missing contact") 66 } 67 if f.Default == nil { 68 problems = append(problems, "missing default") 69 } 70 if f.Description == "" { 71 problems = append(problems, "missing description") 72 } 73 74 if len(problems) > 0 { 75 name := f.Name 76 if name == "" { 77 if f.Key != "" { 78 name = f.Key 79 } else { 80 name = "anonymous" 81 } 82 } 83 // e.g. "my flag: missing key; missing default" 84 return fmt.Errorf("%s: %s\n", name, strings.Join(problems, "; ")) 85 } 86 return nil 87 } 88 89 type flagValidationError struct { 90 errs []error 91 } 92 93 func newFlagValidationError(errs []error) *flagValidationError { 94 if len(errs) == 0 { 95 return nil 96 } 97 return &flagValidationError{errs} 98 } 99 100 func (e *flagValidationError) Error() string { 101 var s strings.Builder 102 s.WriteString("flag validation error: \n") 103 for _, err := range e.errs { 104 s.WriteString(err.Error()) 105 } 106 return s.String() 107 } 108 109 func validate(flags []flagConfig) error { 110 var ( 111 errs []error 112 seen = make(map[string]bool, len(flags)) 113 ) 114 for _, flag := range flags { 115 if err := flag.Valid(); err != nil { 116 errs = append(errs, err) 117 } else if _, repeated := seen[flag.Key]; repeated { 118 errs = append(errs, fmt.Errorf("duplicate flag key '%s'\n", flag.Key)) 119 } 120 seen[flag.Key] = true 121 } 122 if len(errs) != 0 { 123 return newFlagValidationError(errs) 124 } 125 126 return nil 127 } 128 129 var argv = struct { 130 in, out *string 131 qualify *bool 132 }{ 133 in: flag.String("in", "", "flag configuration path"), 134 out: flag.String("out", "", "flag generation destination path"), 135 qualify: flag.Bool("qualify", false, "qualify types with imported package name"), 136 } 137 138 func main() { 139 if err := run(); err != nil { 140 fmt.Fprintf(os.Stderr, "Error: %v\n", err) 141 os.Exit(1) 142 } 143 os.Exit(0) 144 } 145 146 func run() error { 147 flag.Parse() 148 149 in, err := os.Open(*argv.in) 150 if err != nil { 151 return err 152 } 153 defer in.Close() 154 155 configuration, err := io.ReadAll(in) 156 if err != nil { 157 return err 158 } 159 160 var flags []flagConfig 161 err = yaml.Unmarshal(configuration, &flags) 162 if err != nil { 163 return err 164 } 165 err = validate(flags) 166 if err != nil { 167 return err 168 } 169 170 t, err := template.New("flags").Funcs(templateFunctions()).Parse(tmpl) 171 if err != nil { 172 return err 173 } 174 175 out, err := os.Create(*argv.out) 176 if err != nil { 177 return err 178 } 179 defer out.Close() 180 181 var ( 182 buf = new(bytes.Buffer) 183 vars = struct { 184 Qualify bool 185 Flags []flagConfig 186 }{ 187 Qualify: *argv.qualify, 188 Flags: flags, 189 } 190 ) 191 if err := t.Execute(buf, vars); err != nil { 192 return err 193 } 194 195 raw, err := io.ReadAll(buf) 196 if err != nil { 197 return err 198 } 199 200 formatted, err := format.Source(raw) 201 if err != nil { 202 return err 203 } 204 205 _, err = out.Write(formatted) 206 return err 207 } 208 209 func templateFunctions() template.FuncMap { 210 functions := sprig.TxtFuncMap() 211 212 functions["lifetime"] = func(t interface{}) string { 213 switch t { 214 case feature.Permanent: 215 return "Permanent" 216 default: 217 return "Temporary" 218 } 219 } 220 221 functions["conditionalQuote"] = func(t interface{}) string { 222 switch t.(type) { 223 case string: 224 return fmt.Sprintf("%q", t) 225 default: 226 return fmt.Sprintf("%v", t) 227 } 228 } 229 230 functions["flagType"] = func(t interface{}) string { 231 switch t.(type) { 232 case bool: 233 return "BoolFlag" 234 case float64: 235 return "FloatFlag" 236 case int: 237 return "IntFlag" 238 default: 239 return "StringFlag" 240 } 241 } 242 243 functions["maker"] = func(t interface{}) string { 244 switch t.(type) { 245 case bool: 246 return "MakeBoolFlag" 247 case float64: 248 return "MakeFloatFlag" 249 case int: 250 return "MakeIntFlag" 251 default: 252 return "MakeStringFlag" 253 } 254 } 255 256 functions["package"] = func(t interface{}) string { 257 if t.(bool) { 258 return "feature." 259 } 260 return "" 261 } 262 263 functions["import"] = func(t interface{}) string { 264 if t.(bool) { 265 return "import \"github.com/influxdata/influxdb/v2/kit/feature\"" 266 } 267 return "" 268 } 269 270 return functions 271 }