1package bug
2
3import (
4 "encoding/json"
5 "fmt"
6
7 "github.com/MichaelMure/git-bug/identity"
8
9 "github.com/MichaelMure/git-bug/util/git"
10 "github.com/MichaelMure/git-bug/util/text"
11)
12
13var _ Operation = &EditCommentOperation{}
14
15// EditCommentOperation will change a comment in the bug
16type EditCommentOperation struct {
17 OpBase
18 Target git.Hash
19 Message string
20 Files []git.Hash
21}
22
23func (op *EditCommentOperation) base() *OpBase {
24 return &op.OpBase
25}
26
27func (op *EditCommentOperation) Hash() (git.Hash, error) {
28 return hashOperation(op)
29}
30
31func (op *EditCommentOperation) Apply(snapshot *Snapshot) {
32 // Todo: currently any message can be edited, even by a different author
33 // crypto signature are needed.
34
35 var target TimelineItem
36 var commentIndex int
37
38 for i, item := range snapshot.Timeline {
39 h := item.Hash()
40
41 if h == op.Target {
42 target = snapshot.Timeline[i]
43 break
44 }
45
46 // Track the index in the []Comment
47 switch item.(type) {
48 case *CreateTimelineItem, *CommentTimelineItem:
49 commentIndex++
50 }
51 }
52
53 if target == nil {
54 // Target not found, edit is a no-op
55 return
56 }
57
58 comment := Comment{
59 Message: op.Message,
60 Files: op.Files,
61 UnixTime: Timestamp(op.UnixTime),
62 }
63
64 switch target.(type) {
65 case *CreateTimelineItem:
66 item := target.(*CreateTimelineItem)
67 item.Append(comment)
68
69 case *AddCommentTimelineItem:
70 item := target.(*AddCommentTimelineItem)
71 item.Append(comment)
72 }
73
74 snapshot.Comments[commentIndex].Message = op.Message
75 snapshot.Comments[commentIndex].Files = op.Files
76}
77
78func (op *EditCommentOperation) GetFiles() []git.Hash {
79 return op.Files
80}
81
82func (op *EditCommentOperation) Validate() error {
83 if err := opBaseValidate(op, EditCommentOp); err != nil {
84 return err
85 }
86
87 if !op.Target.IsValid() {
88 return fmt.Errorf("target hash is invalid")
89 }
90
91 if !text.Safe(op.Message) {
92 return fmt.Errorf("message is not fully printable")
93 }
94
95 return nil
96}
97
98// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
99// MarshalJSON
100func (op *EditCommentOperation) MarshalJSON() ([]byte, error) {
101 base, err := json.Marshal(op.OpBase)
102 if err != nil {
103 return nil, err
104 }
105
106 // revert back to a flat map to be able to add our own fields
107 var data map[string]interface{}
108 if err := json.Unmarshal(base, &data); err != nil {
109 return nil, err
110 }
111
112 data["target"] = op.Target
113 data["message"] = op.Message
114 data["files"] = op.Files
115
116 return json.Marshal(data)
117}
118
119// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
120// MarshalJSON
121func (op *EditCommentOperation) UnmarshalJSON(data []byte) error {
122 // Unmarshal OpBase and the op separately
123
124 base := OpBase{}
125 err := json.Unmarshal(data, &base)
126 if err != nil {
127 return err
128 }
129
130 aux := struct {
131 Target git.Hash `json:"target"`
132 Message string `json:"message"`
133 Files []git.Hash `json:"files"`
134 }{}
135
136 err = json.Unmarshal(data, &aux)
137 if err != nil {
138 return err
139 }
140
141 op.OpBase = base
142 op.Target = aux.Target
143 op.Message = aux.Message
144 op.Files = aux.Files
145
146 return nil
147}
148
149// Sign post method for gqlgen
150func (op *EditCommentOperation) IsAuthored() {}
151
152func NewEditCommentOp(author identity.Interface, unixTime int64, target git.Hash, message string, files []git.Hash) *EditCommentOperation {
153 return &EditCommentOperation{
154 OpBase: newOpBase(EditCommentOp, author, unixTime),
155 Target: target,
156 Message: message,
157 Files: files,
158 }
159}
160
161// Convenience function to apply the operation
162func EditComment(b Interface, author identity.Interface, unixTime int64, target git.Hash, message string) (*EditCommentOperation, error) {
163 return EditCommentWithFiles(b, author, unixTime, target, message, nil)
164}
165
166func EditCommentWithFiles(b Interface, author identity.Interface, unixTime int64, target git.Hash, message string, files []git.Hash) (*EditCommentOperation, error) {
167 editCommentOp := NewEditCommentOp(author, unixTime, target, message, files)
168 if err := editCommentOp.Validate(); err != nil {
169 return nil, err
170 }
171 b.Append(editCommentOp)
172 return editCommentOp, nil
173}