op_set_title.go

  1package bug
  2
  3import (
  4	"encoding/json"
  5	"fmt"
  6	"strings"
  7
  8	"github.com/MichaelMure/git-bug/identity"
  9
 10	"github.com/MichaelMure/git-bug/util/git"
 11	"github.com/MichaelMure/git-bug/util/text"
 12)
 13
 14var _ Operation = &SetTitleOperation{}
 15
 16// SetTitleOperation will change the title of a bug
 17type SetTitleOperation struct {
 18	OpBase
 19	Title string
 20	Was   string
 21}
 22
 23func (op *SetTitleOperation) base() *OpBase {
 24	return &op.OpBase
 25}
 26
 27func (op *SetTitleOperation) Hash() (git.Hash, error) {
 28	return hashOperation(op)
 29}
 30
 31func (op *SetTitleOperation) Apply(snapshot *Snapshot) {
 32	snapshot.Title = op.Title
 33
 34	hash, err := op.Hash()
 35	if err != nil {
 36		// Should never error unless a programming error happened
 37		// (covered in OpBase.Validate())
 38		panic(err)
 39	}
 40
 41	item := &SetTitleTimelineItem{
 42		hash:     hash,
 43		Author:   op.Author,
 44		UnixTime: Timestamp(op.UnixTime),
 45		Title:    op.Title,
 46		Was:      op.Was,
 47	}
 48
 49	snapshot.Timeline = append(snapshot.Timeline, item)
 50}
 51
 52func (op *SetTitleOperation) Validate() error {
 53	if err := opBaseValidate(op, SetTitleOp); err != nil {
 54		return err
 55	}
 56
 57	if text.Empty(op.Title) {
 58		return fmt.Errorf("title is empty")
 59	}
 60
 61	if strings.Contains(op.Title, "\n") {
 62		return fmt.Errorf("title should be a single line")
 63	}
 64
 65	if !text.Safe(op.Title) {
 66		return fmt.Errorf("title should be fully printable")
 67	}
 68
 69	if strings.Contains(op.Was, "\n") {
 70		return fmt.Errorf("previous title should be a single line")
 71	}
 72
 73	if !text.Safe(op.Was) {
 74		return fmt.Errorf("previous title should be fully printable")
 75	}
 76
 77	return nil
 78}
 79
 80// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
 81// MarshalJSON
 82func (op *SetTitleOperation) MarshalJSON() ([]byte, error) {
 83	base, err := json.Marshal(op.OpBase)
 84	if err != nil {
 85		return nil, err
 86	}
 87
 88	// revert back to a flat map to be able to add our own fields
 89	var data map[string]interface{}
 90	if err := json.Unmarshal(base, &data); err != nil {
 91		return nil, err
 92	}
 93
 94	data["title"] = op.Title
 95	data["was"] = op.Was
 96
 97	return json.Marshal(data)
 98}
 99
100// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
101// MarshalJSON
102func (op *SetTitleOperation) UnmarshalJSON(data []byte) error {
103	// Unmarshal OpBase and the op separately
104
105	base := OpBase{}
106	err := json.Unmarshal(data, &base)
107	if err != nil {
108		return err
109	}
110
111	aux := struct {
112		Title string `json:"title"`
113		Was   string `json:"was"`
114	}{}
115
116	err = json.Unmarshal(data, &aux)
117	if err != nil {
118		return err
119	}
120
121	op.OpBase = base
122	op.Title = aux.Title
123	op.Was = aux.Was
124
125	return nil
126}
127
128// Sign post method for gqlgen
129func (op *SetTitleOperation) IsAuthored() {}
130
131func NewSetTitleOp(author identity.Interface, unixTime int64, title string, was string) *SetTitleOperation {
132	return &SetTitleOperation{
133		OpBase: newOpBase(SetTitleOp, author, unixTime),
134		Title:  title,
135		Was:    was,
136	}
137}
138
139type SetTitleTimelineItem struct {
140	hash     git.Hash
141	Author   identity.Interface
142	UnixTime Timestamp
143	Title    string
144	Was      string
145}
146
147func (s SetTitleTimelineItem) Hash() git.Hash {
148	return s.hash
149}
150
151// Convenience function to apply the operation
152func SetTitle(b Interface, author identity.Interface, unixTime int64, title string) (*SetTitleOperation, error) {
153	it := NewOperationIterator(b)
154
155	var lastTitleOp Operation
156	for it.Next() {
157		op := it.Value()
158		if op.base().OperationType == SetTitleOp {
159			lastTitleOp = op
160		}
161	}
162
163	var was string
164	if lastTitleOp != nil {
165		was = lastTitleOp.(*SetTitleOperation).Title
166	} else {
167		was = b.FirstOp().(*CreateOperation).Title
168	}
169
170	setTitleOp := NewSetTitleOp(author, unixTime, title, was)
171
172	if err := setTitleOp.Validate(); err != nil {
173		return nil, err
174	}
175
176	b.Append(setTitleOp)
177	return setTitleOp, nil
178}