mirror of
https://github.com/superseriousbusiness/gotosocial
synced 2024-12-21 10:13:11 +00:00
670 lines
16 KiB
Go
670 lines
16 KiB
Go
package decoder
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"strconv"
|
|
|
|
"github.com/goccy/go-json/internal/errors"
|
|
"github.com/goccy/go-json/internal/runtime"
|
|
)
|
|
|
|
type PathString string
|
|
|
|
func (s PathString) Build() (*Path, error) {
|
|
builder := new(PathBuilder)
|
|
return builder.Build([]rune(s))
|
|
}
|
|
|
|
type PathBuilder struct {
|
|
root PathNode
|
|
node PathNode
|
|
singleQuotePathSelector bool
|
|
doubleQuotePathSelector bool
|
|
}
|
|
|
|
func (b *PathBuilder) Build(buf []rune) (*Path, error) {
|
|
node, err := b.build(buf)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &Path{
|
|
node: node,
|
|
RootSelectorOnly: node == nil,
|
|
SingleQuotePathSelector: b.singleQuotePathSelector,
|
|
DoubleQuotePathSelector: b.doubleQuotePathSelector,
|
|
}, nil
|
|
}
|
|
|
|
func (b *PathBuilder) build(buf []rune) (PathNode, error) {
|
|
if len(buf) == 0 {
|
|
return nil, errors.ErrEmptyPath()
|
|
}
|
|
if buf[0] != '$' {
|
|
return nil, errors.ErrInvalidPath("JSON Path must start with a $ character")
|
|
}
|
|
if len(buf) == 1 {
|
|
return nil, nil
|
|
}
|
|
buf = buf[1:]
|
|
offset, err := b.buildNext(buf)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(buf) > offset {
|
|
return nil, errors.ErrInvalidPath("remain invalid path %q", buf[offset:])
|
|
}
|
|
return b.root, nil
|
|
}
|
|
|
|
func (b *PathBuilder) buildNextCharIfExists(buf []rune, cursor int) (int, error) {
|
|
if len(buf) > cursor {
|
|
offset, err := b.buildNext(buf[cursor:])
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return cursor + 1 + offset, nil
|
|
}
|
|
return cursor, nil
|
|
}
|
|
|
|
func (b *PathBuilder) buildNext(buf []rune) (int, error) {
|
|
switch buf[0] {
|
|
case '.':
|
|
if len(buf) == 1 {
|
|
return 0, errors.ErrInvalidPath("JSON Path ends with dot character")
|
|
}
|
|
offset, err := b.buildSelector(buf[1:])
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return offset + 1, nil
|
|
case '[':
|
|
if len(buf) == 1 {
|
|
return 0, errors.ErrInvalidPath("JSON Path ends with left bracket character")
|
|
}
|
|
offset, err := b.buildIndex(buf[1:])
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return offset + 1, nil
|
|
default:
|
|
return 0, errors.ErrInvalidPath("expect dot or left bracket character. but found %c character", buf[0])
|
|
}
|
|
}
|
|
|
|
func (b *PathBuilder) buildSelector(buf []rune) (int, error) {
|
|
switch buf[0] {
|
|
case '.':
|
|
if len(buf) == 1 {
|
|
return 0, errors.ErrInvalidPath("JSON Path ends with double dot character")
|
|
}
|
|
offset, err := b.buildPathRecursive(buf[1:])
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return 1 + offset, nil
|
|
case '[', ']', '$', '*':
|
|
return 0, errors.ErrInvalidPath("found invalid path character %c after dot", buf[0])
|
|
}
|
|
for cursor := 0; cursor < len(buf); cursor++ {
|
|
switch buf[cursor] {
|
|
case '$', '*', ']':
|
|
return 0, errors.ErrInvalidPath("found %c character in field selector context", buf[cursor])
|
|
case '.':
|
|
if cursor+1 >= len(buf) {
|
|
return 0, errors.ErrInvalidPath("JSON Path ends with dot character")
|
|
}
|
|
selector := buf[:cursor]
|
|
b.addSelectorNode(string(selector))
|
|
offset, err := b.buildSelector(buf[cursor+1:])
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return cursor + 1 + offset, nil
|
|
case '[':
|
|
if cursor+1 >= len(buf) {
|
|
return 0, errors.ErrInvalidPath("JSON Path ends with left bracket character")
|
|
}
|
|
selector := buf[:cursor]
|
|
b.addSelectorNode(string(selector))
|
|
offset, err := b.buildIndex(buf[cursor+1:])
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return cursor + 1 + offset, nil
|
|
case '"':
|
|
if cursor+1 >= len(buf) {
|
|
return 0, errors.ErrInvalidPath("JSON Path ends with double quote character")
|
|
}
|
|
offset, err := b.buildQuoteSelector(buf[cursor+1:], DoubleQuotePathSelector)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return cursor + 1 + offset, nil
|
|
}
|
|
}
|
|
b.addSelectorNode(string(buf))
|
|
return len(buf), nil
|
|
}
|
|
|
|
func (b *PathBuilder) buildQuoteSelector(buf []rune, sel QuotePathSelector) (int, error) {
|
|
switch buf[0] {
|
|
case '[', ']', '$', '.', '*', '\'', '"':
|
|
return 0, errors.ErrInvalidPath("found invalid path character %c after quote", buf[0])
|
|
}
|
|
for cursor := 0; cursor < len(buf); cursor++ {
|
|
switch buf[cursor] {
|
|
case '\'':
|
|
if sel != SingleQuotePathSelector {
|
|
return 0, errors.ErrInvalidPath("found double quote character in field selector with single quote context")
|
|
}
|
|
if len(buf) <= cursor+1 {
|
|
return 0, errors.ErrInvalidPath("JSON Path ends with single quote character in field selector context")
|
|
}
|
|
if buf[cursor+1] != ']' {
|
|
return 0, errors.ErrInvalidPath("expect right bracket for field selector with single quote but found %c", buf[cursor+1])
|
|
}
|
|
selector := buf[:cursor]
|
|
b.addSelectorNode(string(selector))
|
|
b.singleQuotePathSelector = true
|
|
return b.buildNextCharIfExists(buf, cursor+2)
|
|
case '"':
|
|
if sel != DoubleQuotePathSelector {
|
|
return 0, errors.ErrInvalidPath("found single quote character in field selector with double quote context")
|
|
}
|
|
selector := buf[:cursor]
|
|
b.addSelectorNode(string(selector))
|
|
b.doubleQuotePathSelector = true
|
|
return b.buildNextCharIfExists(buf, cursor+1)
|
|
}
|
|
}
|
|
return 0, errors.ErrInvalidPath("couldn't find quote character in selector quote path context")
|
|
}
|
|
|
|
func (b *PathBuilder) buildPathRecursive(buf []rune) (int, error) {
|
|
switch buf[0] {
|
|
case '.', '[', ']', '$', '*':
|
|
return 0, errors.ErrInvalidPath("found invalid path character %c after double dot", buf[0])
|
|
}
|
|
for cursor := 0; cursor < len(buf); cursor++ {
|
|
switch buf[cursor] {
|
|
case '$', '*', ']':
|
|
return 0, errors.ErrInvalidPath("found %c character in field selector context", buf[cursor])
|
|
case '.':
|
|
if cursor+1 >= len(buf) {
|
|
return 0, errors.ErrInvalidPath("JSON Path ends with dot character")
|
|
}
|
|
selector := buf[:cursor]
|
|
b.addRecursiveNode(string(selector))
|
|
offset, err := b.buildSelector(buf[cursor+1:])
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return cursor + 1 + offset, nil
|
|
case '[':
|
|
if cursor+1 >= len(buf) {
|
|
return 0, errors.ErrInvalidPath("JSON Path ends with left bracket character")
|
|
}
|
|
selector := buf[:cursor]
|
|
b.addRecursiveNode(string(selector))
|
|
offset, err := b.buildIndex(buf[cursor+1:])
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return cursor + 1 + offset, nil
|
|
}
|
|
}
|
|
b.addRecursiveNode(string(buf))
|
|
return len(buf), nil
|
|
}
|
|
|
|
func (b *PathBuilder) buildIndex(buf []rune) (int, error) {
|
|
switch buf[0] {
|
|
case '.', '[', ']', '$':
|
|
return 0, errors.ErrInvalidPath("found invalid path character %c after left bracket", buf[0])
|
|
case '\'':
|
|
if len(buf) == 1 {
|
|
return 0, errors.ErrInvalidPath("JSON Path ends with single quote character")
|
|
}
|
|
offset, err := b.buildQuoteSelector(buf[1:], SingleQuotePathSelector)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return 1 + offset, nil
|
|
case '*':
|
|
if len(buf) == 1 {
|
|
return 0, errors.ErrInvalidPath("JSON Path ends with star character")
|
|
}
|
|
if buf[1] != ']' {
|
|
return 0, errors.ErrInvalidPath("expect right bracket character for index all path but found %c character", buf[1])
|
|
}
|
|
b.addIndexAllNode()
|
|
offset := len("*]")
|
|
if len(buf) > 2 {
|
|
buildOffset, err := b.buildNext(buf[2:])
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return offset + buildOffset, nil
|
|
}
|
|
return offset, nil
|
|
}
|
|
|
|
for cursor := 0; cursor < len(buf); cursor++ {
|
|
switch buf[cursor] {
|
|
case ']':
|
|
index, err := strconv.ParseInt(string(buf[:cursor]), 10, 64)
|
|
if err != nil {
|
|
return 0, errors.ErrInvalidPath("%q is unexpected index path", buf[:cursor])
|
|
}
|
|
b.addIndexNode(int(index))
|
|
return b.buildNextCharIfExists(buf, cursor+1)
|
|
}
|
|
}
|
|
return 0, errors.ErrInvalidPath("couldn't find right bracket character in index path context")
|
|
}
|
|
|
|
func (b *PathBuilder) addIndexAllNode() {
|
|
node := newPathIndexAllNode()
|
|
if b.root == nil {
|
|
b.root = node
|
|
b.node = node
|
|
} else {
|
|
b.node = b.node.chain(node)
|
|
}
|
|
}
|
|
|
|
func (b *PathBuilder) addRecursiveNode(selector string) {
|
|
node := newPathRecursiveNode(selector)
|
|
if b.root == nil {
|
|
b.root = node
|
|
b.node = node
|
|
} else {
|
|
b.node = b.node.chain(node)
|
|
}
|
|
}
|
|
|
|
func (b *PathBuilder) addSelectorNode(name string) {
|
|
node := newPathSelectorNode(name)
|
|
if b.root == nil {
|
|
b.root = node
|
|
b.node = node
|
|
} else {
|
|
b.node = b.node.chain(node)
|
|
}
|
|
}
|
|
|
|
func (b *PathBuilder) addIndexNode(idx int) {
|
|
node := newPathIndexNode(idx)
|
|
if b.root == nil {
|
|
b.root = node
|
|
b.node = node
|
|
} else {
|
|
b.node = b.node.chain(node)
|
|
}
|
|
}
|
|
|
|
type QuotePathSelector int
|
|
|
|
const (
|
|
SingleQuotePathSelector QuotePathSelector = 1
|
|
DoubleQuotePathSelector QuotePathSelector = 2
|
|
)
|
|
|
|
type Path struct {
|
|
node PathNode
|
|
RootSelectorOnly bool
|
|
SingleQuotePathSelector bool
|
|
DoubleQuotePathSelector bool
|
|
}
|
|
|
|
func (p *Path) Field(sel string) (PathNode, bool, error) {
|
|
if p.node == nil {
|
|
return nil, false, nil
|
|
}
|
|
return p.node.Field(sel)
|
|
}
|
|
|
|
func (p *Path) Get(src, dst reflect.Value) error {
|
|
if p.node == nil {
|
|
return nil
|
|
}
|
|
return p.node.Get(src, dst)
|
|
}
|
|
|
|
func (p *Path) String() string {
|
|
if p.node == nil {
|
|
return "$"
|
|
}
|
|
return p.node.String()
|
|
}
|
|
|
|
type PathNode interface {
|
|
fmt.Stringer
|
|
Index(idx int) (PathNode, bool, error)
|
|
Field(fieldName string) (PathNode, bool, error)
|
|
Get(src, dst reflect.Value) error
|
|
chain(PathNode) PathNode
|
|
target() bool
|
|
single() bool
|
|
}
|
|
|
|
type BasePathNode struct {
|
|
child PathNode
|
|
}
|
|
|
|
func (n *BasePathNode) chain(node PathNode) PathNode {
|
|
n.child = node
|
|
return node
|
|
}
|
|
|
|
func (n *BasePathNode) target() bool {
|
|
return n.child == nil
|
|
}
|
|
|
|
func (n *BasePathNode) single() bool {
|
|
return true
|
|
}
|
|
|
|
type PathSelectorNode struct {
|
|
*BasePathNode
|
|
selector string
|
|
}
|
|
|
|
func newPathSelectorNode(selector string) *PathSelectorNode {
|
|
return &PathSelectorNode{
|
|
BasePathNode: &BasePathNode{},
|
|
selector: selector,
|
|
}
|
|
}
|
|
|
|
func (n *PathSelectorNode) Index(idx int) (PathNode, bool, error) {
|
|
return nil, false, &errors.PathError{}
|
|
}
|
|
|
|
func (n *PathSelectorNode) Field(fieldName string) (PathNode, bool, error) {
|
|
if n.selector == fieldName {
|
|
return n.child, true, nil
|
|
}
|
|
return nil, false, nil
|
|
}
|
|
|
|
func (n *PathSelectorNode) Get(src, dst reflect.Value) error {
|
|
switch src.Type().Kind() {
|
|
case reflect.Map:
|
|
iter := src.MapRange()
|
|
for iter.Next() {
|
|
key, ok := iter.Key().Interface().(string)
|
|
if !ok {
|
|
return fmt.Errorf("invalid map key type %T", src.Type().Key())
|
|
}
|
|
child, found, err := n.Field(key)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if found {
|
|
if child != nil {
|
|
return child.Get(iter.Value(), dst)
|
|
}
|
|
return AssignValue(iter.Value(), dst)
|
|
}
|
|
}
|
|
case reflect.Struct:
|
|
typ := src.Type()
|
|
for i := 0; i < typ.Len(); i++ {
|
|
tag := runtime.StructTagFromField(typ.Field(i))
|
|
child, found, err := n.Field(tag.Key)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if found {
|
|
if child != nil {
|
|
return child.Get(src.Field(i), dst)
|
|
}
|
|
return AssignValue(src.Field(i), dst)
|
|
}
|
|
}
|
|
case reflect.Ptr:
|
|
return n.Get(src.Elem(), dst)
|
|
case reflect.Interface:
|
|
return n.Get(reflect.ValueOf(src.Interface()), dst)
|
|
case reflect.Float64, reflect.String, reflect.Bool:
|
|
return AssignValue(src, dst)
|
|
}
|
|
return fmt.Errorf("failed to get %s value from %s", n.selector, src.Type())
|
|
}
|
|
|
|
func (n *PathSelectorNode) String() string {
|
|
s := fmt.Sprintf(".%s", n.selector)
|
|
if n.child != nil {
|
|
s += n.child.String()
|
|
}
|
|
return s
|
|
}
|
|
|
|
type PathIndexNode struct {
|
|
*BasePathNode
|
|
selector int
|
|
}
|
|
|
|
func newPathIndexNode(selector int) *PathIndexNode {
|
|
return &PathIndexNode{
|
|
BasePathNode: &BasePathNode{},
|
|
selector: selector,
|
|
}
|
|
}
|
|
|
|
func (n *PathIndexNode) Index(idx int) (PathNode, bool, error) {
|
|
if n.selector == idx {
|
|
return n.child, true, nil
|
|
}
|
|
return nil, false, nil
|
|
}
|
|
|
|
func (n *PathIndexNode) Field(fieldName string) (PathNode, bool, error) {
|
|
return nil, false, &errors.PathError{}
|
|
}
|
|
|
|
func (n *PathIndexNode) Get(src, dst reflect.Value) error {
|
|
switch src.Type().Kind() {
|
|
case reflect.Array, reflect.Slice:
|
|
if src.Len() > n.selector {
|
|
if n.child != nil {
|
|
return n.child.Get(src.Index(n.selector), dst)
|
|
}
|
|
return AssignValue(src.Index(n.selector), dst)
|
|
}
|
|
case reflect.Ptr:
|
|
return n.Get(src.Elem(), dst)
|
|
case reflect.Interface:
|
|
return n.Get(reflect.ValueOf(src.Interface()), dst)
|
|
}
|
|
return fmt.Errorf("failed to get [%d] value from %s", n.selector, src.Type())
|
|
}
|
|
|
|
func (n *PathIndexNode) String() string {
|
|
s := fmt.Sprintf("[%d]", n.selector)
|
|
if n.child != nil {
|
|
s += n.child.String()
|
|
}
|
|
return s
|
|
}
|
|
|
|
type PathIndexAllNode struct {
|
|
*BasePathNode
|
|
}
|
|
|
|
func newPathIndexAllNode() *PathIndexAllNode {
|
|
return &PathIndexAllNode{
|
|
BasePathNode: &BasePathNode{},
|
|
}
|
|
}
|
|
|
|
func (n *PathIndexAllNode) Index(idx int) (PathNode, bool, error) {
|
|
return n.child, true, nil
|
|
}
|
|
|
|
func (n *PathIndexAllNode) Field(fieldName string) (PathNode, bool, error) {
|
|
return nil, false, &errors.PathError{}
|
|
}
|
|
|
|
func (n *PathIndexAllNode) Get(src, dst reflect.Value) error {
|
|
switch src.Type().Kind() {
|
|
case reflect.Array, reflect.Slice:
|
|
var arr []interface{}
|
|
for i := 0; i < src.Len(); i++ {
|
|
var v interface{}
|
|
rv := reflect.ValueOf(&v)
|
|
if n.child != nil {
|
|
if err := n.child.Get(src.Index(i), rv); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
if err := AssignValue(src.Index(i), rv); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
arr = append(arr, v)
|
|
}
|
|
if err := AssignValue(reflect.ValueOf(arr), dst); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
case reflect.Ptr:
|
|
return n.Get(src.Elem(), dst)
|
|
case reflect.Interface:
|
|
return n.Get(reflect.ValueOf(src.Interface()), dst)
|
|
}
|
|
return fmt.Errorf("failed to get all value from %s", src.Type())
|
|
}
|
|
|
|
func (n *PathIndexAllNode) String() string {
|
|
s := "[*]"
|
|
if n.child != nil {
|
|
s += n.child.String()
|
|
}
|
|
return s
|
|
}
|
|
|
|
type PathRecursiveNode struct {
|
|
*BasePathNode
|
|
selector string
|
|
}
|
|
|
|
func newPathRecursiveNode(selector string) *PathRecursiveNode {
|
|
node := newPathSelectorNode(selector)
|
|
return &PathRecursiveNode{
|
|
BasePathNode: &BasePathNode{
|
|
child: node,
|
|
},
|
|
selector: selector,
|
|
}
|
|
}
|
|
|
|
func (n *PathRecursiveNode) Field(fieldName string) (PathNode, bool, error) {
|
|
if n.selector == fieldName {
|
|
return n.child, true, nil
|
|
}
|
|
return nil, false, nil
|
|
}
|
|
|
|
func (n *PathRecursiveNode) Index(_ int) (PathNode, bool, error) {
|
|
return n, true, nil
|
|
}
|
|
|
|
func valueToSliceValue(v interface{}) []interface{} {
|
|
rv := reflect.ValueOf(v)
|
|
ret := []interface{}{}
|
|
if rv.Type().Kind() == reflect.Slice || rv.Type().Kind() == reflect.Array {
|
|
for i := 0; i < rv.Len(); i++ {
|
|
ret = append(ret, rv.Index(i).Interface())
|
|
}
|
|
return ret
|
|
}
|
|
return []interface{}{v}
|
|
}
|
|
|
|
func (n *PathRecursiveNode) Get(src, dst reflect.Value) error {
|
|
if n.child == nil {
|
|
return fmt.Errorf("failed to get by recursive path ..%s", n.selector)
|
|
}
|
|
var arr []interface{}
|
|
switch src.Type().Kind() {
|
|
case reflect.Map:
|
|
iter := src.MapRange()
|
|
for iter.Next() {
|
|
key, ok := iter.Key().Interface().(string)
|
|
if !ok {
|
|
return fmt.Errorf("invalid map key type %T", src.Type().Key())
|
|
}
|
|
child, found, err := n.Field(key)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if found {
|
|
var v interface{}
|
|
rv := reflect.ValueOf(&v)
|
|
_ = child.Get(iter.Value(), rv)
|
|
arr = append(arr, valueToSliceValue(v)...)
|
|
} else {
|
|
var v interface{}
|
|
rv := reflect.ValueOf(&v)
|
|
_ = n.Get(iter.Value(), rv)
|
|
if v != nil {
|
|
arr = append(arr, valueToSliceValue(v)...)
|
|
}
|
|
}
|
|
}
|
|
_ = AssignValue(reflect.ValueOf(arr), dst)
|
|
return nil
|
|
case reflect.Struct:
|
|
typ := src.Type()
|
|
for i := 0; i < typ.Len(); i++ {
|
|
tag := runtime.StructTagFromField(typ.Field(i))
|
|
child, found, err := n.Field(tag.Key)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if found {
|
|
var v interface{}
|
|
rv := reflect.ValueOf(&v)
|
|
_ = child.Get(src.Field(i), rv)
|
|
arr = append(arr, valueToSliceValue(v)...)
|
|
} else {
|
|
var v interface{}
|
|
rv := reflect.ValueOf(&v)
|
|
_ = n.Get(src.Field(i), rv)
|
|
if v != nil {
|
|
arr = append(arr, valueToSliceValue(v)...)
|
|
}
|
|
}
|
|
}
|
|
_ = AssignValue(reflect.ValueOf(arr), dst)
|
|
return nil
|
|
case reflect.Array, reflect.Slice:
|
|
for i := 0; i < src.Len(); i++ {
|
|
var v interface{}
|
|
rv := reflect.ValueOf(&v)
|
|
_ = n.Get(src.Index(i), rv)
|
|
if v != nil {
|
|
arr = append(arr, valueToSliceValue(v)...)
|
|
}
|
|
}
|
|
_ = AssignValue(reflect.ValueOf(arr), dst)
|
|
return nil
|
|
case reflect.Ptr:
|
|
return n.Get(src.Elem(), dst)
|
|
case reflect.Interface:
|
|
return n.Get(reflect.ValueOf(src.Interface()), dst)
|
|
}
|
|
return fmt.Errorf("failed to get %s value from %s", n.selector, src.Type())
|
|
}
|
|
|
|
func (n *PathRecursiveNode) String() string {
|
|
s := fmt.Sprintf("..%s", n.selector)
|
|
if n.child != nil {
|
|
s += n.child.String()
|
|
}
|
|
return s
|
|
}
|