From 60fcfcdcb0e89741528cfc99a94a48f204d48e6b Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Wed, 12 Sep 2018 16:57:04 +0200 Subject: bug: change the OperationPack serialization format for Json See https://github.com/MichaelMure/git-bug/issues/5 for the details of this choice --- bug/operation_pack.go | 76 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 55 insertions(+), 21 deletions(-) (limited to 'bug/operation_pack.go') diff --git a/bug/operation_pack.go b/bug/operation_pack.go index 8568fa16..fe26952f 100644 --- a/bug/operation_pack.go +++ b/bug/operation_pack.go @@ -1,13 +1,16 @@ package bug import ( - "bytes" - "encoding/gob" + "encoding/json" + "fmt" + "reflect" "github.com/MichaelMure/git-bug/repository" "github.com/MichaelMure/git-bug/util/git" ) +const formatVersion = 1 + // OperationPack represent an ordered set of operation to apply // to a Bug. These operations are stored in a single Git commit. // @@ -21,34 +24,65 @@ type OperationPack struct { commitHash git.Hash } -// ParseOperationPack will deserialize an OperationPack from raw bytes -func ParseOperationPack(data []byte) (*OperationPack, error) { - reader := bytes.NewReader(data) - decoder := gob.NewDecoder(reader) +var operations map[OperationType]reflect.Type - var opp OperationPack +func Register(t OperationType, op interface{}) { + if operations == nil { + operations = make(map[OperationType]reflect.Type) + } + operations[t] = reflect.TypeOf(op) +} - err := decoder.Decode(&opp) +func (opp *OperationPack) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Version uint `json:"version"` + Operations []Operation `json:"ops"` + }{ + Version: formatVersion, + Operations: opp.Operations, + }) +} - if err != nil { - return nil, err +func (opp *OperationPack) UnmarshalJSON(data []byte) error { + aux := struct { + Version uint `json:"version"` + Operations []json.RawMessage `json:"ops"` + }{} + + if err := json.Unmarshal(data, &aux); err != nil { + return err } - return &opp, nil -} + if aux.Version != formatVersion { + return fmt.Errorf("unknown format version %v", aux.Version) + } -// Serialize will serialise an OperationPack into raw bytes -func (opp *OperationPack) Serialize() ([]byte, error) { - var data bytes.Buffer + for _, raw := range aux.Operations { + var t struct { + OperationType OperationType `json:"type"` + } - encoder := gob.NewEncoder(&data) - err := encoder.Encode(*opp) + if err := json.Unmarshal(raw, &t); err != nil { + return err + } - if err != nil { - return nil, err + opType, ok := operations[t.OperationType] + if !ok { + return fmt.Errorf("unknown operation type %v", t.OperationType) + } + + op := reflect.New(opType).Interface() + + if err := json.Unmarshal(raw, op); err != nil { + return err + } + + deref := reflect.ValueOf(op).Elem().Interface() + + opp.Operations = append(opp.Operations, deref.(Operation)) } - return data.Bytes(), nil + return nil } // Append a new operation to the pack @@ -69,7 +103,7 @@ func (opp *OperationPack) IsValid() bool { // Write will serialize and store the OperationPack as a git blob and return // its hash func (opp *OperationPack) Write(repo repository.Repo) (git.Hash, error) { - data, err := opp.Serialize() + data, err := json.Marshal(opp) if err != nil { return "", err -- cgit