summaryrefslogtreecommitdiffstatshomepage
path: root/entity/operations.go
blob: fd88f033cac7ec895c3e0f3db47e4e53ba3abf3c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package entity

import (
	"time"

	"github.com/MichaelMure/git-bug/entities/identity"
	"github.com/MichaelMure/git-bug/repository"
)

// OperationType is an operation type identifier
type OperationType int

// Operation is a piece of data defining a change to reflect on the state of an Entity.
// What this Operation or Entity's state looks like is not of the resort of this package as it only deals with the
// data structure and storage.
type Operation interface {
	// Id return the Operation identifier
	//
	// Some care need to be taken to define a correct Id derivation and enough entropy in the data used to avoid
	// collisions. Notably:
	// - the Id of the first Operation will be used as the Id of the Entity. Collision need to be avoided across entities
	//   of the same type (example: no collision within the "bug" namespace).
	// - collisions can also happen within the set of Operations of an Entity. Simple Operation might not have enough
	//   entropy to yield unique Ids (example: two "close" operation within the same second, same author).
	//   If this is a concern, it is recommended to include a piece of random data in the operation's data, to guarantee
	//   a minimal amount of entropy and avoid collision.
	//
	//   Author's note: I tried to find a clever way around that inelegance (stuffing random useless data into the stored
	//   structure is not exactly elegant), but I failed to find a proper way. Essentially, anything that would reuse some
	//   other data (parent operation's Id, lamport clock) or the graph structure (depth) impose that the Id would only
	//   make sense in the context of the graph and yield some deep coupling between Entity and Operation. This in turn
	//   make the whole thing even less elegant.
	//
	// A common way to derive an Id will be to use the entity.DeriveId() function on the serialized operation data.
	Id() Id
	// Type return the type of the operation
	Type() OperationType
	// Validate check if the Operation data is valid
	Validate() error
	// Author returns the author of this operation
	Author() identity.Interface
	// Time return the time when the operation was added
	Time() time.Time

	// SetMetadata store arbitrary metadata about the operation
	SetMetadata(key string, value string)
	// GetMetadata retrieve arbitrary metadata about the operation
	GetMetadata(key string) (string, bool)
	// AllMetadata return all metadata for this operation
	AllMetadata() map[string]string
}

type OperationWithApply[SnapT Snapshot] interface {
	Operation

	// Apply the operation to a Snapshot to create the final state
	Apply(snapshot SnapT)
}

// OperationWithFiles is an optional extension for an Operation that has files dependency, stored in git.
type OperationWithFiles interface {
	Operation

	// GetFiles return the files needed by this operation
	// This implies that the Operation maintain and store internally the references to those files. This is how
	// this information is read later, when loading from storage.
	// For example, an operation that has a text value referencing some files would maintain a mapping (text ref -->
	// hash).
	GetFiles() []repository.Hash
}

// OperationDoesntChangeSnapshot is an interface signaling that the Operation implementing it doesn't change the
// snapshot, for example a metadata operation that act on other operations.
type OperationDoesntChangeSnapshot interface {
	DoesntChangeSnapshot()
}