Files
libopenapi/index/map_index_nodes.go
2023-11-25 10:10:51 -05:00

126 lines
3.2 KiB
Go

// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley
// SPDX-License-Identifier: MIT
package index
import (
"gopkg.in/yaml.v3"
)
type nodeMap struct {
line int
column int
node *yaml.Node
}
// NodeOrigin represents where a node has come from within a specification. This is not useful for single file specs,
// but becomes very, very important when dealing with exploded specifications, and we need to know where in the mass
// of files a node has come from.
type NodeOrigin struct {
// Node is the node in question
Node *yaml.Node
// Line is yhe original line of where the node was found in the original file
Line int
// Column is the original column of where the node was found in the original file
Column int
// AbsoluteLocation is the absolute path to the reference was extracted from.
// This can either be an absolute path to a file, or a URL.
AbsoluteLocation string
// Index is the index that contains the node that was located in.
Index *SpecIndex
}
// GetNode returns a node from the spec based on a line and column. The second return var bool is true
// if the node was found, false if not.
func (index *SpecIndex) GetNode(line int, column int) (*yaml.Node, bool) {
if index.nodeMap[line] == nil {
return nil, false
}
node := index.nodeMap[line][column]
return node, node != nil
}
// MapNodes maps all nodes in the document to a map of line/column to node.
func (index *SpecIndex) MapNodes(rootNode *yaml.Node) {
cruising := make(chan bool)
nodeChan := make(chan *nodeMap)
go func(nodeChan chan *nodeMap) {
for {
select {
case node, ok := <-nodeChan:
if !ok {
cruising <- true
return
}
if index.nodeMap[node.line] == nil {
index.nodeMap[node.line] = make(map[int]*yaml.Node)
}
index.nodeMap[node.line][node.column] = node.node
}
}
}(nodeChan)
go enjoyALuxuryCruise(rootNode, nodeChan, true)
<-cruising
close(cruising)
index.nodeMapCompleted <- true
close(index.nodeMapCompleted)
}
func (index *SpecIndex) FindNodeOrigin(node *yaml.Node) *NodeOrigin {
// local search, then throw up to rolodex for a full search
if node != nil {
if index.nodeMap[node.Line] != nil {
if index.nodeMap[node.Line][node.Column] != nil {
foundNode := index.nodeMap[node.Line][node.Column]
match := true
if foundNode.Value != node.Value || foundNode.Kind != node.Kind || foundNode.Tag != node.Tag {
match = false
}
if len(foundNode.Content) == len(node.Content) {
for i := range foundNode.Content {
if foundNode.Content[i].Value != node.Content[i].Value {
match = false
}
}
}
if match {
return &NodeOrigin{
Node: foundNode,
Line: node.Line,
Column: node.Column,
AbsoluteLocation: index.specAbsolutePath,
Index: index,
}
}
}
}
}
return nil
}
func enjoyALuxuryCruise(node *yaml.Node, nodeChan chan *nodeMap, root bool) {
if len(node.Content) > 0 {
for _, child := range node.Content {
nodeChan <- &nodeMap{
line: child.Line,
column: child.Column,
node: child,
}
enjoyALuxuryCruise(child, nodeChan, false)
}
}
nodeChan <- &nodeMap{
line: node.Line,
column: node.Column,
node: node,
}
if root {
close(nodeChan)
}
}