2020-05-30 19:06:35 +09:00
|
|
|
package yaml
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
|
|
|
|
"fmt"
|
|
|
|
|
"io"
|
2020-06-04 13:23:52 +09:00
|
|
|
"strconv"
|
2020-05-30 19:06:35 +09:00
|
|
|
|
|
|
|
|
"github.com/goccy/go-yaml/ast"
|
|
|
|
|
"github.com/goccy/go-yaml/internal/errors"
|
|
|
|
|
"github.com/goccy/go-yaml/parser"
|
2020-06-21 17:04:00 +09:00
|
|
|
"github.com/goccy/go-yaml/printer"
|
2020-05-30 19:06:35 +09:00
|
|
|
"golang.org/x/xerrors"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var (
|
2020-06-04 13:23:52 +09:00
|
|
|
ErrInvalidQuery = xerrors.New("invalid query")
|
|
|
|
|
ErrInvalidPath = xerrors.New("invalid path instance")
|
|
|
|
|
ErrInvalidPathString = xerrors.New("invalid path string")
|
2020-05-30 19:06:35 +09:00
|
|
|
)
|
|
|
|
|
|
2020-06-04 13:23:52 +09:00
|
|
|
// PathString create Path from string
|
|
|
|
|
//
|
|
|
|
|
// YAMLPath rule
|
|
|
|
|
// $ : the root object/element
|
|
|
|
|
// . : child operator
|
|
|
|
|
// .. : recursive descent
|
|
|
|
|
// [num] : object/element of array by number
|
2020-06-21 17:04:00 +09:00
|
|
|
// [*] : all objects/elements for array.
|
2020-06-04 13:23:52 +09:00
|
|
|
func PathString(s string) (*Path, error) {
|
|
|
|
|
buf := []rune(s)
|
|
|
|
|
length := len(buf)
|
|
|
|
|
cursor := 0
|
|
|
|
|
builder := &PathBuilder{}
|
|
|
|
|
for cursor < length {
|
|
|
|
|
c := buf[cursor]
|
|
|
|
|
switch c {
|
|
|
|
|
case '$':
|
|
|
|
|
builder = builder.Root()
|
|
|
|
|
cursor++
|
|
|
|
|
case '.':
|
|
|
|
|
b, c, err := parsePathDot(builder, buf, cursor)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.Wrapf(err, "failed to parse path of dot")
|
|
|
|
|
}
|
|
|
|
|
builder = b
|
|
|
|
|
cursor = c
|
|
|
|
|
case '[':
|
|
|
|
|
b, c, err := parsePathIndex(builder, buf, cursor)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.Wrapf(err, "failed to parse path of index")
|
|
|
|
|
}
|
|
|
|
|
builder = b
|
|
|
|
|
cursor = c
|
|
|
|
|
default:
|
|
|
|
|
return nil, errors.Wrapf(ErrInvalidPathString, "invalid path at %d", cursor)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return builder.Build(), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func parsePathRecursive(b *PathBuilder, buf []rune, cursor int) (*PathBuilder, int, error) {
|
|
|
|
|
length := len(buf)
|
|
|
|
|
cursor += 2 // skip .. characters
|
|
|
|
|
start := cursor
|
|
|
|
|
for ; cursor < length; cursor++ {
|
|
|
|
|
c := buf[cursor]
|
|
|
|
|
switch c {
|
|
|
|
|
case '$':
|
|
|
|
|
return nil, 0, errors.Wrapf(ErrInvalidPathString, "specified '$' after '..' character")
|
|
|
|
|
case '*':
|
|
|
|
|
return nil, 0, errors.Wrapf(ErrInvalidPathString, "specified '*' after '..' character")
|
|
|
|
|
case '.', '[':
|
|
|
|
|
goto end
|
|
|
|
|
case ']':
|
|
|
|
|
return nil, 0, errors.Wrapf(ErrInvalidPathString, "specified ']' after '..' character")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
end:
|
|
|
|
|
if start == cursor {
|
|
|
|
|
return nil, 0, errors.Wrapf(ErrInvalidPathString, "not found recursive selector")
|
|
|
|
|
}
|
|
|
|
|
return b.Recursive(string(buf[start:cursor])), cursor, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func parsePathDot(b *PathBuilder, buf []rune, cursor int) (*PathBuilder, int, error) {
|
|
|
|
|
length := len(buf)
|
|
|
|
|
if cursor+1 < length && buf[cursor+1] == '.' {
|
|
|
|
|
b, c, err := parsePathRecursive(b, buf, cursor)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, 0, errors.Wrapf(err, "failed to parse path of recursive")
|
|
|
|
|
}
|
|
|
|
|
return b, c, nil
|
|
|
|
|
}
|
|
|
|
|
cursor++ // skip . character
|
|
|
|
|
start := cursor
|
|
|
|
|
for ; cursor < length; cursor++ {
|
|
|
|
|
c := buf[cursor]
|
|
|
|
|
switch c {
|
|
|
|
|
case '$':
|
|
|
|
|
return nil, 0, errors.Wrapf(ErrInvalidPathString, "specified '$' after '.' character")
|
|
|
|
|
case '*':
|
|
|
|
|
return nil, 0, errors.Wrapf(ErrInvalidPathString, "specified '*' after '.' character")
|
|
|
|
|
case '.', '[':
|
|
|
|
|
goto end
|
|
|
|
|
case ']':
|
|
|
|
|
return nil, 0, errors.Wrapf(ErrInvalidPathString, "specified ']' after '.' character")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
end:
|
|
|
|
|
if start == cursor {
|
|
|
|
|
return nil, 0, errors.Wrapf(ErrInvalidPathString, "not found child selector")
|
|
|
|
|
}
|
|
|
|
|
return b.Child(string(buf[start:cursor])), cursor, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func parsePathIndex(b *PathBuilder, buf []rune, cursor int) (*PathBuilder, int, error) {
|
|
|
|
|
length := len(buf)
|
|
|
|
|
cursor++ // skip '[' character
|
|
|
|
|
if length <= cursor {
|
|
|
|
|
return nil, 0, errors.Wrapf(ErrInvalidPathString, "unexpected end of YAML Path")
|
|
|
|
|
}
|
|
|
|
|
c := buf[cursor]
|
|
|
|
|
switch c {
|
|
|
|
|
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '*':
|
|
|
|
|
start := cursor
|
|
|
|
|
cursor++
|
|
|
|
|
for ; cursor < length; cursor++ {
|
|
|
|
|
c := buf[cursor]
|
|
|
|
|
switch c {
|
|
|
|
|
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
if buf[cursor] != ']' {
|
|
|
|
|
return nil, 0, errors.Wrapf(ErrInvalidPathString, "invalid character %s at %d", string(buf[cursor]), cursor)
|
|
|
|
|
}
|
|
|
|
|
numOrAll := string(buf[start:cursor])
|
|
|
|
|
if numOrAll == "*" {
|
|
|
|
|
return b.IndexAll(), cursor + 1, nil
|
|
|
|
|
}
|
|
|
|
|
num, err := strconv.ParseInt(numOrAll, 10, 64)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, 0, errors.Wrapf(err, "failed to parse number")
|
|
|
|
|
}
|
|
|
|
|
return b.Index(uint(num)), cursor + 1, nil
|
|
|
|
|
}
|
|
|
|
|
return nil, 0, errors.Wrapf(ErrInvalidPathString, "invalid character %s at %d", c, cursor)
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-21 17:04:00 +09:00
|
|
|
// Path represent YAMLPath ( like a JSONPath ).
|
2020-05-30 19:06:35 +09:00
|
|
|
type Path struct {
|
|
|
|
|
node pathNode
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-21 17:04:00 +09:00
|
|
|
// String path to text.
|
2020-05-30 19:06:35 +09:00
|
|
|
func (p *Path) String() string {
|
|
|
|
|
return p.node.String()
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-21 17:04:00 +09:00
|
|
|
// Read decode from r and set extracted value by YAMLPath to v.
|
2020-05-30 19:06:35 +09:00
|
|
|
func (p *Path) Read(r io.Reader, v interface{}) error {
|
|
|
|
|
node, err := p.ReadNode(r)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return errors.Wrapf(err, "failed to read node")
|
|
|
|
|
}
|
|
|
|
|
if err := Unmarshal([]byte(node.String()), v); err != nil {
|
|
|
|
|
return errors.Wrapf(err, "failed to unmarshal")
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-21 17:04:00 +09:00
|
|
|
// ReadNode create AST from r and extract node by YAMLPath.
|
2020-05-30 19:06:35 +09:00
|
|
|
func (p *Path) ReadNode(r io.Reader) (ast.Node, error) {
|
|
|
|
|
if p.node == nil {
|
|
|
|
|
return nil, ErrInvalidPath
|
|
|
|
|
}
|
|
|
|
|
var buf bytes.Buffer
|
|
|
|
|
if _, err := io.Copy(&buf, r); err != nil {
|
|
|
|
|
return nil, errors.Wrapf(err, "failed to copy from reader")
|
|
|
|
|
}
|
|
|
|
|
f, err := parser.ParseBytes(buf.Bytes(), 0)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.Wrapf(err, "failed to parse yaml")
|
|
|
|
|
}
|
2020-06-04 13:23:52 +09:00
|
|
|
node, err := p.FilterFile(f)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.Wrapf(err, "failed to filter from ast.File")
|
|
|
|
|
}
|
|
|
|
|
return node, nil
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-21 17:04:00 +09:00
|
|
|
// Filter filter from target by YAMLPath and set it to v.
|
2020-06-04 13:23:52 +09:00
|
|
|
func (p *Path) Filter(target, v interface{}) error {
|
|
|
|
|
b, err := Marshal(target)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return errors.Wrapf(err, "failed to marshal target value")
|
|
|
|
|
}
|
|
|
|
|
if err := p.Read(bytes.NewBuffer(b), v); err != nil {
|
|
|
|
|
return errors.Wrapf(err, "failed to read")
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-21 17:04:00 +09:00
|
|
|
// FilterFile filter from ast.File by YAMLPath.
|
2020-06-04 13:23:52 +09:00
|
|
|
func (p *Path) FilterFile(f *ast.File) (ast.Node, error) {
|
2020-05-30 19:06:35 +09:00
|
|
|
for _, doc := range f.Docs {
|
2020-06-04 13:23:52 +09:00
|
|
|
node, err := p.FilterNode(doc.Body)
|
2020-05-30 19:06:35 +09:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.Wrapf(err, "failed to filter node by path ( %s )", p.node)
|
|
|
|
|
}
|
|
|
|
|
if node != nil {
|
|
|
|
|
return node, nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil, nil
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-21 17:04:00 +09:00
|
|
|
// FilterNode filter from node by YAMLPath.
|
2020-06-04 13:23:52 +09:00
|
|
|
func (p *Path) FilterNode(node ast.Node) (ast.Node, error) {
|
|
|
|
|
n, err := p.node.filter(node)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.Wrapf(err, "failed to filter node by path ( %s )", p.node)
|
|
|
|
|
}
|
|
|
|
|
return n, nil
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-22 21:54:23 +09:00
|
|
|
// AnnotateSource add annotation to passed source ( see section 5.1 in README.md ).
|
|
|
|
|
func (p *Path) AnnotateSource(source []byte, colored bool) ([]byte, error) {
|
2020-06-21 17:04:00 +09:00
|
|
|
file, err := parser.ParseBytes([]byte(source), 0)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
node, err := p.FilterFile(file)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
var pp printer.Printer
|
|
|
|
|
return []byte(pp.PrintErrorToken(node.GetToken(), colored)), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// PathBuilder represent builder for YAMLPath.
|
2020-06-04 13:23:52 +09:00
|
|
|
type PathBuilder struct {
|
|
|
|
|
root *rootNode
|
|
|
|
|
node pathNode
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-21 17:04:00 +09:00
|
|
|
// Root add '$' to current path.
|
2020-06-04 13:23:52 +09:00
|
|
|
func (b *PathBuilder) Root() *PathBuilder {
|
|
|
|
|
root := newRootNode()
|
|
|
|
|
return &PathBuilder{root: root, node: root}
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-21 17:04:00 +09:00
|
|
|
// IndexAll add '[*]' to current path.
|
2020-06-04 13:23:52 +09:00
|
|
|
func (b *PathBuilder) IndexAll() *PathBuilder {
|
|
|
|
|
b.node = b.node.chain(newIndexAllNode())
|
|
|
|
|
return b
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-21 17:04:00 +09:00
|
|
|
// Recursive add '..selector' to current path.
|
2020-06-04 13:23:52 +09:00
|
|
|
func (b *PathBuilder) Recursive(selector string) *PathBuilder {
|
|
|
|
|
b.node = b.node.chain(newRecursiveNode(selector))
|
|
|
|
|
return b
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-21 17:04:00 +09:00
|
|
|
// Child add '.name' to current path.
|
2020-06-04 13:23:52 +09:00
|
|
|
func (b *PathBuilder) Child(name string) *PathBuilder {
|
|
|
|
|
b.node = b.node.chain(newSelectorNode(name))
|
|
|
|
|
return b
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-21 17:04:00 +09:00
|
|
|
// Index add '[idx]' to current path.
|
2020-06-04 13:23:52 +09:00
|
|
|
func (b *PathBuilder) Index(idx uint) *PathBuilder {
|
|
|
|
|
b.node = b.node.chain(newIndexNode(idx))
|
|
|
|
|
return b
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-21 17:04:00 +09:00
|
|
|
// Build build YAMLPath.
|
2020-06-04 13:23:52 +09:00
|
|
|
func (b *PathBuilder) Build() *Path {
|
|
|
|
|
return &Path{node: b.root}
|
2020-05-30 19:06:35 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type pathNode interface {
|
|
|
|
|
fmt.Stringer
|
2020-06-04 13:23:52 +09:00
|
|
|
chain(pathNode) pathNode
|
|
|
|
|
filter(ast.Node) (ast.Node, error)
|
2020-05-30 19:06:35 +09:00
|
|
|
}
|
|
|
|
|
|
2020-06-04 13:23:52 +09:00
|
|
|
type basePathNode struct {
|
2020-05-30 19:06:35 +09:00
|
|
|
child pathNode
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-04 13:23:52 +09:00
|
|
|
func (n *basePathNode) chain(node pathNode) pathNode {
|
|
|
|
|
n.child = node
|
|
|
|
|
return node
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type rootNode struct {
|
|
|
|
|
*basePathNode
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func newRootNode() *rootNode {
|
|
|
|
|
return &rootNode{basePathNode: &basePathNode{}}
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-30 19:06:35 +09:00
|
|
|
func (n *rootNode) String() string {
|
|
|
|
|
s := "$"
|
|
|
|
|
if n.child != nil {
|
|
|
|
|
s += n.child.String()
|
|
|
|
|
}
|
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-04 13:23:52 +09:00
|
|
|
func (n *rootNode) filter(node ast.Node) (ast.Node, error) {
|
2020-05-30 19:06:35 +09:00
|
|
|
if n.child == nil {
|
|
|
|
|
return nil, nil
|
|
|
|
|
}
|
2020-06-04 13:23:52 +09:00
|
|
|
filtered, err := n.child.filter(node)
|
2020-05-30 19:06:35 +09:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.Wrapf(err, "failed to filter")
|
|
|
|
|
}
|
|
|
|
|
return filtered, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type selectorNode struct {
|
2020-06-04 13:23:52 +09:00
|
|
|
*basePathNode
|
2020-05-30 19:06:35 +09:00
|
|
|
selector string
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-04 13:23:52 +09:00
|
|
|
func newSelectorNode(selector string) *selectorNode {
|
|
|
|
|
return &selectorNode{
|
|
|
|
|
basePathNode: &basePathNode{},
|
|
|
|
|
selector: selector,
|
|
|
|
|
}
|
2020-05-30 19:06:35 +09:00
|
|
|
}
|
|
|
|
|
|
2020-06-04 13:23:52 +09:00
|
|
|
func (n *selectorNode) filter(node ast.Node) (ast.Node, error) {
|
2020-05-30 19:06:35 +09:00
|
|
|
switch node.Type() {
|
|
|
|
|
case ast.MappingType:
|
|
|
|
|
for _, value := range node.(*ast.MappingNode).Values {
|
|
|
|
|
key := value.Key.GetToken().Value
|
|
|
|
|
if key == n.selector {
|
|
|
|
|
if n.child == nil {
|
|
|
|
|
return value.Value, nil
|
|
|
|
|
}
|
2020-06-04 13:23:52 +09:00
|
|
|
filtered, err := n.child.filter(value.Value)
|
2020-05-30 19:06:35 +09:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.Wrapf(err, "failed to filter")
|
|
|
|
|
}
|
|
|
|
|
return filtered, nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
case ast.MappingValueType:
|
|
|
|
|
value := node.(*ast.MappingValueNode)
|
|
|
|
|
key := value.Key.GetToken().Value
|
|
|
|
|
if key == n.selector {
|
|
|
|
|
if n.child == nil {
|
|
|
|
|
return value.Value, nil
|
|
|
|
|
}
|
2020-06-04 13:23:52 +09:00
|
|
|
filtered, err := n.child.filter(value.Value)
|
2020-05-30 19:06:35 +09:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.Wrapf(err, "failed to filter")
|
|
|
|
|
}
|
|
|
|
|
return filtered, nil
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
return nil, errors.Wrapf(ErrInvalidQuery, "expected node type is map or map value. but got %s", node.Type())
|
|
|
|
|
}
|
|
|
|
|
return nil, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (n *selectorNode) String() string {
|
|
|
|
|
s := fmt.Sprintf(".%s", n.selector)
|
|
|
|
|
if n.child != nil {
|
|
|
|
|
s += n.child.String()
|
|
|
|
|
}
|
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type indexNode struct {
|
2020-06-04 13:23:52 +09:00
|
|
|
*basePathNode
|
2020-05-30 19:06:35 +09:00
|
|
|
selector uint
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-04 13:23:52 +09:00
|
|
|
func newIndexNode(selector uint) *indexNode {
|
|
|
|
|
return &indexNode{
|
|
|
|
|
basePathNode: &basePathNode{},
|
|
|
|
|
selector: selector,
|
|
|
|
|
}
|
2020-05-30 19:06:35 +09:00
|
|
|
}
|
|
|
|
|
|
2020-06-04 13:23:52 +09:00
|
|
|
func (n *indexNode) filter(node ast.Node) (ast.Node, error) {
|
2020-05-30 19:06:35 +09:00
|
|
|
if node.Type() != ast.SequenceType {
|
|
|
|
|
return nil, errors.Wrapf(ErrInvalidQuery, "expected sequence type node. but got %s", node.Type())
|
|
|
|
|
}
|
|
|
|
|
sequence := node.(*ast.SequenceNode)
|
|
|
|
|
if n.selector >= uint(len(sequence.Values)) {
|
|
|
|
|
return nil, errors.Wrapf(ErrInvalidQuery, "expected index is %d. but got sequences has %d items", n.selector, sequence.Values)
|
|
|
|
|
}
|
|
|
|
|
value := sequence.Values[n.selector]
|
|
|
|
|
if n.child == nil {
|
|
|
|
|
return value, nil
|
|
|
|
|
}
|
2020-06-04 13:23:52 +09:00
|
|
|
filtered, err := n.child.filter(value)
|
2020-05-30 19:06:35 +09:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.Wrapf(err, "failed to filter")
|
|
|
|
|
}
|
|
|
|
|
return filtered, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (n *indexNode) String() string {
|
|
|
|
|
s := fmt.Sprintf("[%d]", n.selector)
|
|
|
|
|
if n.child != nil {
|
|
|
|
|
s += n.child.String()
|
|
|
|
|
}
|
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-04 13:23:52 +09:00
|
|
|
type indexAllNode struct {
|
|
|
|
|
*basePathNode
|
2020-05-30 19:06:35 +09:00
|
|
|
}
|
|
|
|
|
|
2020-06-04 13:23:52 +09:00
|
|
|
func newIndexAllNode() *indexAllNode {
|
|
|
|
|
return &indexAllNode{
|
|
|
|
|
basePathNode: &basePathNode{},
|
|
|
|
|
}
|
2020-05-30 19:06:35 +09:00
|
|
|
}
|
|
|
|
|
|
2020-06-04 13:23:52 +09:00
|
|
|
func (n *indexAllNode) String() string {
|
|
|
|
|
s := "[*]"
|
|
|
|
|
if n.child != nil {
|
|
|
|
|
s += n.child.String()
|
|
|
|
|
}
|
|
|
|
|
return s
|
2020-05-30 19:06:35 +09:00
|
|
|
}
|
|
|
|
|
|
2020-06-04 13:23:52 +09:00
|
|
|
func (n *indexAllNode) filter(node ast.Node) (ast.Node, error) {
|
|
|
|
|
if node.Type() != ast.SequenceType {
|
|
|
|
|
return nil, errors.Wrapf(ErrInvalidQuery, "expected sequence type node. but got %s", node.Type())
|
|
|
|
|
}
|
|
|
|
|
sequence := node.(*ast.SequenceNode)
|
|
|
|
|
if n.child == nil {
|
|
|
|
|
return sequence, nil
|
|
|
|
|
}
|
|
|
|
|
out := *sequence
|
|
|
|
|
out.Values = []ast.Node{}
|
|
|
|
|
for _, value := range sequence.Values {
|
|
|
|
|
filtered, err := n.child.filter(value)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.Wrapf(err, "failed to filter")
|
|
|
|
|
}
|
|
|
|
|
out.Values = append(out.Values, filtered)
|
|
|
|
|
}
|
|
|
|
|
return &out, nil
|
2020-05-30 19:06:35 +09:00
|
|
|
}
|
|
|
|
|
|
2020-06-04 13:23:52 +09:00
|
|
|
type recursiveNode struct {
|
|
|
|
|
*basePathNode
|
|
|
|
|
selector string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func newRecursiveNode(selector string) *recursiveNode {
|
|
|
|
|
return &recursiveNode{
|
|
|
|
|
basePathNode: &basePathNode{},
|
|
|
|
|
selector: selector,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (n *recursiveNode) String() string {
|
|
|
|
|
s := fmt.Sprintf("..%s", n.selector)
|
|
|
|
|
if n.child != nil {
|
|
|
|
|
s += n.child.String()
|
|
|
|
|
}
|
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (n *recursiveNode) filterNode(node ast.Node) (*ast.SequenceNode, error) {
|
|
|
|
|
sequence := &ast.SequenceNode{}
|
|
|
|
|
switch typedNode := node.(type) {
|
|
|
|
|
case *ast.MappingNode:
|
|
|
|
|
for _, value := range typedNode.Values {
|
|
|
|
|
seq, err := n.filterNode(value)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.Wrapf(err, "failed to filter")
|
|
|
|
|
}
|
|
|
|
|
sequence.Values = append(sequence.Values, seq.Values...)
|
|
|
|
|
}
|
|
|
|
|
case *ast.MappingValueNode:
|
|
|
|
|
key := typedNode.Key.GetToken().Value
|
|
|
|
|
if n.selector == key {
|
|
|
|
|
sequence.Values = append(sequence.Values, typedNode.Value)
|
|
|
|
|
}
|
|
|
|
|
seq, err := n.filterNode(typedNode.Value)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.Wrapf(err, "failed to filter")
|
|
|
|
|
}
|
|
|
|
|
sequence.Values = append(sequence.Values, seq.Values...)
|
|
|
|
|
case *ast.SequenceNode:
|
|
|
|
|
for _, value := range typedNode.Values {
|
|
|
|
|
seq, err := n.filterNode(value)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.Wrapf(err, "failed to filter")
|
|
|
|
|
}
|
|
|
|
|
sequence.Values = append(sequence.Values, seq.Values...)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return sequence, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (n *recursiveNode) filter(node ast.Node) (ast.Node, error) {
|
|
|
|
|
sequence, err := n.filterNode(node)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.Wrapf(err, "failed to filter")
|
|
|
|
|
}
|
|
|
|
|
sequence.Start = node.GetToken()
|
|
|
|
|
return sequence, nil
|
2020-05-30 19:06:35 +09:00
|
|
|
}
|