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 "github.com/MichaelMure/git-bug/util/text"
10)
11
12var _ Operation = &AddCommentOperation{}
13
14// AddCommentOperation will add a new comment in the bug
15type AddCommentOperation struct {
16 OpBase
17 Message string
18 // TODO: change for a map[string]util.hash to store the filename ?
19 Files []git.Hash
20}
21
22func (op *AddCommentOperation) base() *OpBase {
23 return &op.OpBase
24}
25
26func (op *AddCommentOperation) Hash() (git.Hash, error) {
27 return hashOperation(op)
28}
29
30func (op *AddCommentOperation) Apply(snapshot *Snapshot) {
31 comment := Comment{
32 Message: op.Message,
33 Author: op.Author,
34 Files: op.Files,
35 UnixTime: Timestamp(op.UnixTime),
36 }
37
38 snapshot.Comments = append(snapshot.Comments, comment)
39
40 hash, err := op.Hash()
41 if err != nil {
42 // Should never error unless a programming error happened
43 // (covered in OpBase.Validate())
44 panic(err)
45 }
46
47 item := &AddCommentTimelineItem{
48 CommentTimelineItem: NewCommentTimelineItem(hash, comment),
49 }
50
51 snapshot.Timeline = append(snapshot.Timeline, item)
52}
53
54func (op *AddCommentOperation) GetFiles() []git.Hash {
55 return op.Files
56}
57
58func (op *AddCommentOperation) Validate() error {
59 if err := opBaseValidate(op, AddCommentOp); err != nil {
60 return err
61 }
62
63 if !text.Safe(op.Message) {
64 return fmt.Errorf("message is not fully printable")
65 }
66
67 return nil
68}
69
70// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
71// MarshalJSON
72func (op *AddCommentOperation) MarshalJSON() ([]byte, error) {
73 base, err := json.Marshal(op.OpBase)
74 if err != nil {
75 return nil, err
76 }
77
78 // revert back to a flat map to be able to add our own fields
79 var data map[string]interface{}
80 if err := json.Unmarshal(base, &data); err != nil {
81 return nil, err
82 }
83
84 data["message"] = op.Message
85 data["files"] = op.Files
86
87 return json.Marshal(data)
88}
89
90// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
91// MarshalJSON
92func (op *AddCommentOperation) UnmarshalJSON(data []byte) error {
93 // Unmarshal OpBase and the op separately
94
95 base := OpBase{}
96 err := json.Unmarshal(data, &base)
97 if err != nil {
98 return err
99 }
100
101 aux := struct {
102 Message string `json:"message"`
103 Files []git.Hash `json:"files"`
104 }{}
105
106 err = json.Unmarshal(data, &aux)
107 if err != nil {
108 return err
109 }
110
111 op.OpBase = base
112 op.Message = aux.Message
113 op.Files = aux.Files
114
115 return nil
116}
117
118// Sign post method for gqlgen
119func (op *AddCommentOperation) IsAuthored() {}
120
121func NewAddCommentOp(author identity.Interface, unixTime int64, message string, files []git.Hash) *AddCommentOperation {
122 return &AddCommentOperation{
123 OpBase: newOpBase(AddCommentOp, author, unixTime),
124 Message: message,
125 Files: files,
126 }
127}
128
129// CreateTimelineItem replace a AddComment operation in the Timeline and hold its edition history
130type AddCommentTimelineItem struct {
131 CommentTimelineItem
132}
133
134// Convenience function to apply the operation
135func AddComment(b Interface, author identity.Interface, unixTime int64, message string) (*AddCommentOperation, error) {
136 return AddCommentWithFiles(b, author, unixTime, message, nil)
137}
138
139func AddCommentWithFiles(b Interface, author identity.Interface, unixTime int64, message string, files []git.Hash) (*AddCommentOperation, error) {
140 addCommentOp := NewAddCommentOp(author, unixTime, message, files)
141 if err := addCommentOp.Validate(); err != nil {
142 return nil, err
143 }
144 b.Append(addCommentOp)
145 return addCommentOp, nil
146}