1package bug
  2
  3import (
  4	"encoding/json"
  5	"fmt"
  6	"strings"
  7
  8	"github.com/MichaelMure/git-bug/entity"
  9	"github.com/MichaelMure/git-bug/identity"
 10	"github.com/MichaelMure/git-bug/util/timestamp"
 11
 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 `json:"title"`
 21	Was   string `json:"was"`
 22}
 23
 24func (op *SetTitleOperation) base() *OpBase {
 25	return &op.OpBase
 26}
 27
 28func (op *SetTitleOperation) Id() entity.Id {
 29	return idOperation(op)
 30}
 31
 32func (op *SetTitleOperation) Apply(snapshot *Snapshot) {
 33	snapshot.Title = op.Title
 34	snapshot.addActor(op.Author)
 35
 36	item := &SetTitleTimelineItem{
 37		id:       op.Id(),
 38		Author:   op.Author,
 39		UnixTime: timestamp.Timestamp(op.UnixTime),
 40		Title:    op.Title,
 41		Was:      op.Was,
 42	}
 43
 44	snapshot.Timeline = append(snapshot.Timeline, item)
 45}
 46
 47func (op *SetTitleOperation) Validate() error {
 48	if err := opBaseValidate(op, SetTitleOp); err != nil {
 49		return err
 50	}
 51
 52	if text.Empty(op.Title) {
 53		return fmt.Errorf("title is empty")
 54	}
 55
 56	if strings.Contains(op.Title, "\n") {
 57		return fmt.Errorf("title should be a single line")
 58	}
 59
 60	if !text.Safe(op.Title) {
 61		return fmt.Errorf("title should be fully printable")
 62	}
 63
 64	if strings.Contains(op.Was, "\n") {
 65		return fmt.Errorf("previous title should be a single line")
 66	}
 67
 68	if !text.Safe(op.Was) {
 69		return fmt.Errorf("previous title should be fully printable")
 70	}
 71
 72	return nil
 73}
 74
 75// UnmarshalJSON is a two step JSON unmarshaling
 76// This workaround is necessary to avoid the inner OpBase.MarshalJSON
 77// overriding the outer op's MarshalJSON
 78func (op *SetTitleOperation) UnmarshalJSON(data []byte) error {
 79	// Unmarshal OpBase and the op separately
 80
 81	base := OpBase{}
 82	err := json.Unmarshal(data, &base)
 83	if err != nil {
 84		return err
 85	}
 86
 87	aux := struct {
 88		Title string `json:"title"`
 89		Was   string `json:"was"`
 90	}{}
 91
 92	err = json.Unmarshal(data, &aux)
 93	if err != nil {
 94		return err
 95	}
 96
 97	op.OpBase = base
 98	op.Title = aux.Title
 99	op.Was = aux.Was
100
101	return nil
102}
103
104// Sign post method for gqlgen
105func (op *SetTitleOperation) IsAuthored() {}
106
107func NewSetTitleOp(author identity.Interface, unixTime int64, title string, was string) *SetTitleOperation {
108	return &SetTitleOperation{
109		OpBase: newOpBase(SetTitleOp, author, unixTime),
110		Title:  title,
111		Was:    was,
112	}
113}
114
115type SetTitleTimelineItem struct {
116	id       entity.Id
117	Author   identity.Interface
118	UnixTime timestamp.Timestamp
119	Title    string
120	Was      string
121}
122
123func (s SetTitleTimelineItem) Id() entity.Id {
124	return s.id
125}
126
127// Sign post method for gqlgen
128func (s *SetTitleTimelineItem) IsAuthored() {}
129
130// Convenience function to apply the operation
131func SetTitle(b Interface, author identity.Interface, unixTime int64, title string) (*SetTitleOperation, error) {
132	it := NewOperationIterator(b)
133
134	var lastTitleOp Operation
135	for it.Next() {
136		op := it.Value()
137		if op.base().OperationType == SetTitleOp {
138			lastTitleOp = op
139		}
140	}
141
142	var was string
143	if lastTitleOp != nil {
144		was = lastTitleOp.(*SetTitleOperation).Title
145	} else {
146		was = b.FirstOp().(*CreateOperation).Title
147	}
148
149	setTitleOp := NewSetTitleOp(author, unixTime, title, was)
150
151	if err := setTitleOp.Validate(); err != nil {
152		return nil, err
153	}
154
155	b.Append(setTitleOp)
156	return setTitleOp, nil
157}