github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/jsonhelpers/json_helpers.go (about)

     1  package jsonhelpers
     2  
     3  import (
     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.
    14  func 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.
    36  func 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.
    51  func 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.
    68  func 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.
    95  func 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  }