aboutsummaryrefslogtreecommitdiffstats
path: root/entities/bug/bug.go
diff options
context:
space:
mode:
Diffstat (limited to 'entities/bug/bug.go')
-rw-r--r--entities/bug/bug.go179
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
+}