222 lines
4.3 KiB
Go
222 lines
4.3 KiB
Go
|
package parse
|
||
|
|
||
|
import "encoding/json"
|
||
|
|
||
|
// Tree is the intermediate representation of a pipeline.
|
||
|
type Tree struct {
|
||
|
*ListNode // top-level Tree node
|
||
|
}
|
||
|
|
||
|
// New allocates a new Tree.
|
||
|
func NewTree() *Tree {
|
||
|
return &Tree{
|
||
|
NewListNode(),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Parse parses a JSON encoded Tree.
|
||
|
func Parse(data []byte) (*Tree, error) {
|
||
|
tree := &Tree{}
|
||
|
err := tree.UnmarshalJSON(data)
|
||
|
return tree, err
|
||
|
}
|
||
|
|
||
|
// MarshalJSON implements the Marshaler interface and returns
|
||
|
// a JSON encoded representation of the Tree.
|
||
|
func (t *Tree) MarshalJSON() ([]byte, error) {
|
||
|
return json.Marshal(t.ListNode)
|
||
|
}
|
||
|
|
||
|
// UnmarshalJSON implements the Unmarshaler interface and returns
|
||
|
// a Tree from a JSON representation.
|
||
|
func (t *Tree) UnmarshalJSON(data []byte) error {
|
||
|
block, err := decodeList(data)
|
||
|
if err != nil {
|
||
|
return nil
|
||
|
}
|
||
|
t.ListNode = block.(*ListNode)
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// below are custom decoding functions. We cannot use the default json
|
||
|
// decoder because the tree structure uses interfaces and the json decoder
|
||
|
// has difficulty ascertaining the interface type when decoding.
|
||
|
//
|
||
|
|
||
|
func decodeNode(data []byte) (Node, error) {
|
||
|
node := &nodeType{}
|
||
|
|
||
|
err := json.Unmarshal(data, node)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
switch node.Type {
|
||
|
case NodeList:
|
||
|
return decodeList(data)
|
||
|
case NodeDefer:
|
||
|
return decodeDefer(data)
|
||
|
case NodeError:
|
||
|
return decodeError(data)
|
||
|
case NodeRecover:
|
||
|
return decodeRecover(data)
|
||
|
case NodeParallel:
|
||
|
return decodeParallel(data)
|
||
|
case NodeRun:
|
||
|
return decodeRun(data)
|
||
|
}
|
||
|
return nil, nil
|
||
|
}
|
||
|
|
||
|
func decodeNodes(data []json.RawMessage) ([]Node, error) {
|
||
|
var nodes []Node
|
||
|
for _, d := range data {
|
||
|
node, err := decodeNode(d)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
nodes = append(nodes, node)
|
||
|
}
|
||
|
return nodes, nil
|
||
|
}
|
||
|
|
||
|
func decodeList(data []byte) (Node, error) {
|
||
|
v := &nodeList{}
|
||
|
err := json.Unmarshal(data, v)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
b, err := decodeNodes(v.Body)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
n := NewListNode()
|
||
|
n.Body = b
|
||
|
return n, nil
|
||
|
}
|
||
|
|
||
|
func decodeDefer(data []byte) (Node, error) {
|
||
|
v := &nodeDefer{}
|
||
|
err := json.Unmarshal(data, v)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
b, err := decodeNode(v.Body)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
d, err := decodeNode(v.Defer)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
n := NewDeferNode()
|
||
|
n.Body = b
|
||
|
n.Defer = d
|
||
|
return n, nil
|
||
|
}
|
||
|
|
||
|
func decodeError(data []byte) (Node, error) {
|
||
|
v := &nodeError{}
|
||
|
err := json.Unmarshal(data, v)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
b, err := decodeNode(v.Body)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
d, err := decodeNode(v.Defer)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
n := NewErrorNode()
|
||
|
n.Body = b
|
||
|
n.Defer = d
|
||
|
return n, nil
|
||
|
}
|
||
|
|
||
|
func decodeRecover(data []byte) (Node, error) {
|
||
|
v := &nodeRecover{}
|
||
|
err := json.Unmarshal(data, v)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
b, err := decodeNode(v.Body)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
n := NewRecoverNode()
|
||
|
n.Body = b
|
||
|
return n, nil
|
||
|
}
|
||
|
|
||
|
func decodeParallel(data []byte) (Node, error) {
|
||
|
v := &nodeParallel{}
|
||
|
err := json.Unmarshal(data, v)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
b, err := decodeNodes(v.Body)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
n := NewParallelNode()
|
||
|
n.Body = b
|
||
|
n.Limit = v.Limit
|
||
|
return n, nil
|
||
|
}
|
||
|
|
||
|
func decodeRun(data []byte) (Node, error) {
|
||
|
v := &nodeRun{}
|
||
|
err := json.Unmarshal(data, v)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
return &RunNode{NodeRun, v.Name, v.Detach, v.Silent}, nil
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// below are intermediate representations of the node structures
|
||
|
// since we cannot simply encode / decode using the built-in json
|
||
|
// encoding and decoder.
|
||
|
//
|
||
|
|
||
|
type nodeType struct {
|
||
|
Type NodeType `json:"type"`
|
||
|
}
|
||
|
|
||
|
type nodeDefer struct {
|
||
|
Type NodeType `json:"type"`
|
||
|
Body json.RawMessage `json:"body"`
|
||
|
Defer json.RawMessage `json:"defer"`
|
||
|
}
|
||
|
|
||
|
type nodeError struct {
|
||
|
Type NodeType `json:"type"`
|
||
|
Body json.RawMessage `json:"body"`
|
||
|
Defer json.RawMessage `json:"defer"`
|
||
|
}
|
||
|
|
||
|
type nodeList struct {
|
||
|
Type NodeType `json:"type"`
|
||
|
Body []json.RawMessage `json:"body"`
|
||
|
}
|
||
|
|
||
|
type nodeRecover struct {
|
||
|
Type NodeType `json:"type"`
|
||
|
Body json.RawMessage `json:"body"`
|
||
|
}
|
||
|
|
||
|
type nodeParallel struct {
|
||
|
Type NodeType `json:"type"`
|
||
|
Body []json.RawMessage `json:"body"`
|
||
|
Limit int `json:"limit"`
|
||
|
}
|
||
|
|
||
|
type nodeRun struct {
|
||
|
Type NodeType `json:"type"`
|
||
|
Name string `json:"name"`
|
||
|
Detach bool `json:"detach,omitempty"`
|
||
|
Silent bool `json:"silent,omitempty"`
|
||
|
}
|