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 }