You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
urfave-cli/altsrc/json_source_context.go

241 lines
5.3 KiB

package altsrc
import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"strings"
"time"
"github.com/urfave/cli/v3"
)
// NewJSONSourceFromFlagFunc returns a func that takes a cli.Context
// and returns an InputSourceContext suitable for retrieving config
// variables from a file containing JSON data with the file name defined
// by the given flag.
func NewJSONSourceFromFlagFunc(flag string) func(c *cli.Context) (InputSourceContext, error) {
return func(cCtx *cli.Context) (InputSourceContext, error) {
if cCtx.IsSet(flag) {
return NewJSONSourceFromFile(cCtx.String(flag))
}
return defaultInputSource()
}
}
// NewJSONSourceFromFile returns an InputSourceContext suitable for
// retrieving config variables from a file (or url) containing JSON
// data.
func NewJSONSourceFromFile(f string) (InputSourceContext, error) {
data, err := loadDataFrom(f)
if err != nil {
return nil, err
}
return NewJSONSource(data)
}
// NewJSONSourceFromReader returns an InputSourceContext suitable for
// retrieving config variables from an io.Reader that returns JSON data.
func NewJSONSourceFromReader(r io.Reader) (InputSourceContext, error) {
data, err := ioutil.ReadAll(r)
if err != nil {
return nil, err
}
return NewJSONSource(data)
}
// NewJSONSource returns an InputSourceContext suitable for retrieving
// config variables from raw JSON data.
func NewJSONSource(data []byte) (InputSourceContext, error) {
var deserialized map[string]interface{}
if err := json.Unmarshal(data, &deserialized); err != nil {
return nil, err
}
return &jsonSource{deserialized: deserialized}, nil
}
func (x *jsonSource) Source() string {
return x.file
}
func (x *jsonSource) Int(name string) (int, error) {
i, err := x.getValue(name)
if err != nil {
return 0, err
}
switch v := i.(type) {
default:
return 0, fmt.Errorf("unexpected type %T for %q", i, name)
case int:
return v, nil
case float32:
return int(v), nil
case float64:
return int(v), nil
}
}
func (x *jsonSource) Duration(name string) (time.Duration, error) {
i, err := x.getValue(name)
if err != nil {
return 0, err
}
v, ok := i.(time.Duration)
if !ok {
return 0, fmt.Errorf("unexpected type %T for %q", i, name)
}
return v, nil
}
func (x *jsonSource) Float64(name string) (float64, error) {
i, err := x.getValue(name)
if err != nil {
return 0, err
}
v, ok := i.(float64)
if !ok {
return 0, fmt.Errorf("unexpected type %T for %q", i, name)
}
return v, nil
}
func (x *jsonSource) String(name string) (string, error) {
i, err := x.getValue(name)
if err != nil {
return "", err
}
v, ok := i.(string)
if !ok {
return "", fmt.Errorf("unexpected type %T for %q", i, name)
}
return v, nil
}
func (x *jsonSource) StringSlice(name string) ([]string, error) {
i, err := x.getValue(name)
if err != nil {
return nil, err
}
switch v := i.(type) {
default:
return nil, fmt.Errorf("unexpected type %T for %q", i, name)
case []string:
return v, nil
case []interface{}:
c := []string{}
for _, s := range v {
if str, ok := s.(string); ok {
c = append(c, str)
} else {
return c, fmt.Errorf("unexpected item type %T in %T for %q", s, c, name)
}
}
return c, nil
}
}
func (x *jsonSource) IntSlice(name string) ([]int, error) {
i, err := x.getValue(name)
if err != nil {
return nil, err
}
switch v := i.(type) {
default:
return nil, fmt.Errorf("unexpected type %T for %q", i, name)
case []int:
return v, nil
case []interface{}:
c := []int{}
for _, s := range v {
if i2, ok := s.(int); ok {
c = append(c, i2)
} else {
return c, fmt.Errorf("unexpected item type %T in %T for %q", s, c, name)
}
}
return c, nil
}
}
func (x *jsonSource) Int64Slice(name string) ([]int64, error) {
i, err := x.getValue(name)
if err != nil {
return nil, err
}
switch v := i.(type) {
default:
return nil, fmt.Errorf("unexpected type %T for %q", i, name)
case []int64:
return v, nil
case []interface{}:
c := []int64{}
for _, s := range v {
if i2, ok := s.(int64); ok {
c = append(c, i2)
} else {
return c, fmt.Errorf("unexpected item type %T in %T for %q", s, c, name)
}
}
return c, nil
}
}
func (x *jsonSource) Generic(name string) (cli.Generic, error) {
i, err := x.getValue(name)
if err != nil {
return nil, err
}
v, ok := i.(cli.Generic)
if !ok {
return nil, fmt.Errorf("unexpected type %T for %q", i, name)
}
return v, nil
}
func (x *jsonSource) Bool(name string) (bool, error) {
i, err := x.getValue(name)
if err != nil {
return false, err
}
v, ok := i.(bool)
if !ok {
return false, fmt.Errorf("unexpected type %T for %q", i, name)
}
return v, nil
}
func (x *jsonSource) isSet(name string) bool {
_, err := x.getValue(name)
return err == nil
}
func (x *jsonSource) getValue(key string) (interface{}, error) {
return jsonGetValue(key, x.deserialized)
}
func jsonGetValue(key string, m map[string]interface{}) (interface{}, error) {
var ret interface{}
var ok bool
working := m
keys := strings.Split(key, ".")
for ix, k := range keys {
if ret, ok = working[k]; !ok {
return ret, fmt.Errorf("missing key %q", key)
}
if working, ok = ret.(map[string]interface{}); !ok {
if ix < len(keys)-1 {
return ret, fmt.Errorf("unexpected intermediate value at %q segment of %q: %T", k, key, ret)
}
}
}
return ret, nil
}
type jsonSource struct {
file string
deserialized map[string]interface{}
}