operation_pack.go

  1package bug
  2
  3import (
  4	"encoding/json"
  5	"fmt"
  6
  7	"github.com/MichaelMure/git-bug/repository"
  8	"github.com/MichaelMure/git-bug/util/git"
  9	"github.com/pkg/errors"
 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
 27func (opp *OperationPack) MarshalJSON() ([]byte, error) {
 28	return json.Marshal(struct {
 29		Version    uint        `json:"version"`
 30		Operations []Operation `json:"ops"`
 31	}{
 32		Version:    formatVersion,
 33		Operations: opp.Operations,
 34	})
 35}
 36
 37func (opp *OperationPack) UnmarshalJSON(data []byte) error {
 38	aux := struct {
 39		Version    uint              `json:"version"`
 40		Operations []json.RawMessage `json:"ops"`
 41	}{}
 42
 43	if err := json.Unmarshal(data, &aux); err != nil {
 44		return err
 45	}
 46
 47	if aux.Version != formatVersion {
 48		return fmt.Errorf("unknown format version %v", aux.Version)
 49	}
 50
 51	for _, raw := range aux.Operations {
 52		var t struct {
 53			OperationType OperationType `json:"type"`
 54		}
 55
 56		if err := json.Unmarshal(raw, &t); err != nil {
 57			return err
 58		}
 59
 60		op, err := opp.unmarshalOp(raw, t.OperationType)
 61		if err != nil {
 62			return err
 63		}
 64
 65		opp.Operations = append(opp.Operations, op)
 66	}
 67
 68	return nil
 69}
 70
 71func (opp *OperationPack) unmarshalOp(raw []byte, _type OperationType) (Operation, error) {
 72	switch _type {
 73	case CreateOp:
 74		op := CreateOperation{}
 75		err := json.Unmarshal(raw, &op)
 76		return op, err
 77	case SetTitleOp:
 78		op := SetTitleOperation{}
 79		err := json.Unmarshal(raw, &op)
 80		return op, err
 81	case AddCommentOp:
 82		op := AddCommentOperation{}
 83		err := json.Unmarshal(raw, &op)
 84		return op, err
 85	case SetStatusOp:
 86		op := SetStatusOperation{}
 87		err := json.Unmarshal(raw, &op)
 88		return op, err
 89	case LabelChangeOp:
 90		op := LabelChangeOperation{}
 91		err := json.Unmarshal(raw, &op)
 92		return op, err
 93	default:
 94		return nil, fmt.Errorf("unknown operation type %v", _type)
 95	}
 96}
 97
 98// Append a new operation to the pack
 99func (opp *OperationPack) Append(op Operation) {
100	opp.Operations = append(opp.Operations, op)
101}
102
103// IsEmpty tell if the OperationPack is empty
104func (opp *OperationPack) IsEmpty() bool {
105	return len(opp.Operations) == 0
106}
107
108// IsValid tell if the OperationPack is considered valid
109func (opp *OperationPack) Validate() error {
110	if opp.IsEmpty() {
111		return fmt.Errorf("empty")
112	}
113
114	for _, op := range opp.Operations {
115		if err := op.Validate(); err != nil {
116			return errors.Wrap(err, "op")
117		}
118	}
119
120	return nil
121}
122
123// Write will serialize and store the OperationPack as a git blob and return
124// its hash
125func (opp *OperationPack) Write(repo repository.Repo) (git.Hash, error) {
126	data, err := json.Marshal(opp)
127
128	if err != nil {
129		return "", err
130	}
131
132	hash, err := repo.StoreData(data)
133
134	if err != nil {
135		return "", err
136	}
137
138	return hash, nil
139}
140
141// Make a deep copy
142func (opp *OperationPack) Clone() OperationPack {
143
144	clone := OperationPack{
145		Operations: make([]Operation, len(opp.Operations)),
146		commitHash: opp.commitHash,
147	}
148
149	for i, op := range opp.Operations {
150		clone.Operations[i] = op
151	}
152
153	return clone
154}