diff options
Diffstat (limited to 'bug/operation.go')
-rw-r--r-- | bug/operation.go | 77 |
1 files changed, 50 insertions, 27 deletions
diff --git a/bug/operation.go b/bug/operation.go index a408e167..91b77e56 100644 --- a/bug/operation.go +++ b/bug/operation.go @@ -1,11 +1,13 @@ package bug import ( - "github.com/MichaelMure/git-bug/util/git" - "github.com/pkg/errors" - + "crypto/sha256" + "encoding/json" "fmt" "time" + + "github.com/MichaelMure/git-bug/util/git" + "github.com/pkg/errors" ) // OperationType is an operation type identifier @@ -18,22 +20,23 @@ const ( AddCommentOp SetStatusOp LabelChangeOp + EditCommentOp ) // Operation define the interface to fulfill for an edit operation of a Bug type Operation interface { - // OpType return the type of operation - OpType() OperationType + // base return the OpBase of the Operation, for package internal use + base() *OpBase + // Hash return the hash of the operation, to be used for back references + Hash() (git.Hash, error) // Time return the time when the operation was added Time() time.Time // GetUnixTime return the unix timestamp when the operation was added GetUnixTime() int64 - // GetAuthor return the author of the operation - GetAuthor() Person // GetFiles return the files needed by this operation GetFiles() []git.Hash // Apply the operation to a Snapshot to create the final state - Apply(snapshot Snapshot) Snapshot + Apply(snapshot *Snapshot) // Validate check if the operation is valid (ex: a title is a single line) Validate() error // SetMetadata store arbitrary metadata about the operation @@ -42,16 +45,42 @@ type Operation interface { GetMetadata(key string) (string, bool) } +func hashRaw(data []byte) git.Hash { + hasher := sha256.New() + // Write can't fail + _, _ = hasher.Write(data) + return git.Hash(fmt.Sprintf("%x", hasher.Sum(nil))) +} + +// hash compute the hash of the serialized operation +func hashOperation(op Operation) (git.Hash, error) { + base := op.base() + + if base.hash != "" { + return base.hash, nil + } + + data, err := json.Marshal(op) + if err != nil { + return "", err + } + + base.hash = hashRaw(data) + + return base.hash, nil +} + // OpBase implement the common code for all operations type OpBase struct { - OperationType OperationType `json:"type"` - Author Person `json:"author"` - UnixTime int64 `json:"timestamp"` + OperationType OperationType `json:"type"` + Author Person `json:"author"` + UnixTime int64 `json:"timestamp"` + hash git.Hash Metadata map[string]string `json:"metadata,omitempty"` } -// NewOpBase is the constructor for an OpBase -func NewOpBase(opType OperationType, author Person, unixTime int64) *OpBase { +// newOpBase is the constructor for an OpBase +func newOpBase(opType OperationType, author Person, unixTime int64) *OpBase { return &OpBase{ OperationType: opType, Author: author, @@ -59,11 +88,6 @@ func NewOpBase(opType OperationType, author Person, unixTime int64) *OpBase { } } -// OpType return the type of operation -func (op *OpBase) OpType() OperationType { - return op.OperationType -} - // Time return the time when the operation was added func (op *OpBase) Time() time.Time { return time.Unix(op.UnixTime, 0) @@ -74,27 +98,26 @@ func (op *OpBase) GetUnixTime() int64 { return op.UnixTime } -// GetAuthor return the author of the operation -func (op *OpBase) GetAuthor() Person { - return op.Author -} - // GetFiles return the files needed by this operation func (op *OpBase) GetFiles() []git.Hash { return nil } // Validate check the OpBase for errors -func OpBaseValidate(op Operation, opType OperationType) error { - if op.OpType() != opType { - return fmt.Errorf("incorrect operation type (expected: %v, actual: %v)", opType, op.OpType()) +func opBaseValidate(op Operation, opType OperationType) error { + if op.base().OperationType != opType { + return fmt.Errorf("incorrect operation type (expected: %v, actual: %v)", opType, op.base().OperationType) + } + + if _, err := op.Hash(); err != nil { + return errors.Wrap(err, "op is not serializable") } if op.GetUnixTime() == 0 { return fmt.Errorf("time not set") } - if err := op.GetAuthor().Validate(); err != nil { + if err := op.base().Author.Validate(); err != nil { return errors.Wrap(err, "author") } |