selector.go 3.99 KB
Newer Older
1 2
package selector

Eric Myhre's avatar
Eric Myhre committed
3 4 5
import (
	"fmt"

tavit ohanian's avatar
tavit ohanian committed
6
	ld "gitlab.dms3.io/ld/go-ld-prime"
Eric Myhre's avatar
Eric Myhre committed
7
)
8

9 10
// Selector is the programmatic representation of an LD Selector Node
// and can be applied to traverse a given LD DAG
11
type Selector interface {
tavit ohanian's avatar
tavit ohanian committed
12 13 14
	Interests() []ld.PathSegment              // returns the segments we're likely interested in **or nil** if we're a high-cardinality or expression based matcher and need all segments proposed to us.
	Explore(ld.Node, ld.PathSegment) Selector // explore one step -- iteration comes from outside (either whole node, or by following suggestions of Interests).  returns nil if no interest.  you have to traverse to the next node yourself (the selector doesn't do it for you because you might be considering multiple selection reasons at the same time).
	Decide(ld.Node) bool
15
}
16

17 18 19
// ParsedParent is created whenever you are parsing a selector node that may have
// child selectors nodes that need to know it
type ParsedParent interface {
20 21 22
	Link(s Selector) bool
}

23 24 25 26 27
// ParseContext tracks the progress when parsing a selector
type ParseContext struct {
	parentStack []ParsedParent
}

28
// ParseSelector creates a Selector that can be traversed from an LD Selector node
tavit ohanian's avatar
tavit ohanian committed
29
func ParseSelector(n ld.Node) (Selector, error) {
30 31 32
	return ParseContext{}.ParseSelector(n)
}

33
// ParseSelector creates a Selector from an LD Selector Node with the given context
tavit ohanian's avatar
tavit ohanian committed
34 35
func (pc ParseContext) ParseSelector(n ld.Node) (Selector, error) {
	if n.Kind() != ld.Kind_Map {
Eric Myhre's avatar
Eric Myhre committed
36 37 38 39 40 41 42 43 44 45
		return nil, fmt.Errorf("selector spec parse rejected: selector is a keyed union and thus must be a map")
	}
	if n.Length() != 1 {
		return nil, fmt.Errorf("selector spec parse rejected: selector is a keyed union and thus must be single-entry map")
	}
	kn, v, _ := n.MapIterator().Next()
	kstr, _ := kn.AsString()
	// Switch over the single key to determine which selector body comes next.
	//  (This switch is where the keyed union discriminators concretely happen.)
	switch kstr {
46
	case SelectorKey_ExploreFields:
47
		return pc.ParseExploreFields(v)
48
	case SelectorKey_ExploreAll:
49
		return pc.ParseExploreAll(v)
50
	case SelectorKey_ExploreIndex:
51
		return pc.ParseExploreIndex(v)
52
	case SelectorKey_ExploreRange:
53
		return pc.ParseExploreRange(v)
54
	case SelectorKey_ExploreUnion:
55
		return pc.ParseExploreUnion(v)
56
	case SelectorKey_ExploreRecursive:
57
		return pc.ParseExploreRecursive(v)
58
	case SelectorKey_ExploreRecursiveEdge:
59
		return pc.ParseExploreRecursiveEdge(v)
60
	case SelectorKey_Matcher:
61
		return pc.ParseMatcher(v)
Eric Myhre's avatar
Eric Myhre committed
62 63 64 65 66
	default:
		return nil, fmt.Errorf("selector spec parse rejected: %q is not a known member of the selector union", kstr)
	}
}

67 68 69 70 71 72 73 74 75
// PushParent puts a parent onto the stack of parents for a parse context
func (pc ParseContext) PushParent(parent ParsedParent) ParseContext {
	l := len(pc.parentStack)
	parents := make([]ParsedParent, 0, l+1)
	parents = append(parents, parent)
	parents = append(parents, pc.parentStack...)
	return ParseContext{parents}
}

76 77 78
// SegmentIterator iterates either a list or a map, generating PathSegments
// instead of indexes or keys
type SegmentIterator interface {
tavit ohanian's avatar
tavit ohanian committed
79
	Next() (pathSegment ld.PathSegment, value ld.Node, err error)
80 81 82 83
	Done() bool
}

// NewSegmentIterator generates a new iterator based on the node type
tavit ohanian's avatar
tavit ohanian committed
84 85
func NewSegmentIterator(n ld.Node) SegmentIterator {
	if n.Kind() == ld.Kind_List {
86 87 88 89 90 91
		return listSegmentIterator{n.ListIterator()}
	}
	return mapSegmentIterator{n.MapIterator()}
}

type listSegmentIterator struct {
tavit ohanian's avatar
tavit ohanian committed
92
	ld.ListIterator
93 94
}

tavit ohanian's avatar
tavit ohanian committed
95
func (lsi listSegmentIterator) Next() (pathSegment ld.PathSegment, value ld.Node, err error) {
96
	i, v, err := lsi.ListIterator.Next()
tavit ohanian's avatar
tavit ohanian committed
97
	return ld.PathSegmentOfInt(i), v, err
98 99 100 101 102 103 104
}

func (lsi listSegmentIterator) Done() bool {
	return lsi.ListIterator.Done()
}

type mapSegmentIterator struct {
tavit ohanian's avatar
tavit ohanian committed
105
	ld.MapIterator
106 107
}

tavit ohanian's avatar
tavit ohanian committed
108
func (msi mapSegmentIterator) Next() (pathSegment ld.PathSegment, value ld.Node, err error) {
109 110
	k, v, err := msi.MapIterator.Next()
	if err != nil {
tavit ohanian's avatar
tavit ohanian committed
111
		return ld.PathSegment{}, v, err
112 113
	}
	kstr, _ := k.AsString()
tavit ohanian's avatar
tavit ohanian committed
114
	return ld.PathSegmentOfString(kstr), v, err
115 116 117 118 119
}

func (msi mapSegmentIterator) Done() bool {
	return msi.MapIterator.Done()
}