diff options
Diffstat (limited to 'entities/bug/bug.go')
-rw-r--r-- | entities/bug/bug.go | 179 |
1 files changed, 179 insertions, 0 deletions
diff --git a/entities/bug/bug.go b/entities/bug/bug.go new file mode 100644 index 00000000..213a4ca4 --- /dev/null +++ b/entities/bug/bug.go @@ -0,0 +1,179 @@ +// Package bug contains the bug data model and low-level related functions +package bug + +import ( + "fmt" + + "github.com/MichaelMure/git-bug/entities/identity" + "github.com/MichaelMure/git-bug/entity" + "github.com/MichaelMure/git-bug/entity/dag" + "github.com/MichaelMure/git-bug/repository" +) + +var _ Interface = &Bug{} +var _ entity.Interface = &Bug{} + +// 1: original format +// 2: no more legacy identities +// 3: Ids are generated from the create operation serialized data instead of from the first git commit +// 4: with DAG entity framework +const formatVersion = 4 + +var def = dag.Definition{ + Typename: "bug", + Namespace: "bugs", + OperationUnmarshaler: operationUnmarshaller, + FormatVersion: formatVersion, +} + +var ClockLoader = dag.ClockLoader(def) + +// Bug holds the data of a bug thread, organized in a way close to +// how it will be persisted inside Git. This is the data structure +// used to merge two different version of the same Bug. +type Bug struct { + *dag.Entity +} + +// NewBug create a new Bug +func NewBug() *Bug { + return &Bug{ + Entity: dag.New(def), + } +} + +func simpleResolvers(repo repository.ClockedRepo) entity.Resolvers { + return entity.Resolvers{ + &identity.Identity{}: identity.NewSimpleResolver(repo), + } +} + +// Read will read a bug from a repository +func Read(repo repository.ClockedRepo, id entity.Id) (*Bug, error) { + return ReadWithResolver(repo, simpleResolvers(repo), id) +} + +// ReadWithResolver will read a bug from its Id, with custom resolvers +func ReadWithResolver(repo repository.ClockedRepo, resolvers entity.Resolvers, id entity.Id) (*Bug, error) { + e, err := dag.Read(def, repo, resolvers, id) + if err != nil { + return nil, err + } + return &Bug{Entity: e}, nil +} + +type StreamedBug struct { + Bug *Bug + Err error +} + +// ReadAll read and parse all local bugs +func ReadAll(repo repository.ClockedRepo) <-chan StreamedBug { + return readAll(repo, simpleResolvers(repo)) +} + +// ReadAllWithResolver read and parse all local bugs +func ReadAllWithResolver(repo repository.ClockedRepo, resolvers entity.Resolvers) <-chan StreamedBug { + return readAll(repo, resolvers) +} + +// Read and parse all available bug with a given ref prefix +func readAll(repo repository.ClockedRepo, resolvers entity.Resolvers) <-chan StreamedBug { + out := make(chan StreamedBug) + + go func() { + defer close(out) + + for streamedEntity := range dag.ReadAll(def, repo, resolvers) { + if streamedEntity.Err != nil { + out <- StreamedBug{ + Err: streamedEntity.Err, + } + } else { + out <- StreamedBug{ + Bug: &Bug{Entity: streamedEntity.Entity}, + } + } + } + }() + + return out +} + +// ListLocalIds list all the available local bug ids +func ListLocalIds(repo repository.Repo) ([]entity.Id, error) { + return dag.ListLocalIds(def, repo) +} + +// Validate check if the Bug data is valid +func (bug *Bug) Validate() error { + if err := bug.Entity.Validate(); err != nil { + return err + } + + // The very first Op should be a CreateOp + firstOp := bug.FirstOp() + if firstOp == nil || firstOp.Type() != CreateOp { + return fmt.Errorf("first operation should be a Create op") + } + + // Check that there is no more CreateOp op + for i, op := range bug.Operations() { + if i == 0 { + continue + } + if op.Type() == CreateOp { + return fmt.Errorf("only one Create op allowed") + } + } + + return nil +} + +// Append add a new Operation to the Bug +func (bug *Bug) Append(op Operation) { + bug.Entity.Append(op) +} + +// Operations return the ordered operations +func (bug *Bug) Operations() []Operation { + source := bug.Entity.Operations() + result := make([]Operation, len(source)) + for i, op := range source { + result[i] = op.(Operation) + } + return result +} + +// Compile a bug in a easily usable snapshot +func (bug *Bug) Compile() *Snapshot { + snap := &Snapshot{ + id: bug.Id(), + Status: OpenStatus, + } + + for _, op := range bug.Operations() { + op.Apply(snap) + snap.Operations = append(snap.Operations, op) + } + + return snap +} + +// FirstOp lookup for the very first operation of the bug. +// For a valid Bug, this operation should be a CreateOp +func (bug *Bug) FirstOp() Operation { + if fo := bug.Entity.FirstOp(); fo != nil { + return fo.(Operation) + } + return nil +} + +// LastOp lookup for the very last operation of the bug. +// For a valid Bug, should never be nil +func (bug *Bug) LastOp() Operation { + if lo := bug.Entity.LastOp(); lo != nil { + return lo.(Operation) + } + return nil +} |