mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-06 04:20:11 +00:00
129 lines
3.4 KiB
Go
129 lines
3.4 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)
|
|
}
|
|
|
|
// FindNodeOrigin searches this index for a matching node. If the node is found, a NodeOrigin
|
|
// is returned, otherwise nil is returned.
|
|
func (index *SpecIndex) FindNodeOrigin(node *yaml.Node) *NodeOrigin {
|
|
if node != nil {
|
|
if index.nodeMap[node.Line] != nil {
|
|
if index.nodeMap[node.Line][node.Column] != nil {
|
|
foundNode := index.nodeMap[node.Line][node.Column]
|
|
if foundNode.Kind == yaml.DocumentNode {
|
|
foundNode = foundNode.Content[0]
|
|
}
|
|
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)
|
|
}
|
|
}
|