board.go

  1package board
  2
  3import (
  4	"fmt"
  5
  6	"github.com/MichaelMure/git-bug/entities/bug"
  7	"github.com/MichaelMure/git-bug/entities/identity"
  8
  9	"github.com/MichaelMure/git-bug/entity"
 10	"github.com/MichaelMure/git-bug/entity/dag"
 11	"github.com/MichaelMure/git-bug/repository"
 12)
 13
 14var _ Interface = &Board{}
 15
 16// 1: original format
 17const formatVersion = 1
 18
 19var def = dag.Definition{
 20	Typename:             "board",
 21	Namespace:            "boards",
 22	OperationUnmarshaler: operationUnmarshaler,
 23	FormatVersion:        formatVersion,
 24}
 25
 26var ClockLoader = dag.ClockLoader(def)
 27
 28type Interface interface {
 29	dag.Interface[*Snapshot, Operation]
 30}
 31
 32// Board holds the data of a project board.
 33type Board struct {
 34	*dag.Entity
 35}
 36
 37// NewBoard create a new Board
 38func NewBoard() *Board {
 39	return &Board{
 40		Entity: dag.New(def),
 41	}
 42}
 43
 44func simpleResolvers(repo repository.ClockedRepo) entity.Resolvers {
 45	return entity.Resolvers{
 46		&identity.Identity{}: identity.NewSimpleResolver(repo),
 47		&bug.Bug{}:           bug.NewSimpleResolver(repo),
 48	}
 49}
 50
 51// Read will read a board from a repository
 52func Read(repo repository.ClockedRepo, id entity.Id) (*Board, error) {
 53	return ReadWithResolver(repo, simpleResolvers(repo), id)
 54}
 55
 56// ReadWithResolver will read a board from its Id, with a custom identity.Resolver
 57func ReadWithResolver(repo repository.ClockedRepo, resolvers entity.Resolvers, id entity.Id) (*Board, error) {
 58	e, err := dag.Read(def, repo, resolvers, id)
 59	if err != nil {
 60		return nil, err
 61	}
 62	return &Board{Entity: e}, nil
 63}
 64
 65// Validate check if the Board data is valid
 66func (board *Board) Validate() error {
 67	if err := board.Entity.Validate(); err != nil {
 68		return err
 69	}
 70
 71	// The very first Op should be a CreateOp
 72	firstOp := board.FirstOp()
 73	if firstOp == nil || firstOp.Type() != CreateOp {
 74		return fmt.Errorf("first operation should be a Create op")
 75	}
 76
 77	// Check that there is no more CreateOp op
 78	for i, op := range board.Entity.Operations() {
 79		if i == 0 {
 80			continue
 81		}
 82		if op.Type() == CreateOp {
 83			return fmt.Errorf("only one Create op allowed")
 84		}
 85	}
 86
 87	return nil
 88}
 89
 90// Append add a new Operation to the Board
 91func (board *Board) Append(op Operation) {
 92	board.Entity.Append(op)
 93}
 94
 95// Operations return the ordered operations
 96func (board *Board) Operations() []Operation {
 97	source := board.Entity.Operations()
 98	result := make([]Operation, len(source))
 99	for i, op := range source {
100		result[i] = op.(Operation)
101	}
102	return result
103}
104
105// Compile a board in an easily usable snapshot
106func (board *Board) Compile() *Snapshot {
107	snap := &Snapshot{
108		id: board.Id(),
109	}
110
111	for _, op := range board.Operations() {
112		op.Apply(snap)
113		snap.Operations = append(snap.Operations, op)
114	}
115
116	return snap
117}
118
119// FirstOp lookup for the very first operation of the board.
120// For a valid Board, this operation should be a CreateOp
121func (board *Board) FirstOp() Operation {
122	if fo := board.Entity.FirstOp(); fo != nil {
123		return fo.(Operation)
124	}
125	return nil
126}
127
128// LastOp lookup for the very last operation of the board.
129// For a valid Board, should never be nil
130func (board *Board) LastOp() Operation {
131	if lo := board.Entity.LastOp(); lo != nil {
132		return lo.(Operation)
133	}
134	return nil
135}