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