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