github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/internal/k8s/json_path.go (about)

     1  package k8s
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/tilt-dev/tilt/internal/k8s/jsonpath"
     7  )
     8  
     9  // A wrapper around JSONPath with utility functions for
    10  // locating particular types we need (like strings).
    11  //
    12  // Improves the error message to include the problematic path.
    13  type JSONPath struct {
    14  	path string
    15  }
    16  
    17  func NewJSONPath(path string) (JSONPath, error) {
    18  	// Make sure the JSON path parses.
    19  	jp := jsonpath.New("jp")
    20  	err := jp.Parse(path)
    21  	if err != nil {
    22  		return JSONPath{}, err
    23  	}
    24  
    25  	return JSONPath{
    26  		path: path,
    27  	}, nil
    28  }
    29  
    30  // Extract all the strings from the given object.
    31  // Returns an error if the object at the specified path isn't a string.
    32  func (jp JSONPath) FindStrings(obj interface{}) ([]string, error) {
    33  	result := []string{}
    34  	err := jp.VisitStrings(obj, func(match jsonpath.Value, s string) error {
    35  		result = append(result, s)
    36  		return nil
    37  	})
    38  	if err != nil {
    39  		return nil, err
    40  	}
    41  	return result, nil
    42  }
    43  
    44  // Visit all the strings from the given object.
    45  //
    46  // Returns an error if the object at the specified path isn't a string.
    47  func (jp JSONPath) VisitStrings(obj interface{}, visit func(val jsonpath.Value, str string) error) error {
    48  	return jp.Visit(obj, func(match jsonpath.Value) error {
    49  		val := match.Interface()
    50  		str, ok := val.(string)
    51  		if !ok {
    52  			return fmt.Errorf("May only match strings (json_path=%q)\nGot Type: %T\nGot Value: %s",
    53  				jp.path, val, val)
    54  		}
    55  
    56  		return visit(match, str)
    57  	})
    58  }
    59  
    60  // Visit all the values from the given object on this path.
    61  func (jp JSONPath) Visit(obj interface{}, visit func(val jsonpath.Value) error) error {
    62  	// JSONPath is stateful and not thread-safe, so we need to parse a new one
    63  	// each time
    64  	matcher := jsonpath.New("jp")
    65  	err := matcher.Parse(jp.path)
    66  	if err != nil {
    67  		return fmt.Errorf("Matching (json_path=%q): %v", jp.path, err)
    68  	}
    69  
    70  	matches, err := matcher.FindResults(obj)
    71  	if err != nil {
    72  		return fmt.Errorf("Matching (json_path=%q): %v", jp.path, err)
    73  	}
    74  
    75  	for _, matchSet := range matches {
    76  		for _, match := range matchSet {
    77  			err := visit(match)
    78  			if err != nil {
    79  				return err
    80  			}
    81  		}
    82  	}
    83  	return nil
    84  }
    85  
    86  func (jp JSONPath) String() string {
    87  	return jp.path
    88  }