187 lines
4.3 KiB
Go
187 lines
4.3 KiB
Go
package gg
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
)
|
|
|
|
type openEdgeJSON struct {
|
|
From vertexJSON `json:"from"`
|
|
ValueID string `json:"valueID"`
|
|
}
|
|
|
|
type vertexJSON struct {
|
|
Type VertexType `json:"type"`
|
|
ValueID string `json:"valueID,omitempty"`
|
|
In []openEdgeJSON `json:"in"`
|
|
}
|
|
|
|
type graphJSON struct {
|
|
Values map[string]json.RawMessage `json:"values"`
|
|
ValueVertices []vertexJSON `json:"valueVertices"`
|
|
}
|
|
|
|
// MarshalJSON implements the json.Marshaler interface for a Graph. All Values
|
|
// in the Graph will have json.Marshal called on them as-is in order to marshal
|
|
// them.
|
|
func (g *Graph) MarshalJSON() ([]byte, error) {
|
|
gJ := graphJSON{
|
|
Values: map[string]json.RawMessage{},
|
|
ValueVertices: make([]vertexJSON, 0, len(g.vM)),
|
|
}
|
|
|
|
withVal := func(val Value) (string, error) {
|
|
if _, ok := gJ.Values[val.ID]; !ok {
|
|
valJ, err := json.Marshal(val.V)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
gJ.Values[val.ID] = json.RawMessage(valJ)
|
|
}
|
|
return val.ID, nil
|
|
}
|
|
|
|
// two locally defined, mutually recursive functions. This kind of thing
|
|
// could probably be abstracted out, I feel like it happens frequently with
|
|
// graph code.
|
|
var mkIns func([]OpenEdge) ([]openEdgeJSON, error)
|
|
var mkVert func(vertex) (vertexJSON, error)
|
|
|
|
mkIns = func(in []OpenEdge) ([]openEdgeJSON, error) {
|
|
inJ := make([]openEdgeJSON, len(in))
|
|
for i := range in {
|
|
valID, err := withVal(in[i].val)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
vJ, err := mkVert(in[i].fromV)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
inJ[i] = openEdgeJSON{From: vJ, ValueID: valID}
|
|
}
|
|
return inJ, nil
|
|
}
|
|
|
|
mkVert = func(v vertex) (vertexJSON, error) {
|
|
ins, err := mkIns(v.in)
|
|
if err != nil {
|
|
return vertexJSON{}, err
|
|
}
|
|
vJ := vertexJSON{
|
|
Type: v.VertexType,
|
|
In: ins,
|
|
}
|
|
if v.VertexType == ValueVertex {
|
|
valID, err := withVal(v.val)
|
|
if err != nil {
|
|
return vJ, err
|
|
}
|
|
vJ.ValueID = valID
|
|
}
|
|
return vJ, nil
|
|
}
|
|
|
|
for _, v := range g.vM {
|
|
vJ, err := mkVert(v)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
gJ.ValueVertices = append(gJ.ValueVertices, vJ)
|
|
}
|
|
|
|
return json.Marshal(gJ)
|
|
}
|
|
|
|
type jsonUnmarshaler struct {
|
|
g *Graph
|
|
fn func(json.RawMessage) (interface{}, error)
|
|
}
|
|
|
|
// JSONUnmarshaler returns a json.Unmarshaler instance which, when used, will
|
|
// unmarshal a json string into the Graph instance being called on here.
|
|
//
|
|
// The passed in function is used to unmarshal Values (used in both ValueVertex
|
|
// vertices and edges) from json strings into go values. The returned inteface{}
|
|
// should have already had the unmarshal from the given json string performed on
|
|
// it.
|
|
//
|
|
// The json.Unmarshaler returned can be used many times, but will reset the
|
|
// Graph completely before each use.
|
|
func (g *Graph) JSONUnmarshaler(fn func(json.RawMessage) (interface{}, error)) json.Unmarshaler {
|
|
return jsonUnmarshaler{g: g, fn: fn}
|
|
}
|
|
|
|
func (jm jsonUnmarshaler) UnmarshalJSON(b []byte) error {
|
|
*(jm.g) = Graph{}
|
|
jm.g.vM = map[string]vertex{}
|
|
|
|
var gJ graphJSON
|
|
if err := json.Unmarshal(b, &gJ); err != nil {
|
|
return err
|
|
}
|
|
|
|
vals := map[string]Value{}
|
|
getVal := func(valID string) (Value, error) {
|
|
if val, ok := vals[valID]; ok {
|
|
return val, nil
|
|
}
|
|
|
|
j, ok := gJ.Values[valID]
|
|
if !ok {
|
|
return Value{}, fmt.Errorf("unmarshaling malformed graph, value with ID %q not defined", valID)
|
|
}
|
|
|
|
V, err := jm.fn(j)
|
|
if err != nil {
|
|
return Value{}, err
|
|
}
|
|
|
|
val := Value{ID: valID, V: V}
|
|
vals[valID] = val
|
|
return val, nil
|
|
}
|
|
|
|
var mkIns func([]openEdgeJSON) ([]OpenEdge, error)
|
|
var mkVert func(vertexJSON) (vertex, error)
|
|
|
|
mkIns = func(inJ []openEdgeJSON) ([]OpenEdge, error) {
|
|
in := make([]OpenEdge, len(inJ))
|
|
for i := range inJ {
|
|
val, err := getVal(inJ[i].ValueID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
v, err := mkVert(inJ[i].From)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
in[i] = OpenEdge{fromV: v, val: val}
|
|
}
|
|
return in, nil
|
|
}
|
|
|
|
mkVert = func(vJ vertexJSON) (vertex, error) {
|
|
ins, err := mkIns(vJ.In)
|
|
if err != nil {
|
|
return vertex{}, err
|
|
}
|
|
var val Value
|
|
if vJ.Type == ValueVertex {
|
|
if val, err = getVal(vJ.ValueID); err != nil {
|
|
return vertex{}, err
|
|
}
|
|
}
|
|
return mkVertex(vJ.Type, val, ins), nil
|
|
}
|
|
|
|
for _, v := range gJ.ValueVertices {
|
|
v, err := mkVert(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
jm.g.vM[v.id] = v
|
|
}
|
|
return nil
|
|
}
|