github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/dag/marshal.go (about)

     1  package dag
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"sort"
     7  	"strconv"
     8  )
     9  
    10  // the marshal* structs are for serialization of the graph data.
    11  type marshalGraph struct {
    12  	// Type is always "Graph", for identification as a top level object in the
    13  	// JSON stream.
    14  	Type string
    15  
    16  	// Each marshal structure requires a unique ID so that it can be referenced
    17  	// by other structures.
    18  	ID string `json:",omitempty"`
    19  
    20  	// Human readable name for this graph.
    21  	Name string `json:",omitempty"`
    22  
    23  	// Arbitrary attributes that can be added to the output.
    24  	Attrs map[string]string `json:",omitempty"`
    25  
    26  	// List of graph vertices, sorted by ID.
    27  	Vertices []*marshalVertex `json:",omitempty"`
    28  
    29  	// List of edges, sorted by Source ID.
    30  	Edges []*marshalEdge `json:",omitempty"`
    31  
    32  	// Any number of subgraphs. A subgraph itself is considered a vertex, and
    33  	// may be referenced by either end of an edge.
    34  	Subgraphs []*marshalGraph `json:",omitempty"`
    35  
    36  	// Any lists of vertices that are included in cycles.
    37  	Cycles [][]*marshalVertex `json:",omitempty"`
    38  }
    39  
    40  func (g *marshalGraph) vertexByID(id string) *marshalVertex {
    41  	for _, v := range g.Vertices {
    42  		if id == v.ID {
    43  			return v
    44  		}
    45  	}
    46  	return nil
    47  }
    48  
    49  type marshalVertex struct {
    50  	// Unique ID, used to reference this vertex from other structures.
    51  	ID string
    52  
    53  	// Human readable name
    54  	Name string `json:",omitempty"`
    55  
    56  	Attrs map[string]string `json:",omitempty"`
    57  
    58  	// This is to help transition from the old Dot interfaces. We record if the
    59  	// node was a GraphNodeDotter here, so we can call it to get attributes.
    60  	graphNodeDotter GraphNodeDotter
    61  }
    62  
    63  func newMarshalVertex(v Vertex) *marshalVertex {
    64  	dn, ok := v.(GraphNodeDotter)
    65  	if !ok {
    66  		dn = nil
    67  	}
    68  
    69  	// the name will be quoted again later, so we need to ensure it's properly
    70  	// escaped without quotes.
    71  	name := strconv.Quote(VertexName(v))
    72  	name = name[1 : len(name)-1]
    73  
    74  	return &marshalVertex{
    75  		ID:              marshalVertexID(v),
    76  		Name:            name,
    77  		Attrs:           make(map[string]string),
    78  		graphNodeDotter: dn,
    79  	}
    80  }
    81  
    82  // vertices is a sort.Interface implementation for sorting vertices by ID
    83  type vertices []*marshalVertex
    84  
    85  func (v vertices) Less(i, j int) bool { return v[i].Name < v[j].Name }
    86  func (v vertices) Len() int           { return len(v) }
    87  func (v vertices) Swap(i, j int)      { v[i], v[j] = v[j], v[i] }
    88  
    89  type marshalEdge struct {
    90  	// Human readable name
    91  	Name string
    92  
    93  	// Source and Target Vertices by ID
    94  	Source string
    95  	Target string
    96  
    97  	Attrs map[string]string `json:",omitempty"`
    98  }
    99  
   100  func newMarshalEdge(e Edge) *marshalEdge {
   101  	return &marshalEdge{
   102  		Name:   fmt.Sprintf("%s|%s", VertexName(e.Source()), VertexName(e.Target())),
   103  		Source: marshalVertexID(e.Source()),
   104  		Target: marshalVertexID(e.Target()),
   105  		Attrs:  make(map[string]string),
   106  	}
   107  }
   108  
   109  // edges is a sort.Interface implementation for sorting edges by Source ID
   110  type edges []*marshalEdge
   111  
   112  func (e edges) Less(i, j int) bool { return e[i].Name < e[j].Name }
   113  func (e edges) Len() int           { return len(e) }
   114  func (e edges) Swap(i, j int)      { e[i], e[j] = e[j], e[i] }
   115  
   116  // build a marshalGraph structure from a *Graph
   117  func newMarshalGraph(name string, g *Graph) *marshalGraph {
   118  	mg := &marshalGraph{
   119  		Type:  "Graph",
   120  		Name:  name,
   121  		Attrs: make(map[string]string),
   122  	}
   123  
   124  	for _, v := range g.Vertices() {
   125  		id := marshalVertexID(v)
   126  		if sg, ok := marshalSubgrapher(v); ok {
   127  			smg := newMarshalGraph(VertexName(v), sg)
   128  			smg.ID = id
   129  			mg.Subgraphs = append(mg.Subgraphs, smg)
   130  		}
   131  
   132  		mv := newMarshalVertex(v)
   133  		mg.Vertices = append(mg.Vertices, mv)
   134  	}
   135  
   136  	sort.Sort(vertices(mg.Vertices))
   137  
   138  	for _, e := range g.Edges() {
   139  		mg.Edges = append(mg.Edges, newMarshalEdge(e))
   140  	}
   141  
   142  	sort.Sort(edges(mg.Edges))
   143  
   144  	for _, c := range (&AcyclicGraph{*g}).Cycles() {
   145  		var cycle []*marshalVertex
   146  		for _, v := range c {
   147  			mv := newMarshalVertex(v)
   148  			cycle = append(cycle, mv)
   149  		}
   150  		mg.Cycles = append(mg.Cycles, cycle)
   151  	}
   152  
   153  	return mg
   154  }
   155  
   156  // Attempt to return a unique ID for any vertex.
   157  func marshalVertexID(v Vertex) string {
   158  	val := reflect.ValueOf(v)
   159  	switch val.Kind() {
   160  	case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Slice, reflect.UnsafePointer:
   161  		return strconv.Itoa(int(val.Pointer()))
   162  	case reflect.Interface:
   163  		// A vertex shouldn't contain another layer of interface, but handle
   164  		// this just in case.
   165  		return fmt.Sprintf("%#v", val.Interface())
   166  	}
   167  
   168  	if v, ok := v.(Hashable); ok {
   169  		h := v.Hashcode()
   170  		if h, ok := h.(string); ok {
   171  			return h
   172  		}
   173  	}
   174  
   175  	// fallback to a name, which we hope is unique.
   176  	return VertexName(v)
   177  
   178  	// we could try harder by attempting to read the arbitrary value from the
   179  	// interface, but we shouldn't get here from terraform right now.
   180  }
   181  
   182  // check for a Subgrapher, and return the underlying *Graph.
   183  func marshalSubgrapher(v Vertex) (*Graph, bool) {
   184  	sg, ok := v.(Subgrapher)
   185  	if !ok {
   186  		return nil, false
   187  	}
   188  
   189  	switch g := sg.Subgraph().DirectedGraph().(type) {
   190  	case *Graph:
   191  		return g, true
   192  	case *AcyclicGraph:
   193  		return &g.Graph, true
   194  	}
   195  
   196  	return nil, false
   197  }