aboutsummaryrefslogblamecommitdiffstats
path: root/bug/operation_pack.go
blob: 5f3e9da8a10881eb846796fe339d2cdab2ffb7c6 (plain) (tree)
1
2
3
4
5
6
7
8
9

           
        

                       
 
                                                   
                                                 
                               

 

                       






                                                                     
                              
 
                                          
                           

 








                                                         
 







                                                             

         


                                                                           
 



                                                                 
 


                                                               
 
                                                             

                                                                


                                  


                                                    
                                                           

         
                  

 

                                                                                           



                                               
                      
                                        

                                               

                                             

                                               

                                             

                                               

                                      

                                               

                                             

                                               





                                               






                                                                          




                                                   
                                             



                                          
                                                        











                                                     
 
 

                                                                            
                                                                                





                                                               

                                                                               
                                                            




                                      
                                      












                                         














                                                                   
package bug

import (
	"encoding/json"
	"fmt"

	"github.com/MichaelMure/git-bug/repository"
	"github.com/MichaelMure/git-bug/util/git"
	"github.com/pkg/errors"
)

const formatVersion = 1

// OperationPack represent an ordered set of operation to apply
// to a Bug. These operations are stored in a single Git commit.
//
// These commits will be linked together in a linear chain of commits
// inside Git to form the complete ordered chain of operation to
// apply to get the final state of the Bug
type OperationPack struct {
	Operations []Operation

	// Private field so not serialized
	commitHash git.Hash
}

func (opp *OperationPack) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Version    uint        `json:"version"`
		Operations []Operation `json:"ops"`
	}{
		Version:    formatVersion,
		Operations: opp.Operations,
	})
}

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
	}

	if aux.Version != formatVersion {
		return fmt.Errorf("unknown format version %v", aux.Version)
	}

	for _, raw := range aux.Operations {
		var t struct {
			OperationType OperationType `json:"type"`
		}

		if err := json.Unmarshal(raw, &t); err != nil {
			return err
		}

		// delegate to specialized unmarshal function
		op, err := opp.unmarshalOp(raw, t.OperationType)
		if err != nil {
			return err
		}

		// Compute the hash of the operation
		op.base().hash = hashRaw(raw)

		opp.Operations = append(opp.Operations, op)
	}

	return nil
}

func (opp *OperationPack) unmarshalOp(raw []byte, _type OperationType) (Operation, error) {
	switch _type {
	case AddCommentOp:
		op := &AddCommentOperation{}
		err := json.Unmarshal(raw, &op)
		return op, err
	case CreateOp:
		op := &CreateOperation{}
		err := json.Unmarshal(raw, &op)
		return op, err
	case EditCommentOp:
		op := &EditCommentOperation{}
		err := json.Unmarshal(raw, &op)
		return op, err
	case LabelChangeOp:
		op := &LabelChangeOperation{}
		err := json.Unmarshal(raw, &op)
		return op, err
	case NoOpOp:
		op := &NoOpOperation{}
		err := json.Unmarshal(raw, &op)
		return op, err
	case SetMetadataOp:
		op := &SetMetadataOperation{}
		err := json.Unmarshal(raw, &op)
		return op, err
	case SetStatusOp:
		op := &SetStatusOperation{}
		err := json.Unmarshal(raw, &op)
		return op, err
	case SetTitleOp:
		op := &SetTitleOperation{}
		err := json.Unmarshal(raw, &op)
		return op, err
	default:
		return nil, fmt.Errorf("unknown operation type %v", _type)
	}
}

// Append a new operation to the pack
func (opp *OperationPack) Append(op Operation) {
	opp.Operations = append(opp.Operations, op)
}

// IsEmpty tell if the OperationPack is empty
func (opp *OperationPack) IsEmpty() bool {
	return len(opp.Operations) == 0
}

// IsValid tell if the OperationPack is considered valid
func (opp *OperationPack) Validate() error {
	if opp.IsEmpty() {
		return fmt.Errorf("empty")
	}

	for _, op := range opp.Operations {
		if err := op.Validate(); err != nil {
			return errors.Wrap(err, "op")
		}
	}

	return nil
}

// Write will serialize and store the OperationPack as a git blob and return
// its hash
func (opp *OperationPack) Write(repo repository.ClockedRepo) (git.Hash, error) {
	// make sure we don't write invalid data
	err := opp.Validate()
	if err != nil {
		return "", errors.Wrap(err, "validation error")
	}

	// First, make sure that all the identities are properly Commit as well
	for _, op := range opp.Operations {
		err := op.base().Author.CommitAsNeeded(repo)
		if err != nil {
			return "", err
		}
	}

	data, err := json.Marshal(opp)

	if err != nil {
		return "", err
	}

	hash, err := repo.StoreData(data)

	if err != nil {
		return "", err
	}

	return hash, nil
}

// Make a deep copy
func (opp *OperationPack) Clone() OperationPack {

	clone := OperationPack{
		Operations: make([]Operation, len(opp.Operations)),
		commitHash: opp.commitHash,
	}

	for i, op := range opp.Operations {
		clone.Operations[i] = op
	}

	return clone
}