aboutsummaryrefslogtreecommitdiff
path: root/ref/store/memory/read.go
diff options
context:
space:
mode:
authorGravatar Runxi Yu2026-05-24 13:41:34 +0000
committerGravatar Runxi Yu2026-05-24 14:12:35 +0000
commit947bf81a33c6e4e5d21c8b36f9317fe00b84f6ae (patch)
tree67824655ef9dbf2d941dae06d59ea29a1e32d458 /ref/store/memory/read.go
parentREADME: Update (diff)
signatureNo signature
ref/store/memory: Simple memory-backed ref store v0.1.175
Diffstat (limited to 'ref/store/memory/read.go')
-rw-r--r--ref/store/memory/read.go125
1 files changed, 125 insertions, 0 deletions
diff --git a/ref/store/memory/read.go b/ref/store/memory/read.go
new file mode 100644
index 00000000..5f8095bb
--- /dev/null
+++ b/ref/store/memory/read.go
@@ -0,0 +1,125 @@
+package memory
+
+import (
+ "fmt"
+ "path"
+ "slices"
+ "strings"
+
+ "codeberg.org/lindenii/furgit/ref"
+ refstore "codeberg.org/lindenii/furgit/ref/store"
+)
+
+// Resolve resolves one reference name
+// from the in-memory namespace.
+func (store *Store) Resolve(name string) (ref.Ref, error) { //nolint:ireturn
+ store.mu.RLock()
+ defer store.mu.RUnlock()
+
+ return publicRef(name, store.refs[name])
+}
+
+// ResolveToDetached resolves symbolic references
+// through the in-memory namespace
+// until one detached reference is reached.
+func (store *Store) ResolveToDetached(name string) (ref.Detached, error) {
+ store.mu.RLock()
+ defer store.mu.RUnlock()
+
+ return store.resolveToDetachedLocked(name)
+}
+
+// List lists references from the in-memory namespace.
+func (store *Store) List(pattern string) ([]ref.Ref, error) {
+ matchAll := pattern == ""
+ if !matchAll {
+ _, err := path.Match(pattern, "HEAD")
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ store.mu.RLock()
+ defer store.mu.RUnlock()
+
+ names := make([]string, 0, len(store.refs))
+ for name := range store.refs {
+ if !matchAll {
+ matched, err := path.Match(pattern, name)
+ if err != nil {
+ return nil, err
+ }
+
+ if !matched {
+ continue
+ }
+ }
+
+ names = append(names, name)
+ }
+
+ slices.Sort(names)
+
+ refs := make([]ref.Ref, 0, len(names))
+ for _, name := range names {
+ resolved, err := publicRef(name, store.refs[name])
+ if err != nil {
+ return nil, err
+ }
+
+ refs = append(refs, resolved)
+ }
+
+ return refs, nil
+}
+
+func (store *Store) resolveToDetachedLocked(name string) (ref.Detached, error) {
+ cur := name
+ seen := make(map[string]struct{})
+
+ for {
+ if _, ok := seen[cur]; ok {
+ return ref.Detached{}, fmt.Errorf("refstore/memory: symbolic reference cycle at %q", cur)
+ }
+
+ seen[cur] = struct{}{}
+
+ resolved, err := publicRef(cur, store.refs[cur])
+ if err != nil {
+ return ref.Detached{}, err
+ }
+
+ switch resolved := resolved.(type) {
+ case ref.Detached:
+ return resolved, nil
+ case ref.Symbolic:
+ target := strings.TrimSpace(resolved.Target)
+ if target == "" {
+ return ref.Detached{}, fmt.Errorf("refstore/memory: symbolic reference %q has empty target", resolved.Name())
+ }
+
+ cur = target
+ default:
+ return ref.Detached{}, fmt.Errorf("refstore/memory: unsupported reference type %T", resolved)
+ }
+ }
+}
+
+func publicRef(name string, stored storedRef) (ref.Ref, error) { //nolint:ireturn
+ switch stored.kind {
+ case storedDetached:
+ detached := ref.Detached{RefName: name, ID: stored.id}
+ if stored.peeled != nil {
+ peeled := *stored.peeled
+ detached.Peeled = &peeled
+ }
+
+ return detached, nil
+ case storedSymbolic:
+ return ref.Symbolic{RefName: name, Target: stored.target}, nil
+ case storedMissing:
+ return nil, refstore.ErrReferenceNotFound
+ default:
+ return nil, fmt.Errorf("refstore/memory: unsupported stored reference kind %d", stored.kind)
+ }
+}