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

           
        


                       
 
                                                   
                                                 
                               

 

                       






                                                                     
                              

                                                 
                           

 
                                                                 
                                             
 
                                                                                            





                                                                 
 








                                                         
 







                                                             

         


                                                                           
 



                                                                 
 


                                                               
 













                                                                                       

         
                  






                                                   
                                             



                                          
                                                        











                                                     
 
 

                                                                            
                                                                         
                                      












                                         














                                                                   
package bug

import (
	"encoding/json"
	"fmt"
	"reflect"

	"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 by gob
	commitHash git.Hash
}

// hold the different operation type to instantiate to parse JSON
var operations map[OperationType]reflect.Type

// Register will register a new type of Operation to be able to parse the corresponding JSON
func Register(t OperationType, op interface{}) {
	if operations == nil {
		operations = make(map[OperationType]reflect.Type)
	}
	operations[t] = reflect.TypeOf(op)
}

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
		}

		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 nil
}

// 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.Repo) (git.Hash, error) {
	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
}