| 
									
										
										
										
											2020-05-30 19:06:35 +09:00
										 |  |  | package yaml | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2020-06-04 13:23:52 +09:00
										 |  |  | 	"strconv" | 
					
						
							| 
									
										
										
										
											2021-10-12 17:03:58 +09:00
										 |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											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
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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. | 
					
						
							| 
									
										
										
										
											2021-09-02 21:48:09 +09:00
										 |  |  | // | 
					
						
							|  |  |  | // If you want to use reserved characters such as `.` and `*` as a key name, | 
					
						
							|  |  |  | // enclose them in single quotation as follows ( $.foo.'bar.baz-*'.hoge ). | 
					
						
							|  |  |  | // If you want to use a single quote with reserved characters, escape it with `\` ( $.foo.'bar.baz\'s value'.hoge ). | 
					
						
							| 
									
										
										
										
											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 '.': | 
					
						
							| 
									
										
										
										
											2021-09-02 21:48:09 +09:00
										 |  |  | 			b, buf, c, err := parsePathDot(builder, buf, cursor) | 
					
						
							| 
									
										
										
										
											2020-06-04 13:23:52 +09:00
										 |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return nil, errors.Wrapf(err, "failed to parse path of dot") | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-09-02 21:48:09 +09:00
										 |  |  | 			length = len(buf) | 
					
						
							| 
									
										
										
										
											2020-06-04 13:23:52 +09:00
										 |  |  | 			builder = b | 
					
						
							|  |  |  | 			cursor = c | 
					
						
							|  |  |  | 		case '[': | 
					
						
							| 
									
										
										
										
											2021-09-02 21:48:09 +09:00
										 |  |  | 			b, buf, c, err := parsePathIndex(builder, buf, cursor) | 
					
						
							| 
									
										
										
										
											2020-06-04 13:23:52 +09:00
										 |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return nil, errors.Wrapf(err, "failed to parse path of index") | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-09-02 21:48:09 +09:00
										 |  |  | 			length = len(buf) | 
					
						
							| 
									
										
										
										
											2020-06-04 13:23:52 +09:00
										 |  |  | 			builder = b | 
					
						
							|  |  |  | 			cursor = c | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			return nil, errors.Wrapf(ErrInvalidPathString, "invalid path at %d", cursor) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return builder.Build(), nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-02 21:48:09 +09:00
										 |  |  | func parsePathRecursive(b *PathBuilder, buf []rune, cursor int) (*PathBuilder, []rune, int, error) { | 
					
						
							| 
									
										
										
										
											2020-06-04 13:23:52 +09:00
										 |  |  | 	length := len(buf) | 
					
						
							|  |  |  | 	cursor += 2 // skip .. characters | 
					
						
							|  |  |  | 	start := cursor | 
					
						
							|  |  |  | 	for ; cursor < length; cursor++ { | 
					
						
							|  |  |  | 		c := buf[cursor] | 
					
						
							|  |  |  | 		switch c { | 
					
						
							|  |  |  | 		case '$': | 
					
						
							| 
									
										
										
										
											2021-09-02 21:48:09 +09:00
										 |  |  | 			return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified '$' after '..' character") | 
					
						
							| 
									
										
										
										
											2020-06-04 13:23:52 +09:00
										 |  |  | 		case '*': | 
					
						
							| 
									
										
										
										
											2021-09-02 21:48:09 +09:00
										 |  |  | 			return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified '*' after '..' character") | 
					
						
							| 
									
										
										
										
											2020-06-04 13:23:52 +09:00
										 |  |  | 		case '.', '[': | 
					
						
							|  |  |  | 			goto end | 
					
						
							|  |  |  | 		case ']': | 
					
						
							| 
									
										
										
										
											2021-09-02 21:48:09 +09:00
										 |  |  | 			return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified ']' after '..' character") | 
					
						
							| 
									
										
										
										
											2020-06-04 13:23:52 +09:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | end: | 
					
						
							|  |  |  | 	if start == cursor { | 
					
						
							| 
									
										
										
										
											2021-09-02 21:48:09 +09:00
										 |  |  | 		return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "not found recursive selector") | 
					
						
							| 
									
										
										
										
											2020-06-04 13:23:52 +09:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-09-02 21:48:09 +09:00
										 |  |  | 	return b.Recursive(string(buf[start:cursor])), buf, cursor, nil | 
					
						
							| 
									
										
										
										
											2020-06-04 13:23:52 +09:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-02 21:48:09 +09:00
										 |  |  | func parsePathDot(b *PathBuilder, buf []rune, cursor int) (*PathBuilder, []rune, int, error) { | 
					
						
							| 
									
										
										
										
											2020-06-04 13:23:52 +09:00
										 |  |  | 	length := len(buf) | 
					
						
							|  |  |  | 	if cursor+1 < length && buf[cursor+1] == '.' { | 
					
						
							| 
									
										
										
										
											2021-09-02 21:48:09 +09:00
										 |  |  | 		b, buf, c, err := parsePathRecursive(b, buf, cursor) | 
					
						
							| 
									
										
										
										
											2020-06-04 13:23:52 +09:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2021-09-02 21:48:09 +09:00
										 |  |  | 			return nil, nil, 0, errors.Wrapf(err, "failed to parse path of recursive") | 
					
						
							| 
									
										
										
										
											2020-06-04 13:23:52 +09:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-09-02 21:48:09 +09:00
										 |  |  | 		return b, buf, c, nil | 
					
						
							| 
									
										
										
										
											2020-06-04 13:23:52 +09:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	cursor++ // skip . character | 
					
						
							|  |  |  | 	start := cursor | 
					
						
							| 
									
										
										
										
											2021-09-02 21:48:09 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// if started single quote, looking for end single quote char | 
					
						
							|  |  |  | 	if cursor < length && buf[cursor] == '\'' { | 
					
						
							|  |  |  | 		return parseQuotedKey(b, buf, cursor) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-06-04 13:23:52 +09:00
										 |  |  | 	for ; cursor < length; cursor++ { | 
					
						
							|  |  |  | 		c := buf[cursor] | 
					
						
							|  |  |  | 		switch c { | 
					
						
							|  |  |  | 		case '$': | 
					
						
							| 
									
										
										
										
											2021-09-02 21:48:09 +09:00
										 |  |  | 			return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified '$' after '.' character") | 
					
						
							| 
									
										
										
										
											2020-06-04 13:23:52 +09:00
										 |  |  | 		case '*': | 
					
						
							| 
									
										
										
										
											2021-09-02 21:48:09 +09:00
										 |  |  | 			return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified '*' after '.' character") | 
					
						
							| 
									
										
										
										
											2020-06-04 13:23:52 +09:00
										 |  |  | 		case '.', '[': | 
					
						
							|  |  |  | 			goto end | 
					
						
							|  |  |  | 		case ']': | 
					
						
							| 
									
										
										
										
											2021-09-02 21:48:09 +09:00
										 |  |  | 			return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified ']' after '.' character") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | end: | 
					
						
							|  |  |  | 	if start == cursor { | 
					
						
							|  |  |  | 		return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "cloud not find by empty key") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return b.Child(string(buf[start:cursor])), buf, cursor, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func parseQuotedKey(b *PathBuilder, buf []rune, cursor int) (*PathBuilder, []rune, int, error) { | 
					
						
							|  |  |  | 	cursor++ // skip single quote | 
					
						
							|  |  |  | 	start := cursor | 
					
						
							|  |  |  | 	length := len(buf) | 
					
						
							|  |  |  | 	var foundEndDelim bool | 
					
						
							|  |  |  | 	for ; cursor < length; cursor++ { | 
					
						
							|  |  |  | 		switch buf[cursor] { | 
					
						
							|  |  |  | 		case '\\': | 
					
						
							|  |  |  | 			buf = append(append([]rune{}, buf[:cursor]...), buf[cursor+1:]...) | 
					
						
							|  |  |  | 			length = len(buf) | 
					
						
							|  |  |  | 		case '\'': | 
					
						
							|  |  |  | 			foundEndDelim = true | 
					
						
							|  |  |  | 			goto end | 
					
						
							| 
									
										
										
										
											2020-06-04 13:23:52 +09:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | end: | 
					
						
							| 
									
										
										
										
											2021-09-02 21:48:09 +09:00
										 |  |  | 	if !foundEndDelim { | 
					
						
							|  |  |  | 		return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "could not find end delimiter for key") | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-06-04 13:23:52 +09:00
										 |  |  | 	if start == cursor { | 
					
						
							| 
									
										
										
										
											2021-09-02 21:48:09 +09:00
										 |  |  | 		return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "could not find by empty key") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	selector := buf[start:cursor] | 
					
						
							|  |  |  | 	cursor++ | 
					
						
							|  |  |  | 	if cursor < length { | 
					
						
							|  |  |  | 		switch buf[cursor] { | 
					
						
							|  |  |  | 		case '$': | 
					
						
							|  |  |  | 			return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified '$' after '.' character") | 
					
						
							|  |  |  | 		case '*': | 
					
						
							|  |  |  | 			return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified '*' after '.' character") | 
					
						
							|  |  |  | 		case ']': | 
					
						
							|  |  |  | 			return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified ']' after '.' character") | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-06-04 13:23:52 +09:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-09-02 21:48:09 +09:00
										 |  |  | 	return b.Child(string(selector)), buf, cursor, nil | 
					
						
							| 
									
										
										
										
											2020-06-04 13:23:52 +09:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-02 21:48:09 +09:00
										 |  |  | func parsePathIndex(b *PathBuilder, buf []rune, cursor int) (*PathBuilder, []rune, int, error) { | 
					
						
							| 
									
										
										
										
											2020-06-04 13:23:52 +09:00
										 |  |  | 	length := len(buf) | 
					
						
							|  |  |  | 	cursor++ // skip '[' character | 
					
						
							|  |  |  | 	if length <= cursor { | 
					
						
							| 
									
										
										
										
											2021-09-02 21:48:09 +09:00
										 |  |  | 		return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "unexpected end of YAML Path") | 
					
						
							| 
									
										
										
										
											2020-06-04 13:23:52 +09:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	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] != ']' { | 
					
						
							| 
									
										
										
										
											2021-09-02 21:48:09 +09:00
										 |  |  | 			return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "invalid character %s at %d", string(buf[cursor]), cursor) | 
					
						
							| 
									
										
										
										
											2020-06-04 13:23:52 +09:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		numOrAll := string(buf[start:cursor]) | 
					
						
							|  |  |  | 		if numOrAll == "*" { | 
					
						
							| 
									
										
										
										
											2021-09-02 21:48:09 +09:00
										 |  |  | 			return b.IndexAll(), buf, cursor + 1, nil | 
					
						
							| 
									
										
										
										
											2020-06-04 13:23:52 +09:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		num, err := strconv.ParseInt(numOrAll, 10, 64) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2021-09-02 21:48:09 +09:00
										 |  |  | 			return nil, nil, 0, errors.Wrapf(err, "failed to parse number") | 
					
						
							| 
									
										
										
										
											2020-06-04 13:23:52 +09:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-09-02 21:48:09 +09:00
										 |  |  | 		return b.Index(uint(num)), buf, cursor + 1, nil | 
					
						
							| 
									
										
										
										
											2020-06-04 13:23:52 +09:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-09-02 21:48:09 +09:00
										 |  |  | 	return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "invalid character %s at %d", c, cursor) | 
					
						
							| 
									
										
										
										
											2020-06-04 13:23:52 +09:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-02-02 13:58:23 +09:00
										 |  |  | 	return nil, errors.Wrapf(ErrNotFoundNode, "failed to find path ( %s )", p.node) | 
					
						
							| 
									
										
										
										
											2020-05-30 19:06:35 +09:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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-07-16 14:18:26 +09:00
										 |  |  | // MergeFromReader merge YAML text into ast.File. | 
					
						
							|  |  |  | func (p *Path) MergeFromReader(dst *ast.File, src io.Reader) error { | 
					
						
							|  |  |  | 	var buf bytes.Buffer | 
					
						
							|  |  |  | 	if _, err := io.Copy(&buf, src); err != nil { | 
					
						
							|  |  |  | 		return errors.Wrapf(err, "failed to copy from reader") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	file, err := parser.ParseBytes(buf.Bytes(), 0) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return errors.Wrapf(err, "failed to parse") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err := p.MergeFromFile(dst, file); err != nil { | 
					
						
							|  |  |  | 		return errors.Wrapf(err, "failed to merge file") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // MergeFromFile merge ast.File into ast.File. | 
					
						
							|  |  |  | func (p *Path) MergeFromFile(dst *ast.File, src *ast.File) error { | 
					
						
							|  |  |  | 	base, err := p.FilterFile(dst) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return errors.Wrapf(err, "failed to filter file") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for _, doc := range src.Docs { | 
					
						
							|  |  |  | 		if err := ast.Merge(base, doc); err != nil { | 
					
						
							|  |  |  | 			return errors.Wrapf(err, "failed to merge") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // MergeFromNode merge ast.Node into ast.File. | 
					
						
							|  |  |  | func (p *Path) MergeFromNode(dst *ast.File, src ast.Node) error { | 
					
						
							|  |  |  | 	base, err := p.FilterFile(dst) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return errors.Wrapf(err, "failed to filter file") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err := ast.Merge(base, src); err != nil { | 
					
						
							|  |  |  | 		return errors.Wrapf(err, "failed to merge") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ReplaceWithReader replace ast.File with io.Reader. | 
					
						
							|  |  |  | func (p *Path) ReplaceWithReader(dst *ast.File, src io.Reader) error { | 
					
						
							|  |  |  | 	var buf bytes.Buffer | 
					
						
							|  |  |  | 	if _, err := io.Copy(&buf, src); err != nil { | 
					
						
							|  |  |  | 		return errors.Wrapf(err, "failed to copy from reader") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	file, err := parser.ParseBytes(buf.Bytes(), 0) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return errors.Wrapf(err, "failed to parse") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err := p.ReplaceWithFile(dst, file); err != nil { | 
					
						
							|  |  |  | 		return errors.Wrapf(err, "failed to replace file") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ReplaceWithFile replace ast.File with ast.File. | 
					
						
							|  |  |  | func (p *Path) ReplaceWithFile(dst *ast.File, src *ast.File) error { | 
					
						
							|  |  |  | 	for _, doc := range src.Docs { | 
					
						
							|  |  |  | 		if err := p.ReplaceWithNode(dst, doc); err != nil { | 
					
						
							|  |  |  | 			return errors.Wrapf(err, "failed to replace file by path ( %s )", p.node) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ReplaceNode replace ast.File with ast.Node. | 
					
						
							|  |  |  | func (p *Path) ReplaceWithNode(dst *ast.File, node ast.Node) error { | 
					
						
							|  |  |  | 	for _, doc := range dst.Docs { | 
					
						
							|  |  |  | 		if node.Type() == ast.DocumentType { | 
					
						
							|  |  |  | 			node = node.(*ast.DocumentNode).Body | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if err := p.node.replace(doc.Body, node); err != nil { | 
					
						
							|  |  |  | 			return errors.Wrapf(err, "failed to replace node by path ( %s )", p.node) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 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 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-12 17:03:58 +09:00
										 |  |  | func (b *PathBuilder) containsReservedPathCharacters(path string) bool { | 
					
						
							|  |  |  | 	if strings.Contains(path, ".") { | 
					
						
							|  |  |  | 		return true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if strings.Contains(path, "*") { | 
					
						
							|  |  |  | 		return true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (b *PathBuilder) enclosedSingleQuote(name string) bool { | 
					
						
							|  |  |  | 	return strings.HasPrefix(name, "'") && strings.HasSuffix(name, "'") | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (b *PathBuilder) normalizeSelectorName(name string) string { | 
					
						
							|  |  |  | 	if b.enclosedSingleQuote(name) { | 
					
						
							|  |  |  | 		// already escaped name | 
					
						
							|  |  |  | 		return name | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if b.containsReservedPathCharacters(name) { | 
					
						
							|  |  |  | 		escapedName := strings.ReplaceAll(name, `'`, `\'`) | 
					
						
							|  |  |  | 		return "'" + escapedName + "'" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return name | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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 { | 
					
						
							| 
									
										
										
										
											2021-10-12 17:03:58 +09:00
										 |  |  | 	b.node = b.node.chain(newSelectorNode(b.normalizeSelectorName(name))) | 
					
						
							| 
									
										
										
										
											2020-06-04 13:23:52 +09:00
										 |  |  | 	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-07-16 14:18:26 +09:00
										 |  |  | 	replace(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 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-16 14:18:26 +09:00
										 |  |  | func (n *rootNode) replace(node ast.Node, target ast.Node) error { | 
					
						
							|  |  |  | 	if n.child == nil { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err := n.child.replace(node, target); err != nil { | 
					
						
							|  |  |  | 		return errors.Wrapf(err, "failed to replace") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-30 19:06:35 +09:00
										 |  |  | 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 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-16 14:18:26 +09:00
										 |  |  | func (n *selectorNode) replaceMapValue(value *ast.MappingValueNode, target ast.Node) error { | 
					
						
							|  |  |  | 	key := value.Key.GetToken().Value | 
					
						
							|  |  |  | 	if key != n.selector { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if n.child == nil { | 
					
						
							|  |  |  | 		if err := value.Replace(target); err != nil { | 
					
						
							|  |  |  | 			return errors.Wrapf(err, "failed to replace") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		if err := n.child.replace(value.Value, target); err != nil { | 
					
						
							|  |  |  | 			return errors.Wrapf(err, "failed to replace") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (n *selectorNode) replace(node ast.Node, target ast.Node) error { | 
					
						
							|  |  |  | 	switch node.Type() { | 
					
						
							|  |  |  | 	case ast.MappingType: | 
					
						
							|  |  |  | 		for _, value := range node.(*ast.MappingNode).Values { | 
					
						
							|  |  |  | 			if err := n.replaceMapValue(value, target); err != nil { | 
					
						
							|  |  |  | 				return errors.Wrapf(err, "failed to replace map value") | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	case ast.MappingValueType: | 
					
						
							|  |  |  | 		value := node.(*ast.MappingValueNode) | 
					
						
							|  |  |  | 		if err := n.replaceMapValue(value, target); err != nil { | 
					
						
							|  |  |  | 			return errors.Wrapf(err, "failed to replace map value") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return errors.Wrapf(ErrInvalidQuery, "expected node type is map or map value. but got %s", node.Type()) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-30 19:06:35 +09:00
										 |  |  | 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 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-16 14:18:26 +09:00
										 |  |  | func (n *indexNode) replace(node ast.Node, target ast.Node) error { | 
					
						
							|  |  |  | 	if node.Type() != ast.SequenceType { | 
					
						
							|  |  |  | 		return errors.Wrapf(ErrInvalidQuery, "expected sequence type node. but got %s", node.Type()) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	sequence := node.(*ast.SequenceNode) | 
					
						
							|  |  |  | 	if n.selector >= uint(len(sequence.Values)) { | 
					
						
							|  |  |  | 		return errors.Wrapf(ErrInvalidQuery, "expected index is %d. but got sequences has %d items", n.selector, sequence.Values) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if n.child == nil { | 
					
						
							|  |  |  | 		if err := sequence.Replace(int(n.selector), target); err != nil { | 
					
						
							|  |  |  | 			return errors.Wrapf(err, "failed to replace") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err := n.child.replace(sequence.Values[n.selector], target); err != nil { | 
					
						
							|  |  |  | 		return errors.Wrapf(err, "failed to replace") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-30 19:06:35 +09:00
										 |  |  | 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-07-16 14:18:26 +09:00
										 |  |  | func (n *indexAllNode) replace(node ast.Node, target ast.Node) error { | 
					
						
							|  |  |  | 	if node.Type() != ast.SequenceType { | 
					
						
							|  |  |  | 		return errors.Wrapf(ErrInvalidQuery, "expected sequence type node. but got %s", node.Type()) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	sequence := node.(*ast.SequenceNode) | 
					
						
							|  |  |  | 	if n.child == nil { | 
					
						
							|  |  |  | 		for idx := range sequence.Values { | 
					
						
							|  |  |  | 			if err := sequence.Replace(idx, target); err != nil { | 
					
						
							|  |  |  | 				return errors.Wrapf(err, "failed to replace") | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for _, value := range sequence.Values { | 
					
						
							|  |  |  | 		if err := n.child.replace(value, target); err != nil { | 
					
						
							|  |  |  | 			return errors.Wrapf(err, "failed to replace") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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) { | 
					
						
							| 
									
										
										
										
											2021-07-16 21:49:17 +09:00
										 |  |  | 	sequence := &ast.SequenceNode{BaseNode: &ast.BaseNode{}} | 
					
						
							| 
									
										
										
										
											2020-06-04 13:23:52 +09:00
										 |  |  | 	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
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2020-07-16 14:18:26 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | func (n *recursiveNode) replaceNode(node ast.Node, target ast.Node) error { | 
					
						
							|  |  |  | 	switch typedNode := node.(type) { | 
					
						
							|  |  |  | 	case *ast.MappingNode: | 
					
						
							|  |  |  | 		for _, value := range typedNode.Values { | 
					
						
							|  |  |  | 			if err := n.replaceNode(value, target); err != nil { | 
					
						
							|  |  |  | 				return errors.Wrapf(err, "failed to replace") | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	case *ast.MappingValueNode: | 
					
						
							|  |  |  | 		key := typedNode.Key.GetToken().Value | 
					
						
							|  |  |  | 		if n.selector == key { | 
					
						
							|  |  |  | 			if err := typedNode.Replace(target); err != nil { | 
					
						
							|  |  |  | 				return errors.Wrapf(err, "failed to replace") | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if err := n.replaceNode(typedNode.Value, target); err != nil { | 
					
						
							|  |  |  | 			return errors.Wrapf(err, "failed to replace") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	case *ast.SequenceNode: | 
					
						
							|  |  |  | 		for _, value := range typedNode.Values { | 
					
						
							|  |  |  | 			if err := n.replaceNode(value, target); err != nil { | 
					
						
							|  |  |  | 				return errors.Wrapf(err, "failed to replace") | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (n *recursiveNode) replace(node ast.Node, target ast.Node) error { | 
					
						
							|  |  |  | 	if err := n.replaceNode(node, target); err != nil { | 
					
						
							|  |  |  | 		return errors.Wrapf(err, "failed to replace") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } |