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 }