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  }