2022-11-03 13:54:49 +00:00
|
|
|
package d2graph
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"encoding/json"
|
2023-02-18 16:56:17 +00:00
|
|
|
"fmt"
|
2023-02-17 18:44:33 +00:00
|
|
|
"strings"
|
2022-11-03 13:54:49 +00:00
|
|
|
|
2023-02-18 16:56:17 +00:00
|
|
|
"oss.terrastruct.com/d2/d2target"
|
2022-12-01 18:48:01 +00:00
|
|
|
"oss.terrastruct.com/util-go/go2"
|
2022-11-03 13:54:49 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type SerializedGraph struct {
|
2024-11-07 22:41:57 +00:00
|
|
|
Root SerializedObject `json:"root"`
|
|
|
|
|
Edges []SerializedEdge `json:"edges"`
|
|
|
|
|
Objects []SerializedObject `json:"objects"`
|
|
|
|
|
RootLevel int `json:"rootLevel"`
|
|
|
|
|
Data map[string]interface{} `json:"data,omitempty"`
|
2022-11-03 13:54:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type SerializedObject map[string]interface{}
|
|
|
|
|
|
|
|
|
|
type SerializedEdge map[string]interface{}
|
|
|
|
|
|
|
|
|
|
func DeserializeGraph(bytes []byte, g *Graph) error {
|
|
|
|
|
var sg *SerializedGraph
|
|
|
|
|
err := json.Unmarshal(bytes, &sg)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-07 22:41:57 +00:00
|
|
|
g.Data = sg.Data
|
2023-02-18 15:20:28 +00:00
|
|
|
var root Object
|
2024-04-16 00:31:27 +00:00
|
|
|
Convert(sg.Root, &root)
|
2023-02-18 15:20:28 +00:00
|
|
|
g.Root = &root
|
|
|
|
|
root.Graph = g
|
2023-09-25 22:13:46 +00:00
|
|
|
g.RootLevel = sg.RootLevel
|
2022-11-03 13:54:49 +00:00
|
|
|
|
|
|
|
|
idToObj := make(map[string]*Object)
|
|
|
|
|
idToObj[""] = g.Root
|
|
|
|
|
var objects []*Object
|
|
|
|
|
for _, so := range sg.Objects {
|
|
|
|
|
var o Object
|
2024-04-16 00:31:27 +00:00
|
|
|
if err := Convert(so, &o); err != nil {
|
2022-11-03 13:54:49 +00:00
|
|
|
return err
|
|
|
|
|
}
|
2023-09-25 22:45:11 +00:00
|
|
|
o.Graph = g
|
2022-11-03 13:54:49 +00:00
|
|
|
objects = append(objects, &o)
|
|
|
|
|
idToObj[so["AbsID"].(string)] = &o
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, so := range append(sg.Objects, sg.Root) {
|
|
|
|
|
if so["ChildrenArray"] != nil {
|
|
|
|
|
children := make(map[string]*Object)
|
|
|
|
|
var childrenArray []*Object
|
|
|
|
|
|
|
|
|
|
for _, id := range so["ChildrenArray"].([]interface{}) {
|
|
|
|
|
o := idToObj[id.(string)]
|
|
|
|
|
childrenArray = append(childrenArray, o)
|
2023-02-17 18:54:02 +00:00
|
|
|
children[strings.ToLower(o.ID)] = o
|
2022-11-03 13:54:49 +00:00
|
|
|
|
|
|
|
|
o.Parent = idToObj[so["AbsID"].(string)]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
idToObj[so["AbsID"].(string)].Children = children
|
|
|
|
|
idToObj[so["AbsID"].(string)].ChildrenArray = childrenArray
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var edges []*Edge
|
|
|
|
|
for _, se := range sg.Edges {
|
|
|
|
|
var e Edge
|
2024-04-16 00:31:27 +00:00
|
|
|
if err := Convert(se, &e); err != nil {
|
2022-11-03 13:54:49 +00:00
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if se["Src"] != nil {
|
|
|
|
|
e.Src = idToObj[se["Src"].(string)]
|
|
|
|
|
}
|
|
|
|
|
if se["Dst"] != nil {
|
|
|
|
|
e.Dst = idToObj[se["Dst"].(string)]
|
|
|
|
|
}
|
|
|
|
|
edges = append(edges, &e)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g.Objects = objects
|
|
|
|
|
g.Edges = edges
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func SerializeGraph(g *Graph) ([]byte, error) {
|
|
|
|
|
sg := SerializedGraph{}
|
|
|
|
|
|
|
|
|
|
root, err := toSerializedObject(g.Root)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
sg.Root = root
|
2023-09-25 22:13:46 +00:00
|
|
|
sg.RootLevel = g.RootLevel
|
2024-11-07 22:41:57 +00:00
|
|
|
sg.Data = g.Data
|
2022-11-03 13:54:49 +00:00
|
|
|
|
|
|
|
|
var sobjects []SerializedObject
|
|
|
|
|
for _, o := range g.Objects {
|
|
|
|
|
so, err := toSerializedObject(o)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
sobjects = append(sobjects, so)
|
|
|
|
|
}
|
|
|
|
|
sg.Objects = sobjects
|
|
|
|
|
|
|
|
|
|
var sedges []SerializedEdge
|
|
|
|
|
for _, e := range g.Edges {
|
2024-04-16 00:31:27 +00:00
|
|
|
se, err := ToSerializedEdge(e)
|
2022-11-03 13:54:49 +00:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
sedges = append(sedges, se)
|
|
|
|
|
}
|
|
|
|
|
sg.Edges = sedges
|
|
|
|
|
|
|
|
|
|
return json.Marshal(sg)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func toSerializedObject(o *Object) (SerializedObject, error) {
|
|
|
|
|
var so SerializedObject
|
2024-04-16 00:31:27 +00:00
|
|
|
if err := Convert(o, &so); err != nil {
|
2022-11-03 13:54:49 +00:00
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
so["AbsID"] = o.AbsID()
|
|
|
|
|
|
|
|
|
|
if len(o.ChildrenArray) > 0 {
|
|
|
|
|
var children []string
|
|
|
|
|
for _, c := range o.ChildrenArray {
|
|
|
|
|
children = append(children, c.AbsID())
|
|
|
|
|
}
|
|
|
|
|
so["ChildrenArray"] = children
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return so, nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-16 00:31:27 +00:00
|
|
|
func ToSerializedEdge(e *Edge) (SerializedEdge, error) {
|
2022-11-03 13:54:49 +00:00
|
|
|
var se SerializedEdge
|
2024-04-16 00:31:27 +00:00
|
|
|
if err := Convert(e, &se); err != nil {
|
2022-11-03 13:54:49 +00:00
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if e.Src != nil {
|
|
|
|
|
se["Src"] = go2.Pointer(e.Src.AbsID())
|
|
|
|
|
}
|
|
|
|
|
if e.Dst != nil {
|
|
|
|
|
se["Dst"] = go2.Pointer(e.Dst.AbsID())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return se, nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-16 00:31:27 +00:00
|
|
|
func Convert[T, Q any](from T, to *Q) error {
|
2022-11-03 13:54:49 +00:00
|
|
|
b, err := json.Marshal(from)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := json.Unmarshal(b, to); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2023-02-18 16:56:17 +00:00
|
|
|
|
|
|
|
|
func CompareSerializedGraph(g, other *Graph) error {
|
|
|
|
|
if len(g.Objects) != len(other.Objects) {
|
|
|
|
|
return fmt.Errorf("object count differs: g=%d, other=%d", len(g.Objects), len(other.Objects))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(g.Edges) != len(other.Edges) {
|
|
|
|
|
return fmt.Errorf("edge count differs: g=%d, other=%d", len(g.Edges), len(other.Edges))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := CompareSerializedObject(g.Root, other.Root); err != nil {
|
|
|
|
|
return fmt.Errorf("root differs: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for i := 0; i < len(g.Objects); i++ {
|
|
|
|
|
if err := CompareSerializedObject(g.Objects[i], other.Objects[i]); err != nil {
|
|
|
|
|
return fmt.Errorf(
|
|
|
|
|
"objects differ at %d [g=%s, other=%s]: %v",
|
|
|
|
|
i,
|
|
|
|
|
g.Objects[i].ID,
|
|
|
|
|
other.Objects[i].ID,
|
|
|
|
|
err,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for i := 0; i < len(g.Edges); i++ {
|
|
|
|
|
if err := CompareSerializedEdge(g.Edges[i], other.Edges[i]); err != nil {
|
|
|
|
|
return fmt.Errorf(
|
|
|
|
|
"edges differ at %d [g=%s, other=%s]: %v",
|
|
|
|
|
i,
|
|
|
|
|
g.Edges[i].AbsID(),
|
|
|
|
|
other.Edges[i].AbsID(),
|
|
|
|
|
err,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func CompareSerializedObject(obj, other *Object) error {
|
|
|
|
|
if obj != nil && other == nil {
|
|
|
|
|
return fmt.Errorf("other is nil")
|
|
|
|
|
} else if obj == nil && other != nil {
|
|
|
|
|
return fmt.Errorf("obj is nil")
|
|
|
|
|
} else if obj == nil {
|
|
|
|
|
// both are nil
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if obj.ID != other.ID {
|
|
|
|
|
return fmt.Errorf("ids differ: obj=%s, other=%s", obj.ID, other.ID)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if obj.AbsID() != other.AbsID() {
|
|
|
|
|
return fmt.Errorf("absolute ids differ: obj=%s, other=%s", obj.AbsID(), other.AbsID())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if obj.Box != nil && other.Box == nil {
|
|
|
|
|
return fmt.Errorf("other should have a box")
|
|
|
|
|
} else if obj.Box == nil && other.Box != nil {
|
|
|
|
|
return fmt.Errorf("other should not have a box")
|
|
|
|
|
} else if obj.Box != nil {
|
|
|
|
|
if obj.Width != other.Width {
|
|
|
|
|
return fmt.Errorf("widths differ: obj=%f, other=%f", obj.Width, other.Width)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if obj.Height != other.Height {
|
|
|
|
|
return fmt.Errorf("heights differ: obj=%f, other=%f", obj.Height, other.Height)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if obj.Parent != nil && other.Parent == nil {
|
|
|
|
|
return fmt.Errorf("other should have a parent")
|
|
|
|
|
} else if obj.Parent == nil && other.Parent != nil {
|
|
|
|
|
return fmt.Errorf("other should not have a parent")
|
|
|
|
|
} else if obj.Parent != nil && obj.Parent.ID != other.Parent.ID {
|
|
|
|
|
return fmt.Errorf("parent differs: obj=%s, other=%s", obj.Parent.ID, other.Parent.ID)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(obj.Children) != len(other.Children) {
|
|
|
|
|
return fmt.Errorf("children count differs: obj=%d, other=%d", len(obj.Children), len(other.Children))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for childID, objChild := range obj.Children {
|
|
|
|
|
if otherChild, exists := other.Children[childID]; exists {
|
|
|
|
|
if err := CompareSerializedObject(objChild, otherChild); err != nil {
|
|
|
|
|
return fmt.Errorf("children differ at key %s: %v", childID, err)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
return fmt.Errorf("child %s does not exist in other", childID)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(obj.ChildrenArray) != len(other.ChildrenArray) {
|
|
|
|
|
return fmt.Errorf("childrenArray count differs: obj=%d, other=%d", len(obj.ChildrenArray), len(other.ChildrenArray))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for i := 0; i < len(obj.ChildrenArray); i++ {
|
|
|
|
|
if err := CompareSerializedObject(obj.ChildrenArray[i], other.ChildrenArray[i]); err != nil {
|
|
|
|
|
return fmt.Errorf("childrenArray differs at %d: %v", i, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-14 03:04:55 +00:00
|
|
|
if d2target.IsShape(obj.Shape.Value) != d2target.IsShape(other.Shape.Value) {
|
|
|
|
|
return fmt.Errorf(
|
|
|
|
|
"shapes differ: obj=%s, other=%s",
|
|
|
|
|
obj.Shape.Value,
|
|
|
|
|
other.Shape.Value,
|
|
|
|
|
)
|
|
|
|
|
}
|
2023-02-18 16:56:17 +00:00
|
|
|
|
2023-04-14 03:04:55 +00:00
|
|
|
if obj.Icon == nil && other.Icon != nil {
|
|
|
|
|
return fmt.Errorf("other does not have an icon")
|
|
|
|
|
} else if obj.Icon != nil && other.Icon == nil {
|
|
|
|
|
return fmt.Errorf("obj does not have an icon")
|
|
|
|
|
}
|
2023-02-18 16:56:17 +00:00
|
|
|
|
2023-04-14 03:04:55 +00:00
|
|
|
if obj.Direction.Value != other.Direction.Value {
|
|
|
|
|
return fmt.Errorf(
|
|
|
|
|
"directions differ: obj=%s, other=%s",
|
|
|
|
|
obj.Direction.Value,
|
|
|
|
|
other.Direction.Value,
|
|
|
|
|
)
|
2023-02-18 16:56:17 +00:00
|
|
|
}
|
|
|
|
|
|
2023-04-14 03:04:55 +00:00
|
|
|
if obj.Label.Value != other.Label.Value {
|
|
|
|
|
return fmt.Errorf(
|
|
|
|
|
"labels differ: obj=%s, other=%s",
|
|
|
|
|
obj.Label.Value,
|
|
|
|
|
other.Label.Value,
|
|
|
|
|
)
|
2023-02-18 16:56:17 +00:00
|
|
|
}
|
|
|
|
|
|
2023-04-14 03:04:55 +00:00
|
|
|
if obj.NearKey != nil {
|
|
|
|
|
if other.NearKey == nil {
|
|
|
|
|
return fmt.Errorf("other does not have near")
|
|
|
|
|
}
|
|
|
|
|
objKey := strings.Join(Key(obj.NearKey), ".")
|
|
|
|
|
deserKey := strings.Join(Key(other.NearKey), ".")
|
|
|
|
|
if objKey != deserKey {
|
2023-02-18 16:56:17 +00:00
|
|
|
return fmt.Errorf(
|
2023-04-14 03:04:55 +00:00
|
|
|
"near differs: obj=%s, other=%s",
|
|
|
|
|
objKey,
|
|
|
|
|
deserKey,
|
2023-02-18 16:56:17 +00:00
|
|
|
)
|
|
|
|
|
}
|
2023-04-14 03:04:55 +00:00
|
|
|
} else if other.NearKey != nil {
|
|
|
|
|
return fmt.Errorf("other should not have near")
|
2023-02-18 16:56:17 +00:00
|
|
|
}
|
|
|
|
|
|
2023-04-13 22:20:12 +00:00
|
|
|
if obj.LabelDimensions.Width != other.LabelDimensions.Width {
|
|
|
|
|
return fmt.Errorf(
|
|
|
|
|
"label width differs: obj=%d, other=%d",
|
|
|
|
|
obj.LabelDimensions.Width,
|
|
|
|
|
other.LabelDimensions.Width,
|
|
|
|
|
)
|
2023-02-18 16:56:17 +00:00
|
|
|
}
|
|
|
|
|
|
2023-04-13 22:20:12 +00:00
|
|
|
if obj.LabelDimensions.Height != other.LabelDimensions.Height {
|
|
|
|
|
return fmt.Errorf(
|
|
|
|
|
"label height differs: obj=%d, other=%d",
|
|
|
|
|
obj.LabelDimensions.Height,
|
|
|
|
|
other.LabelDimensions.Height,
|
|
|
|
|
)
|
2023-02-18 16:56:17 +00:00
|
|
|
}
|
|
|
|
|
|
2023-04-14 03:04:55 +00:00
|
|
|
if obj.SQLTable == nil && other.SQLTable != nil {
|
|
|
|
|
return fmt.Errorf("other is not a sql table")
|
|
|
|
|
} else if obj.SQLTable != nil && other.SQLTable == nil {
|
|
|
|
|
return fmt.Errorf("obj is not a sql table")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if obj.SQLTable != nil {
|
|
|
|
|
if len(obj.SQLTable.Columns) != len(other.SQLTable.Columns) {
|
|
|
|
|
return fmt.Errorf(
|
|
|
|
|
"table columns count differ: obj=%d, other=%d",
|
|
|
|
|
len(obj.SQLTable.Columns),
|
|
|
|
|
len(other.SQLTable.Columns),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-18 16:56:17 +00:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func CompareSerializedEdge(edge, other *Edge) error {
|
|
|
|
|
if edge.AbsID() != other.AbsID() {
|
|
|
|
|
return fmt.Errorf(
|
|
|
|
|
"absolute ids differ: edge=%s, other=%s",
|
|
|
|
|
edge.AbsID(),
|
|
|
|
|
other.AbsID(),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if edge.Src.AbsID() != other.Src.AbsID() {
|
|
|
|
|
return fmt.Errorf(
|
|
|
|
|
"sources differ: edge=%s, other=%s",
|
|
|
|
|
edge.Src.AbsID(),
|
|
|
|
|
other.Src.AbsID(),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if edge.Dst.AbsID() != other.Dst.AbsID() {
|
|
|
|
|
return fmt.Errorf(
|
|
|
|
|
"targets differ: edge=%s, other=%s",
|
|
|
|
|
edge.Dst.AbsID(),
|
|
|
|
|
other.Dst.AbsID(),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if edge.SrcArrow != other.SrcArrow {
|
|
|
|
|
return fmt.Errorf(
|
|
|
|
|
"source arrows differ: edge=%t, other=%t",
|
|
|
|
|
edge.SrcArrow,
|
|
|
|
|
other.SrcArrow,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if edge.DstArrow != other.DstArrow {
|
|
|
|
|
return fmt.Errorf(
|
|
|
|
|
"target arrows differ: edge=%t, other=%t",
|
|
|
|
|
edge.DstArrow,
|
|
|
|
|
other.DstArrow,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-14 03:04:55 +00:00
|
|
|
if edge.Label.Value != other.Label.Value {
|
2023-02-18 16:56:17 +00:00
|
|
|
return fmt.Errorf(
|
|
|
|
|
"labels differ: edge=%s, other=%s",
|
2023-04-14 03:04:55 +00:00
|
|
|
edge.Label.Value,
|
|
|
|
|
other.Label.Value,
|
2023-02-18 16:56:17 +00:00
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if edge.LabelDimensions.Width != other.LabelDimensions.Width {
|
|
|
|
|
return fmt.Errorf(
|
|
|
|
|
"label width differs: edge=%d, other=%d",
|
|
|
|
|
edge.LabelDimensions.Width,
|
|
|
|
|
other.LabelDimensions.Width,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if edge.LabelDimensions.Height != other.LabelDimensions.Height {
|
|
|
|
|
return fmt.Errorf(
|
2023-04-13 17:19:58 +00:00
|
|
|
"label height differs: edge=%d, other=%d",
|
2023-02-18 16:56:17 +00:00
|
|
|
edge.LabelDimensions.Height,
|
|
|
|
|
other.LabelDimensions.Height,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if edge.SrcTableColumnIndex != nil && other.SrcTableColumnIndex == nil {
|
|
|
|
|
return fmt.Errorf("other should have src column index")
|
|
|
|
|
} else if other.SrcTableColumnIndex != nil && edge.SrcTableColumnIndex == nil {
|
|
|
|
|
return fmt.Errorf("other should not have src column index")
|
|
|
|
|
} else if other.SrcTableColumnIndex != nil {
|
|
|
|
|
edgeColumn := *edge.SrcTableColumnIndex
|
|
|
|
|
otherColumn := *other.SrcTableColumnIndex
|
|
|
|
|
if edgeColumn != otherColumn {
|
|
|
|
|
return fmt.Errorf("src column differs: edge=%d, other=%d", edgeColumn, otherColumn)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if edge.DstTableColumnIndex != nil && other.DstTableColumnIndex == nil {
|
|
|
|
|
return fmt.Errorf("other should have dst column index")
|
|
|
|
|
} else if other.DstTableColumnIndex != nil && edge.DstTableColumnIndex == nil {
|
|
|
|
|
return fmt.Errorf("other should not have dst column index")
|
|
|
|
|
} else if other.DstTableColumnIndex != nil {
|
|
|
|
|
edgeColumn := *edge.DstTableColumnIndex
|
|
|
|
|
otherColumn := *other.DstTableColumnIndex
|
|
|
|
|
if edgeColumn != otherColumn {
|
|
|
|
|
return fmt.Errorf("dst column differs: edge=%d, other=%d", edgeColumn, otherColumn)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|