board.go

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