1package bug
2
3import (
4 "encoding/json"
5 "fmt"
6
7 "github.com/MichaelMure/git-bug/identity"
8 "github.com/MichaelMure/git-bug/util/git"
9)
10
11var _ Operation = &SetMetadataOperation{}
12
13type SetMetadataOperation struct {
14 OpBase
15 Target git.Hash
16 NewMetadata map[string]string
17}
18
19func (op *SetMetadataOperation) base() *OpBase {
20 return &op.OpBase
21}
22
23func (op *SetMetadataOperation) Hash() (git.Hash, error) {
24 return hashOperation(op)
25}
26
27func (op *SetMetadataOperation) Apply(snapshot *Snapshot) {
28 for _, target := range snapshot.Operations {
29 hash, err := target.Hash()
30 if err != nil {
31 // Should never error unless a programming error happened
32 // (covered in OpBase.Validate())
33 panic(err)
34 }
35
36 if hash == op.Target {
37 base := target.base()
38
39 if base.extraMetadata == nil {
40 base.extraMetadata = make(map[string]string)
41 }
42
43 for key, val := range op.NewMetadata {
44 if _, exist := base.extraMetadata[key]; !exist {
45 base.extraMetadata[key] = val
46 }
47 }
48
49 return
50 }
51 }
52}
53
54func (op *SetMetadataOperation) Validate() error {
55 if err := opBaseValidate(op, SetMetadataOp); err != nil {
56 return err
57 }
58
59 if !op.Target.IsValid() {
60 return fmt.Errorf("target hash is invalid")
61 }
62
63 return nil
64}
65
66// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
67// MarshalJSON
68func (op *SetMetadataOperation) MarshalJSON() ([]byte, error) {
69 base, err := json.Marshal(op.OpBase)
70 if err != nil {
71 return nil, err
72 }
73
74 // revert back to a flat map to be able to add our own fields
75 var data map[string]interface{}
76 if err := json.Unmarshal(base, &data); err != nil {
77 return nil, err
78 }
79
80 data["target"] = op.Target
81 data["new_metadata"] = op.NewMetadata
82
83 return json.Marshal(data)
84}
85
86// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
87// MarshalJSON
88func (op *SetMetadataOperation) UnmarshalJSON(data []byte) error {
89 // Unmarshal OpBase and the op separately
90
91 base := OpBase{}
92 err := json.Unmarshal(data, &base)
93 if err != nil {
94 return err
95 }
96
97 aux := struct {
98 Target git.Hash `json:"target"`
99 NewMetadata map[string]string `json:"new_metadata"`
100 }{}
101
102 err = json.Unmarshal(data, &aux)
103 if err != nil {
104 return err
105 }
106
107 op.OpBase = base
108 op.Target = aux.Target
109 op.NewMetadata = aux.NewMetadata
110
111 return nil
112}
113
114// Sign post method for gqlgen
115func (op *SetMetadataOperation) IsAuthored() {}
116
117func NewSetMetadataOp(author identity.Interface, unixTime int64, target git.Hash, newMetadata map[string]string) *SetMetadataOperation {
118 return &SetMetadataOperation{
119 OpBase: newOpBase(SetMetadataOp, author, unixTime),
120 Target: target,
121 NewMetadata: newMetadata,
122 }
123}
124
125// Convenience function to apply the operation
126func SetMetadata(b Interface, author identity.Interface, unixTime int64, target git.Hash, newMetadata map[string]string) (*SetMetadataOperation, error) {
127 SetMetadataOp := NewSetMetadataOp(author, unixTime, target, newMetadata)
128 if err := SetMetadataOp.Validate(); err != nil {
129 return nil, err
130 }
131 b.Append(SetMetadataOp)
132 return SetMetadataOp, nil
133}