-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdynjson.go
81 lines (71 loc) · 2.42 KB
/
dynjson.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
// Copyright 2015 Michal Witkowski.
// Copyright 2022 Fortio Authors.
// All Rights Reserved.
// See LICENSE for licensing terms.
package dflag
import (
"encoding/json"
"flag"
"reflect"
)
// JSON is the only/most kudlgy type, not playing so well or reusing as much as the rest of the generic re-implementation.
// DynJSON creates a `Flag` that is backed by an arbitrary JSON which is safe to change dynamically at runtime.
// The `value` must be a pointer to a struct that is JSON (un)marshallable.
// New values based on the default constructor of `value` type will be created on each update.
func DynJSON(flagSet *flag.FlagSet, name string, value interface{}, usage string) *DynJSONValue {
reflectVal := reflect.ValueOf(value)
if reflectVal.Kind() != reflect.Ptr ||
(reflectVal.Elem().Kind() != reflect.Struct && reflectVal.Elem().Kind() != reflect.Slice) {
panic("DynJSON value must be a pointer to a struct or to a slice")
}
dynValue := DynJSONValue{}
dynInit(&dynValue.DynValue, value, usage)
dynValue.flagSet = flagSet
dynValue.flagName = name
dynValue.structType = reflectVal.Type().Elem()
flagSet.Var(&dynValue, name, usage) // use our Set()
flagSet.Lookup(name).DefValue = dynValue.usageString()
return &dynValue
}
// DynJSONValue is a flag-related JSON struct value wrapper.
type DynJSONValue struct {
DynValue[interface{}]
structType reflect.Type
}
// IsJSON always return true (method is present for the DynamicJSONFlagValue interface tagging).
func (d *DynJSONValue) IsJSON() bool {
return true
}
// Set updates the value from a string representation in a thread-safe manner.
// This operation may return an error if the provided `input` doesn't parse, or the resulting value doesn't pass an
// optional validator.
// If a notifier is set on the value, it will be invoked in a separate go-routine.
func (d *DynJSONValue) Set(rawInput string) error {
input := rawInput
if d.inpMutator != nil {
input = d.inpMutator(rawInput)
}
val := reflect.New(d.structType).Interface()
if err := json.Unmarshal([]byte(input), val); err != nil {
return err
}
return d.SetV(val)
}
// String returns the canonical string representation of the type.
func (d *DynJSONValue) String() string {
if !d.ready {
return ""
}
out, err := json.Marshal(d.Get())
if err != nil {
return "ERR"
}
return string(out)
}
func (d *DynJSONValue) usageString() string {
s := d.String()
if len(s) > 128 {
return "{ ... truncated ... }"
}
return s
}