operation_pack.go

  1package bug
  2
  3import (
  4	"encoding/json"
  5	"fmt"
  6	"reflect"
  7
  8	"github.com/MichaelMure/git-bug/repository"
  9	"github.com/MichaelMure/git-bug/util/git"
 10)
 11
 12const formatVersion = 1
 13
 14// OperationPack represent an ordered set of operation to apply
 15// to a Bug. These operations are stored in a single Git commit.
 16//
 17// These commits will be linked together in a linear chain of commits
 18// inside Git to form the complete ordered chain of operation to
 19// apply to get the final state of the Bug
 20type OperationPack struct {
 21	Operations []Operation
 22
 23	// Private field so not serialized by gob
 24	commitHash git.Hash
 25}
 26
 27var operations map[OperationType]reflect.Type
 28
 29func Register(t OperationType, op interface{}) {
 30	if operations == nil {
 31		operations = make(map[OperationType]reflect.Type)
 32	}
 33	operations[t] = reflect.TypeOf(op)
 34}
 35
 36func (opp *OperationPack) MarshalJSON() ([]byte, error) {
 37	return json.Marshal(struct {
 38		Version    uint        `json:"version"`
 39		Operations []Operation `json:"ops"`
 40	}{
 41		Version:    formatVersion,
 42		Operations: opp.Operations,
 43	})
 44}
 45
 46func (opp *OperationPack) UnmarshalJSON(data []byte) error {
 47	aux := struct {
 48		Version    uint              `json:"version"`
 49		Operations []json.RawMessage `json:"ops"`
 50	}{}
 51
 52	if err := json.Unmarshal(data, &aux); err != nil {
 53		return err
 54	}
 55
 56	if aux.Version != formatVersion {
 57		return fmt.Errorf("unknown format version %v", aux.Version)
 58	}
 59
 60	for _, raw := range aux.Operations {
 61		var t struct {
 62			OperationType OperationType `json:"type"`
 63		}
 64
 65		if err := json.Unmarshal(raw, &t); err != nil {
 66			return err
 67		}
 68
 69		opType, ok := operations[t.OperationType]
 70		if !ok {
 71			return fmt.Errorf("unknown operation type %v", t.OperationType)
 72		}
 73
 74		op := reflect.New(opType).Interface()
 75
 76		if err := json.Unmarshal(raw, op); err != nil {
 77			return err
 78		}
 79
 80		deref := reflect.ValueOf(op).Elem().Interface()
 81
 82		opp.Operations = append(opp.Operations, deref.(Operation))
 83	}
 84
 85	return nil
 86}
 87
 88// Append a new operation to the pack
 89func (opp *OperationPack) Append(op Operation) {
 90	opp.Operations = append(opp.Operations, op)
 91}
 92
 93// IsEmpty tell if the OperationPack is empty
 94func (opp *OperationPack) IsEmpty() bool {
 95	return len(opp.Operations) == 0
 96}
 97
 98// IsValid tell if the OperationPack is considered valid
 99func (opp *OperationPack) IsValid() bool {
100	return !opp.IsEmpty()
101}
102
103// Write will serialize and store the OperationPack as a git blob and return
104// its hash
105func (opp *OperationPack) Write(repo repository.Repo) (git.Hash, error) {
106	data, err := json.Marshal(opp)
107
108	if err != nil {
109		return "", err
110	}
111
112	hash, err := repo.StoreData(data)
113
114	if err != nil {
115		return "", err
116	}
117
118	return hash, nil
119}
120
121// Make a deep copy
122func (opp *OperationPack) Clone() OperationPack {
123
124	clone := OperationPack{
125		Operations: make([]Operation, len(opp.Operations)),
126		commitHash: opp.commitHash,
127	}
128
129	for i, op := range opp.Operations {
130		clone.Operations[i] = op
131	}
132
133	return clone
134}