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}