github.com/alecthomas/kong@v0.9.1-0.20240410131203-2ab5733f1179/resolver.go (about)

     1  package kong
     2  
     3  import (
     4  	"encoding/json"
     5  	"io"
     6  	"strings"
     7  )
     8  
     9  // A Resolver resolves a Flag value from an external source.
    10  type Resolver interface {
    11  	// Validate configuration against Application.
    12  	//
    13  	// This can be used to validate that all provided configuration is valid within  this application.
    14  	Validate(app *Application) error
    15  
    16  	// Resolve the value for a Flag.
    17  	Resolve(context *Context, parent *Path, flag *Flag) (interface{}, error)
    18  }
    19  
    20  // ResolverFunc is a convenience type for non-validating Resolvers.
    21  type ResolverFunc func(context *Context, parent *Path, flag *Flag) (interface{}, error)
    22  
    23  var _ Resolver = ResolverFunc(nil)
    24  
    25  func (r ResolverFunc) Resolve(context *Context, parent *Path, flag *Flag) (interface{}, error) { //nolint: revive
    26  	return r(context, parent, flag)
    27  }
    28  func (r ResolverFunc) Validate(app *Application) error { return nil } //nolint: revive
    29  
    30  // JSON returns a Resolver that retrieves values from a JSON source.
    31  //
    32  // Flag names are used as JSON keys indirectly, by tring snake_case and camelCase variants.
    33  func JSON(r io.Reader) (Resolver, error) {
    34  	values := map[string]interface{}{}
    35  	err := json.NewDecoder(r).Decode(&values)
    36  	if err != nil {
    37  		return nil, err
    38  	}
    39  	var f ResolverFunc = func(context *Context, parent *Path, flag *Flag) (interface{}, error) {
    40  		name := strings.ReplaceAll(flag.Name, "-", "_")
    41  		snakeCaseName := snakeCase(flag.Name)
    42  		raw, ok := values[name]
    43  		if ok {
    44  			return raw, nil
    45  		} else if raw, ok = values[snakeCaseName]; ok {
    46  			return raw, nil
    47  		}
    48  		raw = values
    49  		for _, part := range strings.Split(name, ".") {
    50  			if values, ok := raw.(map[string]interface{}); ok {
    51  				raw, ok = values[part]
    52  				if !ok {
    53  					return nil, nil
    54  				}
    55  			} else {
    56  				return nil, nil
    57  			}
    58  		}
    59  		return raw, nil
    60  	}
    61  
    62  	return f, nil
    63  }
    64  
    65  func snakeCase(name string) string {
    66  	name = strings.Join(strings.Split(strings.Title(name), "-"), "") //nolint: staticcheck
    67  	return strings.ToLower(name[:1]) + name[1:]
    68  }