aboutsummaryrefslogtreecommitdiffstats
path: root/entity/id_interleaved.go
diff options
context:
space:
mode:
authorMichael Muré <batolettre@gmail.com>2021-05-03 11:45:15 +0200
committerMichael Muré <batolettre@gmail.com>2022-08-22 13:25:26 +0200
commit45b04351d8d02e53b3401b0ee23f7cbe750b63cd (patch)
tree59d203ef6c0f6a497b7074cd5617c8869cac3b14 /entity/id_interleaved.go
parent43026fc53669d462a60feec7d22aec090959be72 (diff)
downloadgit-bug-45b04351d8d02e53b3401b0ee23f7cbe750b63cd.tar.gz
bug: have a type for combined ids, fix https://github.com/MichaelMure/git-bug/issues/653
Diffstat (limited to 'entity/id_interleaved.go')
-rw-r--r--entity/id_interleaved.go84
1 files changed, 81 insertions, 3 deletions
diff --git a/entity/id_interleaved.go b/entity/id_interleaved.go
index 0ce2ba00..28c59a42 100644
--- a/entity/id_interleaved.go
+++ b/entity/id_interleaved.go
@@ -1,13 +1,91 @@
package entity
import (
+ "fmt"
+ "io"
"strings"
+
+ "github.com/pkg/errors"
)
+const UnsetCombinedId = CombinedId("unset")
+
+// CombinedId is an Id holding information from both a primary Id and a secondary Id.
+// While it looks like a regular Id, do not just cast from one to another.
+// Instead, use CombineIds and SeparateIds to create it and split it.
+type CombinedId string
+
+// String return the identifier as a string
+func (ci CombinedId) String() string {
+ return string(ci)
+}
+
+// Human return the identifier, shortened for human consumption
+func (ci CombinedId) Human() string {
+ format := fmt.Sprintf("%%.%ds", humanIdLength)
+ return fmt.Sprintf(format, ci)
+}
+
+func (ci CombinedId) HasPrefix(prefix string) bool {
+ return strings.HasPrefix(string(ci), prefix)
+}
+
+// UnmarshalGQL implement the Unmarshaler interface for gqlgen
+func (ci *CombinedId) UnmarshalGQL(v interface{}) error {
+ _, ok := v.(string)
+ if !ok {
+ return fmt.Errorf("CombinedIds must be strings")
+ }
+
+ *ci = v.(CombinedId)
+
+ if err := ci.Validate(); err != nil {
+ return errors.Wrap(err, "invalid CombinedId")
+ }
+
+ return nil
+}
+
+// MarshalGQL implement the Marshaler interface for gqlgen
+func (ci CombinedId) MarshalGQL(w io.Writer) {
+ _, _ = w.Write([]byte(`"` + ci.String() + `"`))
+}
+
+// Validate tell if the Id is valid
+func (ci CombinedId) Validate() error {
+ // Special case to detect outdated repo
+ if len(ci) == 40 {
+ return fmt.Errorf("outdated repository format, please use https://github.com/MichaelMure/git-bug-migration to upgrade")
+ }
+ if len(ci) != idLength {
+ return fmt.Errorf("invalid length")
+ }
+ for _, r := range ci {
+ if (r < 'a' || r > 'z') && (r < '0' || r > '9') {
+ return fmt.Errorf("invalid character")
+ }
+ }
+ return nil
+}
+
+// PrimaryPrefix is a helper to extract the primary prefix.
+// If practical, use SeparateIds instead.
+func (ci CombinedId) PrimaryPrefix() string {
+ primaryPrefix, _ := SeparateIds(string(ci))
+ return primaryPrefix
+}
+
+// SecondaryPrefix is a helper to extract the secondary prefix.
+// If practical, use SeparateIds instead.
+func (ci CombinedId) SecondaryPrefix() string {
+ _, secondaryPrefix := SeparateIds(string(ci))
+ return secondaryPrefix
+}
+
// CombineIds compute a merged Id holding information from both the primary Id
// and the secondary Id.
//
-// This allow to later find efficiently a secondary element because we can access
+// This allows to later find efficiently a secondary element because we can access
// the primary one directly instead of searching for a primary that has a
// secondary matching the Id.
//
@@ -32,7 +110,7 @@ import (
// 7: 4P, 3S
// 10: 6P, 4S
// 16: 11P, 5S
-func CombineIds(primary Id, secondary Id) Id {
+func CombineIds(primary Id, secondary Id) CombinedId {
var id strings.Builder
for i := 0; i < idLength; i++ {
@@ -46,7 +124,7 @@ func CombineIds(primary Id, secondary Id) Id {
}
}
- return Id(id.String())
+ return CombinedId(id.String())
}
// SeparateIds extract primary and secondary prefix from an arbitrary length prefix