1package jsonhelpers
2
3import (
4	"errors"
5	"fmt"
6
7	keybase1 "github.com/keybase/client/go/protocol/keybase1"
8	jsonw "github.com/keybase/go-jsonw"
9)
10
11// JSONStringSimple converts a simple json object into a string.  Simple
12// objects are those that are not arrays or objects.  Non-simple objects result
13// in an error.
14func JSONStringSimple(object *jsonw.Wrapper) (string, error) {
15	x, err := object.GetInt()
16	if err == nil {
17		return fmt.Sprintf("%d", x), nil
18	}
19	y, err := object.GetString()
20	if err == nil {
21		return y, nil
22	}
23	z, err := object.GetBool()
24	if err == nil {
25		if z {
26			return "true", nil
27		}
28		return "false", nil
29	}
30
31	return "", fmt.Errorf("Non-simple object: %v", object)
32}
33
34// pyindex converts an index into a real index like python.
35// Returns an index to use and whether the index is safe to use.
36func pyindex(index, len int) (int, bool) {
37	if len <= 0 {
38		return 0, false
39	}
40	// wrap from the end
41	if index < 0 {
42		index = len + index
43	}
44	if index < 0 || index >= len {
45		return 0, false
46	}
47	return index, true
48}
49
50// Return the elements of an array.
51func jsonUnpackArray(w *jsonw.Wrapper) ([]*jsonw.Wrapper, error) {
52	w, err := w.ToArray()
53	if err != nil {
54		return nil, err
55	}
56	length, err := w.Len()
57	if err != nil {
58		return nil, err
59	}
60	res := make([]*jsonw.Wrapper, length)
61	for i := 0; i < length; i++ {
62		res[i] = w.AtIndex(i)
63	}
64	return res, nil
65}
66
67// Return the elements of an array or values of a map.
68func JSONGetChildren(w *jsonw.Wrapper) ([]*jsonw.Wrapper, error) {
69	dict, err := w.ToDictionary()
70	isDict := err == nil
71	array, err := w.ToArray()
72	isArray := err == nil
73
74	switch {
75	case isDict:
76		keys, err := dict.Keys()
77		if err != nil {
78			return nil, err
79		}
80		var res = make([]*jsonw.Wrapper, len(keys))
81		for i, key := range keys {
82			res[i] = dict.AtKey(key)
83		}
84		return res, nil
85	case isArray:
86		return jsonUnpackArray(array)
87	default:
88		return nil, errors.New("got children of non-container")
89	}
90}
91
92// Most failures here log instead of returning an error. If an error occurs,
93// ([], nil) will be returned.  This is because a selector may descend into
94// many subtrees and fail in all but one.
95func AtSelectorPath(selectedObject *jsonw.Wrapper, selectors []keybase1.SelectorEntry,
96	logger func(format string, arg ...interface{}), mkErr func(selector keybase1.SelectorEntry) error) ([]*jsonw.Wrapper, error) {
97	// The terminating condition is when we've consumed all the selectors.
98	if len(selectors) == 0 {
99		return []*jsonw.Wrapper{selectedObject}, nil
100	}
101
102	selector := selectors[0]
103	nextselectors := selectors[1:]
104
105	switch {
106	case selector.IsIndex:
107		object, err := selectedObject.ToArray()
108		if err != nil {
109			logger("JSON select by index from non-array: %v (%v) (%v)", err, selector.Index, object)
110			return nil, nil
111		}
112		length, err := object.Len()
113		if err != nil {
114			return nil, nil
115		}
116
117		index, ok := pyindex(selector.Index, length)
118		if !ok || index < 0 {
119			return nil, nil
120		}
121		nextobject := object.AtIndex(index)
122		return AtSelectorPath(nextobject, nextselectors, logger, mkErr)
123	case selector.IsKey:
124		object, err := selectedObject.ToDictionary()
125		if err != nil {
126			logger("JSON select by key from non-map: %v (%v) (%v)", err, selector.Key, object)
127			return nil, nil
128		}
129
130		nextobject := object.AtKey(selector.Key)
131		return AtSelectorPath(nextobject, nextselectors, logger, mkErr)
132	case selector.IsAll:
133		children, err := JSONGetChildren(selectedObject)
134		if err != nil {
135			logger("JSON select could not get children: %v (%v)", err, selectedObject)
136			return nil, nil
137		}
138		var results []*jsonw.Wrapper
139		for _, child := range children {
140			innerresults, perr := AtSelectorPath(child, nextselectors, logger, mkErr)
141			if perr != nil {
142				return nil, perr
143			}
144			results = append(results, innerresults...)
145		}
146		return results, nil
147	default:
148		return nil, mkErr(selector)
149	}
150}
151