github.com/galdor/go-ejson@v0.0.0-20231201100034-d335379f26b0/pointer.go (about)

     1  package ejson
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"strconv"
     9  	"strings"
    10  )
    11  
    12  type Pointer []string
    13  
    14  var ErrInvalidPointerFormat = errors.New("invalid format")
    15  
    16  var (
    17  	tokenEncoder *strings.Replacer
    18  	tokenDecoder *strings.Replacer
    19  )
    20  
    21  func init() {
    22  	tokenEncoder = strings.NewReplacer("~", "~0", "/", "~1")
    23  	tokenDecoder = strings.NewReplacer("~1", "/", "~0", "~")
    24  }
    25  
    26  func NewPointer(tokens ...interface{}) Pointer {
    27  	return Pointer{}.Child(tokens...)
    28  }
    29  
    30  func (p *Pointer) Parse(s string) error {
    31  	if len(s) == 0 {
    32  		*p = Pointer{}
    33  		return nil
    34  	}
    35  
    36  	if s[0] != '/' {
    37  		return ErrInvalidPointerFormat
    38  	}
    39  
    40  	parts := strings.Split(s[1:], "/")
    41  
    42  	tokens := make([]string, len(parts))
    43  	for i, part := range parts {
    44  		tokens[i] = decodeToken(part)
    45  	}
    46  
    47  	*p = Pointer(tokens)
    48  
    49  	return nil
    50  }
    51  
    52  func (p *Pointer) MustParse(s string) {
    53  	if err := p.Parse(s); err != nil {
    54  		panic(fmt.Sprintf("invalid json pointer %q: %v", s, err))
    55  	}
    56  }
    57  
    58  func (p Pointer) String() string {
    59  	var buf bytes.Buffer
    60  
    61  	for _, token := range p {
    62  		buf.WriteByte('/')
    63  		buf.WriteString(encodeToken(token))
    64  	}
    65  
    66  	return buf.String()
    67  }
    68  
    69  func (p Pointer) MarshalJSON() ([]byte, error) {
    70  	return json.Marshal(p.String())
    71  }
    72  
    73  func (p *Pointer) UnmarshalJSON(data []byte) error {
    74  	var s string
    75  	if err := json.Unmarshal(data, &s); err != nil {
    76  		return err
    77  	}
    78  
    79  	return p.Parse(s)
    80  }
    81  
    82  func (p *Pointer) Prepend(tokens ...string) {
    83  	*p = append(Pointer(tokens), *p...)
    84  }
    85  
    86  func (p *Pointer) Append(tokens ...string) {
    87  	*p = append(*p, tokens...)
    88  }
    89  
    90  func (p Pointer) Parent() Pointer {
    91  	if len(p) == 0 {
    92  		panic("empty pointer")
    93  	}
    94  
    95  	return append(Pointer{}, p[:len(p)-1]...)
    96  }
    97  
    98  func (p Pointer) Child(tokens ...interface{}) Pointer {
    99  	p2 := append(Pointer{}, p...)
   100  
   101  	for _, token := range tokens {
   102  		switch v := token.(type) {
   103  		case string:
   104  			p2 = append(p2, v)
   105  
   106  		case int:
   107  			p2 = append(p2, strconv.Itoa(v))
   108  
   109  		case Pointer:
   110  			p2 = append(p2, v...)
   111  
   112  		case nil:
   113  
   114  		default:
   115  			panic(fmt.Sprintf("invalid json pointer token %#v (%T)",
   116  				token, token))
   117  		}
   118  	}
   119  
   120  	return p2
   121  }
   122  
   123  func (p Pointer) Find(value interface{}) interface{} {
   124  	v := value
   125  
   126  	for _, token := range p {
   127  		switch tv := v.(type) {
   128  		case []interface{}:
   129  			i, err := strconv.ParseInt(token, 10, 64)
   130  			if err != nil {
   131  				return nil
   132  			}
   133  
   134  			if i < 0 || i >= int64(len(tv)) {
   135  				return nil
   136  			}
   137  
   138  			v = tv[i]
   139  
   140  		case map[string]interface{}:
   141  			child, found := tv[token]
   142  			if !found {
   143  				return nil
   144  			}
   145  
   146  			v = child
   147  
   148  		default:
   149  			return nil
   150  		}
   151  	}
   152  
   153  	return v
   154  }
   155  
   156  func encodeToken(s string) string {
   157  	return tokenEncoder.Replace(s)
   158  }
   159  
   160  func decodeToken(s string) string {
   161  	return tokenDecoder.Replace(s)
   162  }