this repo has no description
at master 98 lines 2.7 kB view raw
1// Copyright 2025 CUE Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package json 16 17import ( 18 "fmt" 19 "iter" 20 "strconv" 21 "strings" 22 23 "cuelang.org/go/cue" 24) 25 26var ( 27 jsonPtrEsc = strings.NewReplacer("~", "~0", "/", "~1") 28 jsonPtrUnesc = strings.NewReplacer("~0", "~", "~1", "/") 29) 30 31// Pointer represents a JSON Pointer as defined by RFC 6901. 32// It is a slash-separated list of tokens that reference a specific location 33// within a JSON document. 34// TODO(go1.26) alias this to [encoding/json/jsontext.Pointer] 35type Pointer string 36 37// PointerFromTokens returns a JSON Pointer formed from 38// the unquoted tokens in the given sequence. Any 39// slash (/) or tilde (~) characters will be escaped appropriately. 40func PointerFromTokens(tokens iter.Seq[string]) Pointer { 41 var buf strings.Builder 42 for tok := range tokens { 43 buf.WriteByte('/') 44 buf.WriteString(jsonPtrEsc.Replace(tok)) 45 } 46 return Pointer(buf.String()) 47} 48 49// Tokens returns a sequence of all the 50// unquoted path elements (tokens) of the JSON Pointer. 51func (p Pointer) Tokens() iter.Seq[string] { 52 s := string(p) 53 return func(yield func(string) bool) { 54 needUnesc := strings.IndexByte(s, '~') >= 0 55 for len(s) > 0 { 56 s = strings.TrimPrefix(s, "/") 57 i := min(uint(strings.IndexByte(s, '/')), uint(len(s))) 58 tok := s[:i] 59 if needUnesc { 60 tok = jsonPtrUnesc.Replace(tok) 61 } 62 if !yield(tok) { 63 return 64 } 65 s = s[i:] 66 } 67 } 68} 69 70// PointerFromCUEPath returns a JSON Pointer equivalent to the 71// given CUE path. It returns an error if the path contains an element 72// that cannot be represented as a JSON Pointer. 73func PointerFromCUEPath(p cue.Path) (Pointer, error) { 74 var err error 75 ptr := PointerFromTokens(func(yield func(s string) bool) { 76 for _, sel := range p.Selectors() { 77 var token string 78 switch sel.Type() { 79 case cue.StringLabel: 80 token = sel.Unquoted() 81 case cue.IndexLabel: 82 token = strconv.Itoa(sel.Index()) 83 default: 84 if err == nil { 85 err = fmt.Errorf("cannot convert selector %v to JSON pointer", sel) 86 continue 87 } 88 } 89 if !yield(token) { 90 return 91 } 92 } 93 }) 94 if err != nil { 95 return "", err 96 } 97 return ptr, nil 98}