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