github.phpd.cn/thought-machine/please@v12.2.0+incompatible/src/help/config/config_help.go (about)

     1  // Package main implements a parser for our config structure
     2  // that emits help topics based on its struct tags.
     3  package main
     4  
     5  import (
     6  	"encoding/json"
     7  	"fmt"
     8  	"reflect"
     9  	"regexp"
    10  	"runtime"
    11  	"strings"
    12  
    13  	"core"
    14  )
    15  
    16  type output struct {
    17  	Preamble string            `json:"preamble"`
    18  	Topics   map[string]string `json:"topics"`
    19  }
    20  
    21  var urlRegex = regexp.MustCompile("https?://[^ ]+[^.]")
    22  
    23  // ExampleValue returns an example value for a config field based on its type.
    24  func ExampleValue(f reflect.Value, name string, t reflect.Type, example, options string) string {
    25  	if t.Kind() == reflect.Slice {
    26  		return ExampleValue(f, name, t.Elem(), example, options) + fmt.Sprintf("\n\n%s can be repeated", name)
    27  	} else if example != "" {
    28  		return example
    29  	} else if options != "" {
    30  		return strings.Replace(options, ",", " | ", -1)
    31  	} else if name == "version" {
    32  		return core.PleaseVersion.String() // keep it up to date!
    33  	} else if t.Kind() == reflect.String {
    34  		if f.String() != "" {
    35  			return f.String()
    36  		}
    37  		if t.Name() == "URL" {
    38  			return "https://mydomain.com/somepath"
    39  		}
    40  		return "<str>"
    41  	} else if t.Kind() == reflect.Bool {
    42  		return "true | false | yes | no | on | off"
    43  	} else if t.Name() == "Duration" {
    44  		return "10ms | 20s | 5m"
    45  	} else if t.Kind() == reflect.Int || t.Kind() == reflect.Int64 {
    46  		if f.Int() != 0 {
    47  			return fmt.Sprintf("%d", f.Int())
    48  		}
    49  		return "42"
    50  	} else if t.Name() == "ByteSize" {
    51  		return "5K | 10MB | 20GiB"
    52  	} else if t.Kind() == reflect.Uint64 {
    53  		return fmt.Sprintf("%d", f.Uint())
    54  	} else if t.Name() == "BuildLabel" {
    55  		return "//src/core:core"
    56  	} else if t.Name() == "Arch" {
    57  		return runtime.GOOS + "_" + runtime.GOARCH
    58  	}
    59  	panic(fmt.Sprintf("Unknown type: %s", t.Kind()))
    60  }
    61  
    62  func main() {
    63  	o := output{
    64  		Preamble: "${BOLD_BLUE}%s${RESET} is a config setting defined in the .plzconfig file. See `plz help plzconfig` for more information.",
    65  		Topics:   map[string]string{},
    66  	}
    67  	config := core.DefaultConfiguration()
    68  	v := reflect.ValueOf(config).Elem()
    69  	t := v.Type()
    70  	for i := 0; i < t.NumField(); i++ {
    71  		f := v.Field(i)
    72  		sectname := strings.ToLower(t.Field(i).Name)
    73  		subfields := []string{}
    74  		if f.Type().Kind() == reflect.Struct {
    75  			for j := 0; j < f.Type().NumField(); j++ {
    76  				subf := f.Field(j)
    77  				subt := t.Field(i).Type.Field(j)
    78  				if help := subt.Tag.Get("help"); help != "" {
    79  					name := strings.ToLower(subt.Name)
    80  					example := subt.Tag.Get("example")
    81  					preamble := fmt.Sprintf("${BOLD_YELLOW}[%s]${RESET}\n${YELLOW}%s${RESET} = ${GREEN}%s${RESET}\n\n", sectname, name, ExampleValue(subf, name, subt.Type, example, subt.Tag.Get("options")))
    82  					help = strings.Replace(help, "\\n", "\n", -1) + "\n"
    83  					if v := subt.Tag.Get("var"); v != "" {
    84  						help += fmt.Sprintf("\nThis variable is exposed to BUILD rules via the variable ${BOLD_CYAN}CONFIG.%s${RESET},\n"+
    85  							"and can be overridden package-locally via ${GREEN}package${RESET}(${YELLOW}%s${RESET}='${GREY}<value>${RESET}').\n", v, strings.ToLower(v))
    86  					}
    87  					o.Topics[name] = preamble + help
    88  					subfields = append(subfields, "  "+name)
    89  				} else if f.CanSet() {
    90  					panic(fmt.Sprintf("Missing help struct tag on %s.%s", t.Field(i).Name, subt.Name))
    91  				}
    92  			}
    93  		}
    94  		if help := t.Field(i).Tag.Get("help"); help != "" {
    95  			help += "\n"
    96  			if len(subfields) > 0 {
    97  				help += "\n${YELLOW}This option has the following sub-fields:${RESET}\n${GREEN}" + strings.Join(subfields, "\n") + "${RESET}\n"
    98  			}
    99  			o.Topics[sectname] = urlRegex.ReplaceAllStringFunc(help, func(s string) string { return "${BLUE}" + s + "${RESET}" })
   100  		}
   101  	}
   102  	b, _ := json.Marshal(o)
   103  	fmt.Println(string(b))
   104  }