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