mirror of
https://github.com/open-policy-agent/opa.git
synced 2026-08-12 19:32:48 -06:00
ast: Add helper to convert ref to pointer string
In the past we've written a bunch of ad-hoc code to convert refs to slash-separated/JSON Pointer-esque strings. These just add a helper to the ref struct to convert to/from that kind of string. Signed-off-by: Torin Sandall <torinsandall@gmail.com>
This commit is contained in:
+37
@@ -8,6 +8,7 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
@@ -805,6 +806,27 @@ func EmptyRef() Ref {
|
||||
return Ref([]*Term{})
|
||||
}
|
||||
|
||||
// PtrRef returns a new reference against the head for the pointer
|
||||
// s. Path components in the pointer are unescaped.
|
||||
func PtrRef(head *Term, s string) (Ref, error) {
|
||||
s = strings.Trim(s, "/")
|
||||
if s == "" {
|
||||
return Ref{head}, nil
|
||||
}
|
||||
parts := strings.Split(s, "/")
|
||||
ref := make(Ref, len(parts)+1)
|
||||
ref[0] = head
|
||||
for i := 0; i < len(parts); i++ {
|
||||
var err error
|
||||
parts[i], err = url.PathUnescape(parts[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ref[i+1] = StringTerm(parts[i])
|
||||
}
|
||||
return ref, nil
|
||||
}
|
||||
|
||||
// RefTerm creates a new Term with a Ref value.
|
||||
func RefTerm(r ...*Term) *Term {
|
||||
return &Term{Value: Ref(r)}
|
||||
@@ -967,6 +989,21 @@ func (ref Ref) IsNested() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Ptr returns a slash-separated path string for this ref. If the ref
|
||||
// contains non-string terms this function returns an error. Path
|
||||
// components are escaped.
|
||||
func (ref Ref) Ptr() (string, error) {
|
||||
parts := make([]string, 0, len(ref)-1)
|
||||
for _, term := range ref[1:] {
|
||||
if str, ok := term.Value.(String); ok {
|
||||
parts = append(parts, url.PathEscape(string(str)))
|
||||
} else {
|
||||
return "", fmt.Errorf("invalid path value type")
|
||||
}
|
||||
}
|
||||
return strings.Join(parts, "/"), nil
|
||||
}
|
||||
|
||||
var varRegexp = regexp.MustCompile("^[[:alpha:]_][[:alpha:][:digit:]_]*$")
|
||||
|
||||
func (ref Ref) String() string {
|
||||
|
||||
@@ -419,6 +419,49 @@ func TestRefConcat(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRefPtr(t *testing.T) {
|
||||
cases := []string{
|
||||
"",
|
||||
"a",
|
||||
"a/b",
|
||||
"/a/b",
|
||||
"/a/b/",
|
||||
"a%2Fb",
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
ref, err := PtrRef(DefaultRootDocument.Copy(), tc)
|
||||
if err != nil {
|
||||
t.Fatal("Unexpected error:", err)
|
||||
}
|
||||
|
||||
ptr, err := ref.Ptr()
|
||||
if err != nil {
|
||||
t.Fatal("Unexpected error:", err)
|
||||
}
|
||||
|
||||
roundtrip, err := PtrRef(DefaultRootDocument.Copy(), ptr)
|
||||
if err != nil {
|
||||
t.Fatal("Unexpected error:", err)
|
||||
}
|
||||
|
||||
if !ref.Equal(roundtrip) {
|
||||
t.Fatalf("Expected roundtrip of %q to be equal but got %v and %v", tc, ref, roundtrip)
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := PtrRef(DefaultRootDocument.Copy(), "2%"); err == nil {
|
||||
t.Fatalf("Expected error from %q", "2%")
|
||||
}
|
||||
|
||||
ref := Ref{VarTerm("x"), IntNumberTerm(1)}
|
||||
|
||||
if _, err := ref.Ptr(); err == nil {
|
||||
t.Fatal("Expected error from x[1]")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestSetEqual(t *testing.T) {
|
||||
tests := []struct {
|
||||
a string
|
||||
|
||||
Reference in New Issue
Block a user