mirror of
https://github.com/mikefarah/yq.git
synced 2024-12-19 20:19:04 +00:00
177 lines
5.2 KiB
Go
177 lines
5.2 KiB
Go
package yqlib
|
|
|
|
import (
|
|
"container/list"
|
|
"fmt"
|
|
)
|
|
|
|
func entrySeqFor(key *CandidateNode, value *CandidateNode) *CandidateNode {
|
|
var keyKey = &CandidateNode{Kind: ScalarNode, Tag: "!!str", Value: "key"}
|
|
var valueKey = &CandidateNode{Kind: ScalarNode, Tag: "!!str", Value: "value"}
|
|
|
|
return &CandidateNode{
|
|
Kind: MappingNode,
|
|
Tag: "!!map",
|
|
Content: []*CandidateNode{keyKey, key, valueKey, value},
|
|
}
|
|
}
|
|
|
|
func toEntriesFromMap(candidateNode *CandidateNode) *CandidateNode {
|
|
var sequence = candidateNode.CreateReplacementWithDocWrappers(SequenceNode, "!!seq", 0)
|
|
|
|
var contents = candidateNode.unwrapDocument().Content
|
|
for index := 0; index < len(contents); index = index + 2 {
|
|
key := contents[index]
|
|
value := contents[index+1]
|
|
|
|
sequence.AddChild(entrySeqFor(key, value))
|
|
}
|
|
return sequence
|
|
}
|
|
|
|
func toEntriesfromSeq(candidateNode *CandidateNode) *CandidateNode {
|
|
var sequence = candidateNode.CreateReplacementWithDocWrappers(SequenceNode, "!!seq", 0)
|
|
|
|
var contents = candidateNode.unwrapDocument().Content
|
|
for index := 0; index < len(contents); index = index + 1 {
|
|
key := &CandidateNode{Kind: ScalarNode, Tag: "!!int", Value: fmt.Sprintf("%v", index)}
|
|
value := contents[index]
|
|
|
|
sequence.AddChild(entrySeqFor(key, value))
|
|
}
|
|
return sequence
|
|
}
|
|
|
|
func toEntriesOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
|
var results = list.New()
|
|
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
|
candidate := el.Value.(*CandidateNode)
|
|
candidateNode := candidate.unwrapDocument()
|
|
|
|
switch candidateNode.Kind {
|
|
case MappingNode:
|
|
results.PushBack(toEntriesFromMap(candidate))
|
|
|
|
case SequenceNode:
|
|
results.PushBack(toEntriesfromSeq(candidate))
|
|
default:
|
|
if candidateNode.Tag != "!!null" {
|
|
return Context{}, fmt.Errorf("%v has no keys", candidate.Tag)
|
|
}
|
|
}
|
|
}
|
|
|
|
return context.ChildContext(results), nil
|
|
}
|
|
|
|
func parseEntry(candidateNode *CandidateNode, position int) (*CandidateNode, *CandidateNode, error) {
|
|
prefs := traversePreferences{DontAutoCreate: true}
|
|
|
|
keyResults, err := traverseMap(Context{}, candidateNode, createStringScalarNode("key"), prefs, false)
|
|
|
|
if err != nil {
|
|
return nil, nil, err
|
|
} else if keyResults.Len() != 1 {
|
|
return nil, nil, fmt.Errorf("expected to find one 'key' entry but found %v in position %v", keyResults.Len(), position)
|
|
}
|
|
|
|
valueResults, err := traverseMap(Context{}, candidateNode, createStringScalarNode("value"), prefs, false)
|
|
|
|
if err != nil {
|
|
return nil, nil, err
|
|
} else if valueResults.Len() != 1 {
|
|
return nil, nil, fmt.Errorf("expected to find one 'value' entry but found %v in position %v", valueResults.Len(), position)
|
|
}
|
|
|
|
return keyResults.Front().Value.(*CandidateNode), valueResults.Front().Value.(*CandidateNode), nil
|
|
|
|
}
|
|
|
|
func fromEntries(candidateNode *CandidateNode) (*CandidateNode, error) {
|
|
var node = candidateNode.unwrapDocument().CopyWithoutContent()
|
|
|
|
var contents = candidateNode.unwrapDocument().Content
|
|
|
|
for index := 0; index < len(contents); index = index + 1 {
|
|
key, value, err := parseEntry(contents[index], index)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
node.AddKeyValueChild(key, value)
|
|
}
|
|
node.Kind = MappingNode
|
|
node.Tag = "!!map"
|
|
return node, nil
|
|
}
|
|
|
|
func fromEntriesOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
|
var results = list.New()
|
|
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
|
candidate := el.Value.(*CandidateNode)
|
|
candidateNode := candidate.unwrapDocument()
|
|
|
|
switch candidateNode.Kind {
|
|
case SequenceNode:
|
|
mapResult, err := fromEntries(candidate)
|
|
if err != nil {
|
|
return Context{}, err
|
|
}
|
|
results.PushBack(mapResult)
|
|
default:
|
|
return Context{}, fmt.Errorf("from entries only runs against arrays")
|
|
}
|
|
}
|
|
|
|
return context.ChildContext(results), nil
|
|
}
|
|
|
|
func withEntriesOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
|
|
|
//to_entries on the context
|
|
toEntries, err := toEntriesOperator(d, context, expressionNode)
|
|
if err != nil {
|
|
return Context{}, err
|
|
}
|
|
|
|
var results = list.New()
|
|
|
|
for el := toEntries.MatchingNodes.Front(); el != nil; el = el.Next() {
|
|
candidate := el.Value.(*CandidateNode)
|
|
|
|
//run expression against entries
|
|
// splat toEntries and pipe it into Rhs
|
|
splatted, err := splat(context.SingleChildContext(candidate), traversePreferences{})
|
|
if err != nil {
|
|
return Context{}, err
|
|
}
|
|
|
|
result, err := d.GetMatchingNodes(splatted, expressionNode.RHS)
|
|
log.Debug("expressionNode.Rhs %v", expressionNode.RHS.Operation.OperationType)
|
|
log.Debug("result %v", result)
|
|
if err != nil {
|
|
return Context{}, err
|
|
}
|
|
|
|
selfExpression := &ExpressionNode{Operation: &Operation{OperationType: selfReferenceOpType}}
|
|
collected, err := collectTogether(d, result, selfExpression)
|
|
if err != nil {
|
|
return Context{}, err
|
|
}
|
|
collected.LeadingContent = candidate.LeadingContent
|
|
collected.TrailingContent = candidate.TrailingContent
|
|
|
|
log.Debugf("**** collected %v", collected.LeadingContent)
|
|
|
|
fromEntries, err := fromEntriesOperator(d, context.SingleChildContext(collected), expressionNode)
|
|
if err != nil {
|
|
return Context{}, err
|
|
}
|
|
results.PushBackList(fromEntries.MatchingNodes)
|
|
|
|
}
|
|
|
|
//from_entries on the result
|
|
return context.ChildContext(results), nil
|
|
}
|