operation_pack.go

  1package entity
  2
  3import (
  4	"encoding/json"
  5	"strconv"
  6	"strings"
  7
  8	"github.com/pkg/errors"
  9
 10	"github.com/MichaelMure/git-bug/repository"
 11	"github.com/MichaelMure/git-bug/util/lamport"
 12)
 13
 14const opsEntryName = "ops"
 15const versionEntryPrefix = "version-"
 16const createClockEntryPrefix = "create-clock-"
 17const editClockEntryPrefix = "edit-clock-"
 18
 19type operationPack struct {
 20	Operations []Operation
 21	CreateTime lamport.Time
 22	EditTime   lamport.Time
 23}
 24
 25// func (opp *operationPack) MarshalJSON() ([]byte, error) {
 26// 	return json.Marshal(struct {
 27// 		Operations []Operation `json:"ops"`
 28// 	}{
 29// 		Operations: opp.Operations,
 30// 	})
 31// }
 32
 33func readOperationPack(def Definition, repo repository.RepoData, treeHash repository.Hash) (*operationPack, error) {
 34	entries, err := repo.ReadTree(treeHash)
 35	if err != nil {
 36		return nil, err
 37	}
 38
 39	// check the format version first, fail early instead of trying to read something
 40	// var version uint
 41	// for _, entry := range entries {
 42	// 	if strings.HasPrefix(entry.Name, versionEntryPrefix) {
 43	// 		v, err := strconv.ParseUint(strings.TrimPrefix(entry.Name, versionEntryPrefix), 10, 64)
 44	// 		if err != nil {
 45	// 			return nil, errors.Wrap(err, "can't read format version")
 46	// 		}
 47	// 		if v > 1<<12 {
 48	// 			return nil, fmt.Errorf("format version too big")
 49	// 		}
 50	// 		version = uint(v)
 51	// 		break
 52	// 	}
 53	// }
 54	// if version == 0 {
 55	// 	return nil, NewErrUnknowFormat(def.formatVersion)
 56	// }
 57	// if version != def.formatVersion {
 58	// 	return nil, NewErrInvalidFormat(version, def.formatVersion)
 59	// }
 60
 61	var ops []Operation
 62	var createTime lamport.Time
 63	var editTime lamport.Time
 64
 65	for _, entry := range entries {
 66		if entry.Name == opsEntryName {
 67			data, err := repo.ReadData(entry.Hash)
 68			if err != nil {
 69				return nil, errors.Wrap(err, "failed to read git blob data")
 70			}
 71
 72			ops, err = unmarshallOperations(def, data)
 73			if err != nil {
 74				return nil, err
 75			}
 76			break
 77		}
 78
 79		if strings.HasPrefix(entry.Name, createClockEntryPrefix) {
 80			v, err := strconv.ParseUint(strings.TrimPrefix(entry.Name, createClockEntryPrefix), 10, 64)
 81			if err != nil {
 82				return nil, errors.Wrap(err, "can't read creation lamport time")
 83			}
 84			createTime = lamport.Time(v)
 85		}
 86
 87		if strings.HasPrefix(entry.Name, editClockEntryPrefix) {
 88			v, err := strconv.ParseUint(strings.TrimPrefix(entry.Name, editClockEntryPrefix), 10, 64)
 89			if err != nil {
 90				return nil, errors.Wrap(err, "can't read edit lamport time")
 91			}
 92			editTime = lamport.Time(v)
 93		}
 94	}
 95
 96	return &operationPack{
 97		Operations: ops,
 98		CreateTime: createTime,
 99		EditTime:   editTime,
100	}, nil
101}
102
103func unmarshallOperations(def Definition, data []byte) ([]Operation, error) {
104	aux := struct {
105		Operations []json.RawMessage `json:"ops"`
106	}{}
107
108	if err := json.Unmarshal(data, &aux); err != nil {
109		return nil, err
110	}
111
112	ops := make([]Operation, 0, len(aux.Operations))
113
114	for _, raw := range aux.Operations {
115		// delegate to specialized unmarshal function
116		op, err := def.operationUnmarshaler(raw)
117		if err != nil {
118			return nil, err
119		}
120
121		ops = append(ops, op)
122	}
123
124	return ops, nil
125}