op_set_title.go

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