aboutsummaryrefslogtreecommitdiff
path: root/object/resolve
diff options
context:
space:
mode:
authorGravatar Runxi Yu2026-03-25 19:33:32 +0000
committerGravatar Runxi Yu2026-03-25 19:33:57 +0000
commit1aa5cad4c8d6455eeb1f10893549e18bcca11996 (patch)
tree31082a30bde08639fc764c52c3cf2283489f3302 /object/resolve
parentTODO: updates (diff)
signatureNo signature
object/fetch: Rename from object/resolve
Diffstat (limited to 'object/resolve')
-rw-r--r--object/resolve/doc.go5
-rw-r--r--object/resolve/exact_blob.go24
-rw-r--r--object/resolve/exact_blob_reader.go14
-rw-r--r--object/resolve/exact_commit.go24
-rw-r--r--object/resolve/exact_commit_reader.go16
-rw-r--r--object/resolve/exact_object.go18
-rw-r--r--object/resolve/exact_reader.go26
-rw-r--r--object/resolve/exact_tag.go24
-rw-r--r--object/resolve/exact_tag_reader.go16
-rw-r--r--object/resolve/exact_tree.go24
-rw-r--r--object/resolve/exact_tree_reader.go16
-rw-r--r--object/resolve/object_parse.go28
-rw-r--r--object/resolve/path.go103
-rw-r--r--object/resolve/peel_to_blob.go29
-rw-r--r--object/resolve/peel_to_blob_id.go39
-rw-r--r--object/resolve/peel_to_blob_reader.go18
-rw-r--r--object/resolve/peel_to_commit.go29
-rw-r--r--object/resolve/peel_to_commit_id.go39
-rw-r--r--object/resolve/peel_to_commit_reader.go20
-rw-r--r--object/resolve/peel_to_tag.go12
-rw-r--r--object/resolve/peel_to_tag_id.go8
-rw-r--r--object/resolve/peel_to_tag_reader.go20
-rw-r--r--object/resolve/peel_to_tree.go33
-rw-r--r--object/resolve/peel_to_tree_id.go46
-rw-r--r--object/resolve/peel_to_tree_reader.go20
-rw-r--r--object/resolve/resolver.go17
-rw-r--r--object/resolve/treefs.go30
-rw-r--r--object/resolve/treefs_entry.go90
-rw-r--r--object/resolve/treefs_info.go75
-rw-r--r--object/resolve/treefs_new.go17
-rw-r--r--object/resolve/treefs_op.go28
-rw-r--r--object/resolve/treefs_open.go122
-rw-r--r--object/resolve/treefs_path.go29
-rw-r--r--object/resolve/treefs_readdir.go20
-rw-r--r--object/resolve/treefs_readfile.go40
-rw-r--r--object/resolve/treefs_stat.go22
-rw-r--r--object/resolve/treefs_sub.go22
-rw-r--r--object/resolve/treefs_test.go111
38 files changed, 0 insertions, 1274 deletions
diff --git a/object/resolve/doc.go b/object/resolve/doc.go
deleted file mode 100644
index c4197087..00000000
--- a/object/resolve/doc.go
+++ /dev/null
@@ -1,5 +0,0 @@
-// Package resolve resolves stored Git objects by exact type, by peeling
-// tree-ish or commit-ish references, and by path within trees.
-//
-// A Resolver does not take ownership of the underlying object store.
-package resolve
diff --git a/object/resolve/exact_blob.go b/object/resolve/exact_blob.go
deleted file mode 100644
index 2cd8b298..00000000
--- a/object/resolve/exact_blob.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package resolve
-
-import (
- "fmt"
-
- "codeberg.org/lindenii/furgit/object/blob"
- objectid "codeberg.org/lindenii/furgit/object/id"
- "codeberg.org/lindenii/furgit/object/stored"
-)
-
-// ExactBlob reads, parses, and wraps the blob at id.
-func (r *Resolver) ExactBlob(id objectid.ObjectID) (*stored.Stored[*blob.Blob], error) {
- parsed, err := r.parseObject(id)
- if err != nil {
- return nil, err
- }
-
- blob, ok := parsed.(*blob.Blob)
- if !ok {
- return nil, fmt.Errorf("object/resolve: expected blob object %s, got %v", id, parsed.ObjectType())
- }
-
- return stored.New(id, blob), nil
-}
diff --git a/object/resolve/exact_blob_reader.go b/object/resolve/exact_blob_reader.go
deleted file mode 100644
index 5a702888..00000000
--- a/object/resolve/exact_blob_reader.go
+++ /dev/null
@@ -1,14 +0,0 @@
-package resolve
-
-import (
- "io"
-
- objectid "codeberg.org/lindenii/furgit/object/id"
- objecttype "codeberg.org/lindenii/furgit/object/type"
-)
-
-// ExactBlobReader returns a reader for the content of the blob at id,
-// together with its content size in bytes.
-func (r *Resolver) ExactBlobReader(id objectid.ObjectID) (io.ReadCloser, int64, error) {
- return r.exactReader(id, objecttype.TypeBlob, "blob")
-}
diff --git a/object/resolve/exact_commit.go b/object/resolve/exact_commit.go
deleted file mode 100644
index e6b379aa..00000000
--- a/object/resolve/exact_commit.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package resolve
-
-import (
- "fmt"
-
- "codeberg.org/lindenii/furgit/object/commit"
- objectid "codeberg.org/lindenii/furgit/object/id"
- "codeberg.org/lindenii/furgit/object/stored"
-)
-
-// ExactCommit reads, parses, and wraps the commit at id.
-func (r *Resolver) ExactCommit(id objectid.ObjectID) (*stored.Stored[*commit.Commit], error) {
- parsed, err := r.parseObject(id)
- if err != nil {
- return nil, err
- }
-
- commit, ok := parsed.(*commit.Commit)
- if !ok {
- return nil, fmt.Errorf("object/resolve: expected commit object %s, got %v", id, parsed.ObjectType())
- }
-
- return stored.New(id, commit), nil
-}
diff --git a/object/resolve/exact_commit_reader.go b/object/resolve/exact_commit_reader.go
deleted file mode 100644
index 6c05b016..00000000
--- a/object/resolve/exact_commit_reader.go
+++ /dev/null
@@ -1,16 +0,0 @@
-package resolve
-
-import (
- "io"
-
- objectid "codeberg.org/lindenii/furgit/object/id"
- objecttype "codeberg.org/lindenii/furgit/object/type"
-)
-
-// ExactCommitReader returns a reader for the content of the commit at id,
-// together with its content size in bytes.
-//
-// Usage of this method is unusual.
-func (r *Resolver) ExactCommitReader(id objectid.ObjectID) (io.ReadCloser, int64, error) {
- return r.exactReader(id, objecttype.TypeCommit, "commit")
-}
diff --git a/object/resolve/exact_object.go b/object/resolve/exact_object.go
deleted file mode 100644
index db6165f4..00000000
--- a/object/resolve/exact_object.go
+++ /dev/null
@@ -1,18 +0,0 @@
-package resolve
-
-import (
- "codeberg.org/lindenii/furgit/object"
- objectid "codeberg.org/lindenii/furgit/object/id"
- "codeberg.org/lindenii/furgit/object/stored"
-)
-
-// ExactObject reads, parses, and wraps the object at id without constraining
-// its concrete object kind.
-func (r *Resolver) ExactObject(id objectid.ObjectID) (*stored.Stored[object.Object], error) {
- parsed, err := r.parseObject(id)
- if err != nil {
- return nil, err
- }
-
- return stored.New(id, parsed), nil
-}
diff --git a/object/resolve/exact_reader.go b/object/resolve/exact_reader.go
deleted file mode 100644
index cf181038..00000000
--- a/object/resolve/exact_reader.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package resolve
-
-import (
- "fmt"
- "io"
-
- objectid "codeberg.org/lindenii/furgit/object/id"
- objecttype "codeberg.org/lindenii/furgit/object/type"
-)
-
-// exactReader reads one object's content stream and verifies that its header
-// type matches wantType.
-func (r *Resolver) exactReader(id objectid.ObjectID, wantType objecttype.Type, wantName string) (io.ReadCloser, int64, error) {
- gotType, size, rc, err := r.store.ReadReaderContent(id)
- if err != nil {
- return nil, 0, err
- }
-
- if gotType != wantType {
- _ = rc.Close()
-
- return nil, 0, fmt.Errorf("object/resolve: expected %s object %s, got %v", wantName, id, gotType)
- }
-
- return rc, size, nil
-}
diff --git a/object/resolve/exact_tag.go b/object/resolve/exact_tag.go
deleted file mode 100644
index 8c5d22c9..00000000
--- a/object/resolve/exact_tag.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package resolve
-
-import (
- "fmt"
-
- objectid "codeberg.org/lindenii/furgit/object/id"
- "codeberg.org/lindenii/furgit/object/stored"
- "codeberg.org/lindenii/furgit/object/tag"
-)
-
-// ExactTag reads, parses, and wraps the tag at id.
-func (r *Resolver) ExactTag(id objectid.ObjectID) (*stored.Stored[*tag.Tag], error) {
- parsed, err := r.parseObject(id)
- if err != nil {
- return nil, err
- }
-
- tag, ok := parsed.(*tag.Tag)
- if !ok {
- return nil, fmt.Errorf("object/resolve: expected tag object %s, got %v", id, parsed.ObjectType())
- }
-
- return stored.New(id, tag), nil
-}
diff --git a/object/resolve/exact_tag_reader.go b/object/resolve/exact_tag_reader.go
deleted file mode 100644
index e69441d5..00000000
--- a/object/resolve/exact_tag_reader.go
+++ /dev/null
@@ -1,16 +0,0 @@
-package resolve
-
-import (
- "io"
-
- objectid "codeberg.org/lindenii/furgit/object/id"
- objecttype "codeberg.org/lindenii/furgit/object/type"
-)
-
-// ExactTagReader returns a reader for the content of the tag at id,
-// together with its content size in bytes.
-//
-// Usage of this method is unusual.
-func (r *Resolver) ExactTagReader(id objectid.ObjectID) (io.ReadCloser, int64, error) {
- return r.exactReader(id, objecttype.TypeTag, "tag")
-}
diff --git a/object/resolve/exact_tree.go b/object/resolve/exact_tree.go
deleted file mode 100644
index de58ddb1..00000000
--- a/object/resolve/exact_tree.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package resolve
-
-import (
- "fmt"
-
- objectid "codeberg.org/lindenii/furgit/object/id"
- "codeberg.org/lindenii/furgit/object/stored"
- "codeberg.org/lindenii/furgit/object/tree"
-)
-
-// ExactTree reads, parses, and wraps the tree at id.
-func (r *Resolver) ExactTree(id objectid.ObjectID) (*stored.Stored[*tree.Tree], error) {
- parsed, err := r.parseObject(id)
- if err != nil {
- return nil, err
- }
-
- tree, ok := parsed.(*tree.Tree)
- if !ok {
- return nil, fmt.Errorf("object/resolve: expected tree object %s, got %v", id, parsed.ObjectType())
- }
-
- return stored.New(id, tree), nil
-}
diff --git a/object/resolve/exact_tree_reader.go b/object/resolve/exact_tree_reader.go
deleted file mode 100644
index 8d9ec821..00000000
--- a/object/resolve/exact_tree_reader.go
+++ /dev/null
@@ -1,16 +0,0 @@
-package resolve
-
-import (
- "io"
-
- objectid "codeberg.org/lindenii/furgit/object/id"
- objecttype "codeberg.org/lindenii/furgit/object/type"
-)
-
-// ExactTreeReader returns a reader for the content of the tree at id,
-// together with its content size in bytes.
-//
-// Usage of this method is unusual.
-func (r *Resolver) ExactTreeReader(id objectid.ObjectID) (io.ReadCloser, int64, error) {
- return r.exactReader(id, objecttype.TypeTree, "tree")
-}
diff --git a/object/resolve/object_parse.go b/object/resolve/object_parse.go
deleted file mode 100644
index 8e8d8bde..00000000
--- a/object/resolve/object_parse.go
+++ /dev/null
@@ -1,28 +0,0 @@
-package resolve
-
-import (
- "fmt"
-
- "codeberg.org/lindenii/furgit/object"
- objectid "codeberg.org/lindenii/furgit/object/id"
- objecttype "codeberg.org/lindenii/furgit/object/type"
-)
-
-func (r *Resolver) parseObject(id objectid.ObjectID) (object.Object, error) {
- ty, content, err := r.store.ReadBytesContent(id)
- if err != nil {
- return nil, err
- }
-
- parsed, err := object.ParseObjectWithoutHeader(ty, content, id.Algorithm())
- if err != nil {
- tyName, ok := objecttype.Name(ty)
- if !ok {
- tyName = fmt.Sprintf("type %d", ty)
- }
-
- return nil, fmt.Errorf("object/resolve: parse object %s (%s): %w", id, tyName, err)
- }
-
- return parsed, nil
-}
diff --git a/object/resolve/path.go b/object/resolve/path.go
deleted file mode 100644
index d11f3b48..00000000
--- a/object/resolve/path.go
+++ /dev/null
@@ -1,103 +0,0 @@
-package resolve
-
-import (
- "fmt"
-
- objectid "codeberg.org/lindenii/furgit/object/id"
- "codeberg.org/lindenii/furgit/object/tree"
-)
-
-// PathEmptyError indicates that Path received no segments.
-type PathEmptyError struct{}
-
-func (err *PathEmptyError) Error() string {
- return "object/resolve: empty tree path"
-}
-
-// PathSegmentEmptyError indicates that one path segment is empty.
-type PathSegmentEmptyError struct {
- Index int
-}
-
-func (err *PathSegmentEmptyError) Error() string {
- return fmt.Sprintf("object/resolve: empty tree path segment at index %d", err.Index)
-}
-
-// PathNotFoundError indicates that one tree path segment was not found.
-type PathNotFoundError struct {
- Index int
- Name []byte
-}
-
-func (err *PathNotFoundError) Error() string {
- return fmt.Sprintf("object/resolve: tree entry %q not found at index %d", err.Name, err.Index)
-}
-
-// PathNotTreeError indicates that one intermediate path segment was not a tree.
-type PathNotTreeError struct {
- Index int
- Name []byte
-}
-
-func (err *PathNotTreeError) Error() string {
- return fmt.Sprintf("object/resolve: path segment %q at index %d is not a tree", err.Name, err.Index)
-}
-
-// Path resolves parts within the tree identified by root and returns the final
-// tree entry.
-//
-// The root object may be any tree-ish object accepted by PeelToTree.
-//
-// parts must contain at least one path segment. Intermediate path segments
-// must resolve to tree entries. The final entry is returned without loading
-// its object. Path segments may not contain \x00.
-//
-// The path cannot be accurately represented as a string or a single []byte
-// because Git tree entry names may include slashes. While []string is
-// technically possible (since Go strings are not necessarily UTF-8), they
-// do often imply UTF-8 in practice, which would be undesirable.
-//
-// If your entry names are valid UTF-8 and uses / solely as segment separators,
-// it may be convenient to use TreeFS for an io/fs.FS-like interface.
-func (r *Resolver) Path(root objectid.ObjectID, parts [][]byte) (tree.TreeEntry, error) {
- if len(parts) == 0 {
- return tree.TreeEntry{}, &PathEmptyError{}
- }
-
- current, err := r.PeelToTree(root)
- if err != nil {
- return tree.TreeEntry{}, err
- }
-
- for i, part := range parts {
- if len(part) == 0 {
- return tree.TreeEntry{}, &PathSegmentEmptyError{Index: i}
- }
-
- entry := current.Object().Entry(part)
- if entry == nil {
- return tree.TreeEntry{}, &PathNotFoundError{
- Index: i,
- Name: append([]byte(nil), part...),
- }
- }
-
- if i == len(parts)-1 {
- return *entry, nil
- }
-
- if entry.Mode != tree.FileModeDir {
- return tree.TreeEntry{}, &PathNotTreeError{
- Index: i,
- Name: append([]byte(nil), part...),
- }
- }
-
- current, err = r.ExactTree(entry.ID)
- if err != nil {
- return tree.TreeEntry{}, err
- }
- }
-
- return tree.TreeEntry{}, &PathNotFoundError{Index: len(parts) - 1}
-}
diff --git a/object/resolve/peel_to_blob.go b/object/resolve/peel_to_blob.go
deleted file mode 100644
index c8aec1ad..00000000
--- a/object/resolve/peel_to_blob.go
+++ /dev/null
@@ -1,29 +0,0 @@
-package resolve
-
-import (
- "fmt"
-
- "codeberg.org/lindenii/furgit/object/blob"
- objectid "codeberg.org/lindenii/furgit/object/id"
- "codeberg.org/lindenii/furgit/object/stored"
- "codeberg.org/lindenii/furgit/object/tag"
-)
-
-// PeelToBlob peels tags until it reaches a blob.
-func (r *Resolver) PeelToBlob(id objectid.ObjectID) (*stored.Stored[*blob.Blob], error) {
- for {
- obj, err := r.ExactObject(id)
- if err != nil {
- return nil, err
- }
-
- switch parsed := obj.Object().(type) {
- case *blob.Blob:
- return stored.New(id, parsed), nil
- case *tag.Tag:
- id = parsed.Target
- default:
- return nil, fmt.Errorf("object/resolve: expected blob-ish object %s, got %v", id, parsed.ObjectType())
- }
- }
-}
diff --git a/object/resolve/peel_to_blob_id.go b/object/resolve/peel_to_blob_id.go
deleted file mode 100644
index c3467cf2..00000000
--- a/object/resolve/peel_to_blob_id.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package resolve
-
-import (
- "fmt"
-
- objectid "codeberg.org/lindenii/furgit/object/id"
- objecttype "codeberg.org/lindenii/furgit/object/type"
-)
-
-// PeelToBlobID peels tags until it reaches a blob object ID.
-func (r *Resolver) PeelToBlobID(id objectid.ObjectID) (objectid.ObjectID, error) {
- for {
- ty, _, err := r.store.ReadHeader(id)
- if err != nil {
- return objectid.ObjectID{}, err
- }
-
- switch ty {
- case objecttype.TypeBlob:
- return id, nil
- case objecttype.TypeTag:
- tag, err := r.ExactTag(id)
- if err != nil {
- return objectid.ObjectID{}, err
- }
-
- id = tag.Object().Target
- case objecttype.TypeInvalid,
- objecttype.TypeCommit,
- objecttype.TypeTree,
- objecttype.TypeFuture,
- objecttype.TypeOfsDelta,
- objecttype.TypeRefDelta:
- return objectid.ObjectID{}, fmt.Errorf("object/resolve: expected blob-ish object %s, got %v", id, ty)
- default:
- return objectid.ObjectID{}, fmt.Errorf("object/resolve: expected blob-ish object %s, got %v", id, ty)
- }
- }
-}
diff --git a/object/resolve/peel_to_blob_reader.go b/object/resolve/peel_to_blob_reader.go
deleted file mode 100644
index d3bc7f49..00000000
--- a/object/resolve/peel_to_blob_reader.go
+++ /dev/null
@@ -1,18 +0,0 @@
-package resolve
-
-import (
- "io"
-
- objectid "codeberg.org/lindenii/furgit/object/id"
-)
-
-// PeelToBlobReader returns a reader for the content of the peeled blob at id,
-// together with its content size in bytes.
-func (r *Resolver) PeelToBlobReader(id objectid.ObjectID) (io.ReadCloser, int64, error) {
- blobID, err := r.PeelToBlobID(id)
- if err != nil {
- return nil, 0, err
- }
-
- return r.ExactBlobReader(blobID)
-}
diff --git a/object/resolve/peel_to_commit.go b/object/resolve/peel_to_commit.go
deleted file mode 100644
index 0272dd83..00000000
--- a/object/resolve/peel_to_commit.go
+++ /dev/null
@@ -1,29 +0,0 @@
-package resolve
-
-import (
- "fmt"
-
- "codeberg.org/lindenii/furgit/object/commit"
- objectid "codeberg.org/lindenii/furgit/object/id"
- "codeberg.org/lindenii/furgit/object/stored"
- "codeberg.org/lindenii/furgit/object/tag"
-)
-
-// PeelToCommit peels tags until it reaches a commit.
-func (r *Resolver) PeelToCommit(id objectid.ObjectID) (*stored.Stored[*commit.Commit], error) {
- for {
- obj, err := r.ExactObject(id)
- if err != nil {
- return nil, err
- }
-
- switch parsed := obj.Object().(type) {
- case *commit.Commit:
- return stored.New(id, parsed), nil
- case *tag.Tag:
- id = parsed.Target
- default:
- return nil, fmt.Errorf("object/resolve: expected commit-ish object %s, got %v", id, parsed.ObjectType())
- }
- }
-}
diff --git a/object/resolve/peel_to_commit_id.go b/object/resolve/peel_to_commit_id.go
deleted file mode 100644
index 972823ce..00000000
--- a/object/resolve/peel_to_commit_id.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package resolve
-
-import (
- "fmt"
-
- objectid "codeberg.org/lindenii/furgit/object/id"
- objecttype "codeberg.org/lindenii/furgit/object/type"
-)
-
-// PeelToCommitID peels tags until it reaches a commit object ID.
-func (r *Resolver) PeelToCommitID(id objectid.ObjectID) (objectid.ObjectID, error) {
- for {
- ty, _, err := r.store.ReadHeader(id)
- if err != nil {
- return objectid.ObjectID{}, err
- }
-
- switch ty {
- case objecttype.TypeCommit:
- return id, nil
- case objecttype.TypeTag:
- tag, err := r.ExactTag(id)
- if err != nil {
- return objectid.ObjectID{}, err
- }
-
- id = tag.Object().Target
- case objecttype.TypeInvalid,
- objecttype.TypeTree,
- objecttype.TypeBlob,
- objecttype.TypeFuture,
- objecttype.TypeOfsDelta,
- objecttype.TypeRefDelta:
- return objectid.ObjectID{}, fmt.Errorf("object/resolve: expected commit-ish object %s, got %v", id, ty)
- default:
- return objectid.ObjectID{}, fmt.Errorf("object/resolve: expected commit-ish object %s, got %v", id, ty)
- }
- }
-}
diff --git a/object/resolve/peel_to_commit_reader.go b/object/resolve/peel_to_commit_reader.go
deleted file mode 100644
index 6972eff3..00000000
--- a/object/resolve/peel_to_commit_reader.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package resolve
-
-import (
- "io"
-
- objectid "codeberg.org/lindenii/furgit/object/id"
-)
-
-// PeelToCommitReader returns a reader for the content of the peeled commit at
-// id, together with its content size in bytes.
-//
-// Usage of this method is unusual.
-func (r *Resolver) PeelToCommitReader(id objectid.ObjectID) (io.ReadCloser, int64, error) {
- commitID, err := r.PeelToCommitID(id)
- if err != nil {
- return nil, 0, err
- }
-
- return r.ExactCommitReader(commitID)
-}
diff --git a/object/resolve/peel_to_tag.go b/object/resolve/peel_to_tag.go
deleted file mode 100644
index e131f4c1..00000000
--- a/object/resolve/peel_to_tag.go
+++ /dev/null
@@ -1,12 +0,0 @@
-package resolve
-
-import (
- objectid "codeberg.org/lindenii/furgit/object/id"
- "codeberg.org/lindenii/furgit/object/stored"
- "codeberg.org/lindenii/furgit/object/tag"
-)
-
-// PeelToTag returns the tag at id without further peeling.
-func (r *Resolver) PeelToTag(id objectid.ObjectID) (*stored.Stored[*tag.Tag], error) {
- return r.ExactTag(id)
-}
diff --git a/object/resolve/peel_to_tag_id.go b/object/resolve/peel_to_tag_id.go
deleted file mode 100644
index 275ef561..00000000
--- a/object/resolve/peel_to_tag_id.go
+++ /dev/null
@@ -1,8 +0,0 @@
-package resolve
-
-import objectid "codeberg.org/lindenii/furgit/object/id"
-
-// PeelToTagID returns id unchanged.
-func (r *Resolver) PeelToTagID(id objectid.ObjectID) (objectid.ObjectID, error) {
- return id, nil
-}
diff --git a/object/resolve/peel_to_tag_reader.go b/object/resolve/peel_to_tag_reader.go
deleted file mode 100644
index 77279511..00000000
--- a/object/resolve/peel_to_tag_reader.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package resolve
-
-import (
- "io"
-
- objectid "codeberg.org/lindenii/furgit/object/id"
-)
-
-// PeelToTagReader returns a reader for the content of the tag at id,
-// together with its content size in bytes.
-//
-// Usage of this method is unusual.
-func (r *Resolver) PeelToTagReader(id objectid.ObjectID) (io.ReadCloser, int64, error) {
- tagID, err := r.PeelToTagID(id)
- if err != nil {
- return nil, 0, err
- }
-
- return r.ExactTagReader(tagID)
-}
diff --git a/object/resolve/peel_to_tree.go b/object/resolve/peel_to_tree.go
deleted file mode 100644
index 2f2da4d7..00000000
--- a/object/resolve/peel_to_tree.go
+++ /dev/null
@@ -1,33 +0,0 @@
-package resolve
-
-import (
- "fmt"
-
- "codeberg.org/lindenii/furgit/object/commit"
- objectid "codeberg.org/lindenii/furgit/object/id"
- "codeberg.org/lindenii/furgit/object/stored"
- "codeberg.org/lindenii/furgit/object/tag"
- "codeberg.org/lindenii/furgit/object/tree"
-)
-
-// PeelToTree peels tags until it reaches a tree or commit. If it reaches a
-// commit, it returns the commit's root tree.
-func (r *Resolver) PeelToTree(id objectid.ObjectID) (*stored.Stored[*tree.Tree], error) {
- for {
- obj, err := r.ExactObject(id)
- if err != nil {
- return nil, err
- }
-
- switch parsed := obj.Object().(type) {
- case *tree.Tree:
- return stored.New(id, parsed), nil
- case *commit.Commit:
- return r.ExactTree(parsed.Tree)
- case *tag.Tag:
- id = parsed.Target
- default:
- return nil, fmt.Errorf("object/resolve: expected tree-ish object %s, got %v", id, parsed.ObjectType())
- }
- }
-}
diff --git a/object/resolve/peel_to_tree_id.go b/object/resolve/peel_to_tree_id.go
deleted file mode 100644
index bacee65e..00000000
--- a/object/resolve/peel_to_tree_id.go
+++ /dev/null
@@ -1,46 +0,0 @@
-package resolve
-
-import (
- "fmt"
-
- objectid "codeberg.org/lindenii/furgit/object/id"
- objecttype "codeberg.org/lindenii/furgit/object/type"
-)
-
-// PeelToTreeID peels tags until it reaches a tree object ID, or a commit whose
-// root tree object ID is then returned.
-func (r *Resolver) PeelToTreeID(id objectid.ObjectID) (objectid.ObjectID, error) {
- for {
- ty, _, err := r.store.ReadHeader(id)
- if err != nil {
- return objectid.ObjectID{}, err
- }
-
- switch ty {
- case objecttype.TypeTree:
- return id, nil
- case objecttype.TypeCommit:
- commit, err := r.ExactCommit(id)
- if err != nil {
- return objectid.ObjectID{}, err
- }
-
- return commit.Object().Tree, nil
- case objecttype.TypeTag:
- tag, err := r.ExactTag(id)
- if err != nil {
- return objectid.ObjectID{}, err
- }
-
- id = tag.Object().Target
- case objecttype.TypeInvalid,
- objecttype.TypeBlob,
- objecttype.TypeFuture,
- objecttype.TypeOfsDelta,
- objecttype.TypeRefDelta:
- return objectid.ObjectID{}, fmt.Errorf("object/resolve: expected tree-ish object %s, got %v", id, ty)
- default:
- return objectid.ObjectID{}, fmt.Errorf("object/resolve: expected tree-ish object %s, got %v", id, ty)
- }
- }
-}
diff --git a/object/resolve/peel_to_tree_reader.go b/object/resolve/peel_to_tree_reader.go
deleted file mode 100644
index 515224ed..00000000
--- a/object/resolve/peel_to_tree_reader.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package resolve
-
-import (
- "io"
-
- objectid "codeberg.org/lindenii/furgit/object/id"
-)
-
-// PeelToTreeReader returns a reader for the content of the peeled tree at id,
-// together with its content size in bytes.
-//
-// Usage of this method is unusual.
-func (r *Resolver) PeelToTreeReader(id objectid.ObjectID) (io.ReadCloser, int64, error) {
- treeID, err := r.PeelToTreeID(id)
- if err != nil {
- return nil, 0, err
- }
-
- return r.ExactTreeReader(treeID)
-}
diff --git a/object/resolve/resolver.go b/object/resolve/resolver.go
deleted file mode 100644
index f5e4e8c3..00000000
--- a/object/resolve/resolver.go
+++ /dev/null
@@ -1,17 +0,0 @@
-package resolve
-
-import objectstorer "codeberg.org/lindenii/furgit/object/storer"
-
-// Resolver resolves parsed and streamed objects from an object store.
-//
-// A Resolver does not take ownership of the store and does not close it.
-type Resolver struct {
- store objectstorer.Store
-}
-
-// New returns a Resolver that reads objects from store.
-//
-// The returned Resolver does not take ownership of store.
-func New(store objectstorer.Store) *Resolver {
- return &Resolver{store: store}
-}
diff --git a/object/resolve/treefs.go b/object/resolve/treefs.go
deleted file mode 100644
index a080d56d..00000000
--- a/object/resolve/treefs.go
+++ /dev/null
@@ -1,30 +0,0 @@
-package resolve
-
-import (
- "io/fs"
-
- objectid "codeberg.org/lindenii/furgit/object/id"
- "codeberg.org/lindenii/furgit/object/tree"
-)
-
-// TreeFS exposes one Git tree as an fs.FS.
-//
-// TreeFS interprets names using io/fs path rules. Those rules do not match raw
-// Git tree entry naming exactly: names are UTF-8, slash-separated, and must be
-// valid fs.FS paths. Tree entries that cannot be represented under those rules
-// are not addressable through this API.
-//
-// TreeFS does not take ownership of its Resolver.
-type TreeFS struct {
- resolver *Resolver
- rootTree objectid.ObjectID
- rootEntry *tree.TreeEntry
-}
-
-var (
- _ fs.FS = (*TreeFS)(nil)
- _ fs.ReadFileFS = (*TreeFS)(nil)
- _ fs.ReadDirFS = (*TreeFS)(nil)
- _ fs.StatFS = (*TreeFS)(nil)
- _ fs.SubFS = (*TreeFS)(nil)
-)
diff --git a/object/resolve/treefs_entry.go b/object/resolve/treefs_entry.go
deleted file mode 100644
index 6d23e282..00000000
--- a/object/resolve/treefs_entry.go
+++ /dev/null
@@ -1,90 +0,0 @@
-package resolve
-
-import (
- "errors"
- "fmt"
- "io/fs"
-
- objectid "codeberg.org/lindenii/furgit/object/id"
- "codeberg.org/lindenii/furgit/object/tree"
-)
-
-func (treeFS *TreeFS) resolvePath(op treeFSOp, name string) (treeEntryValue, error) {
- if !treeFSValidPath(name) {
- return treeEntryValue{}, treeFSPathError(op, name, fs.ErrInvalid)
- }
-
- if name == "." {
- return treeEntryValue{
- name: ".",
- mode: tree.FileModeDir,
- treeID: treeFS.rootTree,
- treeEntry: treeFS.rootEntry,
- }, nil
- }
-
- entry, err := treeFS.resolver.Path(treeFS.rootTree, treeFSSplitPath(name))
- if err != nil {
- return treeEntryValue{}, treeFS.pathResolveError(op, name, err)
- }
-
- return treeEntryValue{
- name: string(entry.Name),
- mode: entry.Mode,
- objectID: entry.ID,
- treeEntry: &entry,
- }, nil
-}
-
-func (treeFS *TreeFS) pathResolveError(op treeFSOp, name string, err error) error {
- if _, ok := errors.AsType[*PathNotFoundError](err); ok {
- return treeFSPathError(op, name, fs.ErrNotExist)
- }
-
- if _, ok := errors.AsType[*PathNotTreeError](err); ok {
- return treeFSPathError(op, name, fs.ErrInvalid)
- }
-
- if _, ok := errors.AsType[*PathEmptyError](err); ok {
- return treeFSPathError(op, name, fs.ErrInvalid)
- }
-
- if _, ok := errors.AsType[*PathSegmentEmptyError](err); ok {
- return treeFSPathError(op, name, fs.ErrInvalid)
- }
-
- return treeFSPathError(op, name, err)
-}
-
-type treeEntryValue struct {
- name string
- mode tree.FileMode
- objectID objectid.ObjectID
- treeID objectid.ObjectID
- treeEntry *tree.TreeEntry
-}
-
-func (entry treeEntryValue) isDir() bool {
- return entry.mode == tree.FileModeDir
-}
-
-func (entry treeEntryValue) blobSize(resolve *Resolver) (int64, error) {
- _, size, err := resolve.store.ReadHeader(entry.objectID)
- if err != nil {
- return 0, err
- }
-
- return size, nil
-}
-
-func (entry treeEntryValue) subtreeID() (objectid.ObjectID, error) {
- if entry.name == "." {
- return entry.treeID, nil
- }
-
- if entry.mode != tree.FileModeDir {
- return objectid.ObjectID{}, fmt.Errorf("object/resolve: path %q is not a tree", entry.name)
- }
-
- return entry.objectID, nil
-}
diff --git a/object/resolve/treefs_info.go b/object/resolve/treefs_info.go
deleted file mode 100644
index f8eb1e9e..00000000
--- a/object/resolve/treefs_info.go
+++ /dev/null
@@ -1,75 +0,0 @@
-package resolve
-
-import (
- "io/fs"
- "time"
-
- "codeberg.org/lindenii/furgit/object/tree"
-)
-
-type treeFSInfo struct {
- name string
- mode fs.FileMode
- size int64
- sys any
- isDir bool
-}
-
-var (
- _ fs.FileInfo = (*treeFSInfo)(nil)
- _ fs.DirEntry = (*treeFSInfo)(nil)
-)
-
-func (info *treeFSInfo) Name() string { return info.name }
-func (info *treeFSInfo) Size() int64 { return info.size }
-func (info *treeFSInfo) Mode() fs.FileMode { return info.mode }
-func (info *treeFSInfo) Type() fs.FileMode { return info.mode.Type() }
-func (info *treeFSInfo) IsDir() bool { return info.isDir }
-func (info *treeFSInfo) ModTime() time.Time { return time.Time{} }
-func (info *treeFSInfo) Sys() any { return info.sys }
-func (info *treeFSInfo) Info() (fs.FileInfo, error) {
- return info, nil
-}
-
-func treeFSEntryMode(mode tree.FileMode) fs.FileMode {
- switch mode {
- case tree.FileModeDir:
- return fs.ModeDir | 0o555
- case tree.FileModeRegular:
- return 0o444
- case tree.FileModeExecutable:
- return 0o555
- case tree.FileModeSymlink:
- return fs.ModeSymlink | 0o444
- case tree.FileModeGitlink:
- return fs.ModeIrregular
- default:
- return fs.ModeIrregular
- }
-}
-
-func (treeFS *TreeFS) statEntry(entry treeEntryValue) (*treeFSInfo, error) {
- size := int64(0)
-
- if entry.mode == tree.FileModeRegular || entry.mode == tree.FileModeExecutable || entry.mode == tree.FileModeSymlink {
- var err error
-
- size, err = entry.blobSize(treeFS.resolver)
- if err != nil {
- return nil, err
- }
- }
-
- var sys any
- if entry.treeEntry != nil {
- sys = *entry.treeEntry
- }
-
- return &treeFSInfo{
- name: entry.name,
- mode: treeFSEntryMode(entry.mode),
- size: size,
- sys: sys,
- isDir: entry.isDir(),
- }, nil
-}
diff --git a/object/resolve/treefs_new.go b/object/resolve/treefs_new.go
deleted file mode 100644
index 9f5fe77d..00000000
--- a/object/resolve/treefs_new.go
+++ /dev/null
@@ -1,17 +0,0 @@
-package resolve
-
-import objectid "codeberg.org/lindenii/furgit/object/id"
-
-// TreeFS returns a new filesystem view rooted at root, which may be any
-// tree-ish object accepted by PeelToTreeID.
-func (r *Resolver) TreeFS(root objectid.ObjectID) (*TreeFS, error) {
- rootTree, err := r.PeelToTreeID(root)
- if err != nil {
- return nil, err
- }
-
- return &TreeFS{
- resolver: r,
- rootTree: rootTree,
- }, nil
-}
diff --git a/object/resolve/treefs_op.go b/object/resolve/treefs_op.go
deleted file mode 100644
index ed93ec85..00000000
--- a/object/resolve/treefs_op.go
+++ /dev/null
@@ -1,28 +0,0 @@
-package resolve
-
-type treeFSOp uint8
-
-const (
- treeFSOpOpen treeFSOp = iota
- treeFSOpReadFile
- treeFSOpReadDir
- treeFSOpStat
- treeFSOpSub
-)
-
-func (op treeFSOp) pathErrorOp() string {
- switch op {
- case treeFSOpOpen:
- return "open"
- case treeFSOpReadFile:
- return "readfile"
- case treeFSOpReadDir:
- return "readdir"
- case treeFSOpStat:
- return "stat"
- case treeFSOpSub:
- return "sub"
- default:
- return "treefs"
- }
-}
diff --git a/object/resolve/treefs_open.go b/object/resolve/treefs_open.go
deleted file mode 100644
index 8e2b3588..00000000
--- a/object/resolve/treefs_open.go
+++ /dev/null
@@ -1,122 +0,0 @@
-package resolve
-
-import (
- "fmt"
- "io"
- "io/fs"
-
- "codeberg.org/lindenii/furgit/object/tree"
-)
-
-// Open opens name for reading.
-//
-// Directories are returned as fs.ReadDirFile values. Gitlink entries are not
-// readable through TreeFS.
-func (treeFS *TreeFS) Open(name string) (fs.File, error) {
- entry, err := treeFS.resolvePath(treeFSOpOpen, name)
- if err != nil {
- return nil, err
- }
-
- info, err := treeFS.statEntry(entry)
- if err != nil {
- return nil, treeFSPathError(treeFSOpOpen, name, err)
- }
-
- if entry.isDir() {
- treeID, err := entry.subtreeID()
- if err != nil {
- return nil, treeFSPathError(treeFSOpOpen, name, err)
- }
-
- tree, err := treeFS.resolver.ExactTree(treeID)
- if err != nil {
- return nil, treeFSPathError(treeFSOpOpen, name, err)
- }
-
- entries := make([]fs.DirEntry, 0, len(tree.Object().Entries))
- for _, child := range tree.Object().Entries {
- childEntry := treeEntryValue{
- name: string(child.Name),
- mode: child.Mode,
- objectID: child.ID,
- treeEntry: &child,
- }
-
- childInfo, err := treeFS.statEntry(childEntry)
- if err != nil {
- return nil, treeFSPathError(treeFSOpOpen, name, err)
- }
-
- entries = append(entries, childInfo)
- }
-
- return &treeFSDir{
- info: info,
- entries: entries,
- }, nil
- }
-
- if entry.mode == tree.FileModeGitlink {
- return nil, treeFSPathError(treeFSOpOpen, name, fmt.Errorf("object/resolve: gitlink entries are not readable as files"))
- }
-
- reader, _, err := treeFS.resolver.ExactBlobReader(entry.objectID)
- if err != nil {
- return nil, treeFSPathError(treeFSOpOpen, name, err)
- }
-
- return &treeFSBlob{
- info: info,
- reader: reader,
- }, nil
-}
-
-type treeFSBlob struct {
- info *treeFSInfo
- reader io.ReadCloser
-}
-
-var _ fs.File = (*treeFSBlob)(nil)
-
-func (file *treeFSBlob) Stat() (fs.FileInfo, error) { return file.info, nil }
-func (file *treeFSBlob) Read(p []byte) (int, error) { return file.reader.Read(p) }
-func (file *treeFSBlob) Close() error { return file.reader.Close() }
-
-type treeFSDir struct {
- info *treeFSInfo
- entries []fs.DirEntry
- offset int
-}
-
-var (
- _ fs.File = (*treeFSDir)(nil)
- _ fs.ReadDirFile = (*treeFSDir)(nil)
-)
-
-func (dir *treeFSDir) Stat() (fs.FileInfo, error) { return dir.info, nil }
-func (dir *treeFSDir) Close() error { return nil }
-
-func (dir *treeFSDir) Read(_ []byte) (int, error) {
- return 0, fs.ErrInvalid
-}
-
-func (dir *treeFSDir) ReadDir(n int) ([]fs.DirEntry, error) {
- if dir.offset >= len(dir.entries) && n > 0 {
- return nil, io.EOF
- }
-
- if n <= 0 {
- out := append([]fs.DirEntry(nil), dir.entries[dir.offset:]...)
- dir.offset = len(dir.entries)
-
- return out, nil
- }
-
- end := min(dir.offset+n, len(dir.entries))
-
- out := append([]fs.DirEntry(nil), dir.entries[dir.offset:end]...)
- dir.offset = end
-
- return out, nil
-}
diff --git a/object/resolve/treefs_path.go b/object/resolve/treefs_path.go
deleted file mode 100644
index c35791cb..00000000
--- a/object/resolve/treefs_path.go
+++ /dev/null
@@ -1,29 +0,0 @@
-package resolve
-
-import (
- "io/fs"
- "strings"
-)
-
-func treeFSValidPath(name string) bool {
- return name == "." || fs.ValidPath(name)
-}
-
-func treeFSSplitPath(name string) [][]byte {
- if name == "." {
- return nil
- }
-
- parts := strings.Split(name, "/")
-
- out := make([][]byte, len(parts))
- for i, part := range parts {
- out[i] = []byte(part)
- }
-
- return out
-}
-
-func treeFSPathError(op treeFSOp, path string, err error) error {
- return &fs.PathError{Op: op.pathErrorOp(), Path: path, Err: err}
-}
diff --git a/object/resolve/treefs_readdir.go b/object/resolve/treefs_readdir.go
deleted file mode 100644
index 5516de33..00000000
--- a/object/resolve/treefs_readdir.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package resolve
-
-import "io/fs"
-
-// ReadDir reads and returns all directory entries for name.
-func (treeFS *TreeFS) ReadDir(name string) ([]fs.DirEntry, error) {
- file, err := treeFS.Open(name)
- if err != nil {
- return nil, err
- }
-
- defer func() { _ = file.Close() }()
-
- readDirFile, ok := file.(fs.ReadDirFile)
- if !ok {
- return nil, treeFSPathError(treeFSOpReadDir, name, fs.ErrInvalid)
- }
-
- return readDirFile.ReadDir(-1)
-}
diff --git a/object/resolve/treefs_readfile.go b/object/resolve/treefs_readfile.go
deleted file mode 100644
index e1d514a3..00000000
--- a/object/resolve/treefs_readfile.go
+++ /dev/null
@@ -1,40 +0,0 @@
-package resolve
-
-import (
- "fmt"
- "io"
-
- "codeberg.org/lindenii/furgit/object/tree"
-)
-
-// ReadFile reads the blob contents at name.
-//
-// Directories and gitlink entries are not readable through TreeFS.
-func (treeFS *TreeFS) ReadFile(name string) ([]byte, error) {
- entry, err := treeFS.resolvePath(treeFSOpReadFile, name)
- if err != nil {
- return nil, err
- }
-
- if entry.isDir() {
- return nil, treeFSPathError(treeFSOpReadFile, name, fmt.Errorf("is a directory"))
- }
-
- if entry.mode == tree.FileModeGitlink {
- return nil, treeFSPathError(treeFSOpReadFile, name, fmt.Errorf("object/resolve: gitlink entries are not readable as files"))
- }
-
- reader, _, err := treeFS.resolver.ExactBlobReader(entry.objectID)
- if err != nil {
- return nil, treeFSPathError(treeFSOpReadFile, name, err)
- }
-
- defer func() { _ = reader.Close() }()
-
- data, err := io.ReadAll(reader)
- if err != nil {
- return nil, treeFSPathError(treeFSOpReadFile, name, err)
- }
-
- return data, nil
-}
diff --git a/object/resolve/treefs_stat.go b/object/resolve/treefs_stat.go
deleted file mode 100644
index 396dfbae..00000000
--- a/object/resolve/treefs_stat.go
+++ /dev/null
@@ -1,22 +0,0 @@
-package resolve
-
-import "io/fs"
-
-// Stat returns synthetic file metadata for name.
-//
-// TreeFS metadata reflects Git tree entry mode and blob size where applicable.
-// It does not represent filesystem stat metadata: ModTime is zero, ownership is
-// unavailable, and Sys returns the underlying tree.TreeEntry when one exists.
-func (treeFS *TreeFS) Stat(name string) (fs.FileInfo, error) {
- entry, err := treeFS.resolvePath(treeFSOpStat, name)
- if err != nil {
- return nil, err
- }
-
- info, err := treeFS.statEntry(entry)
- if err != nil {
- return nil, treeFSPathError(treeFSOpStat, name, err)
- }
-
- return info, nil
-}
diff --git a/object/resolve/treefs_sub.go b/object/resolve/treefs_sub.go
deleted file mode 100644
index f0eefdc5..00000000
--- a/object/resolve/treefs_sub.go
+++ /dev/null
@@ -1,22 +0,0 @@
-package resolve
-
-import "io/fs"
-
-// Sub returns a new TreeFS rooted at dir.
-func (treeFS *TreeFS) Sub(dir string) (fs.FS, error) {
- entry, err := treeFS.resolvePath(treeFSOpSub, dir)
- if err != nil {
- return nil, err
- }
-
- treeID, err := entry.subtreeID()
- if err != nil {
- return nil, treeFSPathError(treeFSOpSub, dir, fs.ErrInvalid)
- }
-
- return &TreeFS{
- resolver: treeFS.resolver,
- rootTree: treeID,
- rootEntry: entry.treeEntry,
- }, nil
-}
diff --git a/object/resolve/treefs_test.go b/object/resolve/treefs_test.go
deleted file mode 100644
index 59eebd5e..00000000
--- a/object/resolve/treefs_test.go
+++ /dev/null
@@ -1,111 +0,0 @@
-package resolve_test
-
-import (
- "errors"
- "io/fs"
- "testing"
-
- "codeberg.org/lindenii/furgit/internal/testgit"
- objectid "codeberg.org/lindenii/furgit/object/id"
- "codeberg.org/lindenii/furgit/object/resolve"
- "codeberg.org/lindenii/furgit/object/tree"
- "codeberg.org/lindenii/furgit/repository"
-)
-
-func TestTreeFS(t *testing.T) {
- t.Parallel()
-
- testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper
- t.Parallel()
-
- repoData := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo})
- repoData.WriteFile(t, "plain.txt", []byte("plain\n"), 0o644)
- repoData.WriteFileAll(t, "dir/exec.sh", []byte("#!/bin/sh\nexit 0\n"), 0o755, 0o755)
- repoData.SymbolicRef(t, "HEAD", "refs/heads/main")
- _ = repoData.Run(t, "add", ".")
- treeHex := repoData.Run(t, "write-tree")
-
- treeID, err := objectid.ParseHex(algo, treeHex)
- if err != nil {
- t.Fatalf("ParseHex(write-tree): %v", err)
- }
-
- commitID := repoData.CommitTree(t, treeID, "treefs")
-
- root := repoData.OpenGitRoot(t)
-
- repo, err := repository.Open(root)
- if err != nil {
- t.Fatalf("repository.Open: %v", err)
- }
-
- defer func() { _ = repo.Close() }()
-
- resolver := resolve.New(repo.Objects())
-
- treeFS, err := resolver.TreeFS(commitID)
- if err != nil {
- t.Fatalf("resolver.TreeFS: %v", err)
- }
-
- content, err := treeFS.ReadFile("plain.txt")
- if err != nil {
- t.Fatalf("ReadFile(plain.txt): %v", err)
- }
-
- if string(content) != "plain\n" {
- t.Fatalf("ReadFile(plain.txt) = %q, want %q", string(content), "plain\n")
- }
-
- entries, err := treeFS.ReadDir(".")
- if err != nil {
- t.Fatalf("ReadDir(.): %v", err)
- }
-
- if len(entries) != 2 {
- t.Fatalf("len(ReadDir(.)) = %d, want 2", len(entries))
- }
-
- info, err := treeFS.Stat("plain.txt")
- if err != nil {
- t.Fatalf("Stat(plain.txt): %v", err)
- }
-
- entry, ok := info.Sys().(tree.TreeEntry)
- if !ok {
- t.Fatalf("Stat(plain.txt).Sys() type = %T, want tree.TreeEntry", info.Sys())
- }
-
- if entry.Mode != tree.FileModeRegular {
- t.Fatalf("Stat(plain.txt).Sys().Mode = %o, want %o", entry.Mode, tree.FileModeRegular)
- }
-
- subFS, err := treeFS.Sub("dir")
- if err != nil {
- t.Fatalf("Sub(dir): %v", err)
- }
-
- subReadFileFS, ok := subFS.(fs.ReadFileFS)
- if !ok {
- t.Fatalf("Sub(dir) type does not implement fs.ReadFileFS")
- }
-
- subContent, err := subReadFileFS.ReadFile("exec.sh")
- if err != nil {
- t.Fatalf("Sub(dir).ReadFile(exec.sh): %v", err)
- }
-
- if string(subContent) != "#!/bin/sh\nexit 0\n" {
- t.Fatalf("Sub(dir).ReadFile(exec.sh) = %q", string(subContent))
- }
-
- _, err = treeFS.ReadFile("dir")
- if err == nil {
- t.Fatal("ReadFile(dir) unexpectedly succeeded")
- }
-
- if _, ok := errors.AsType[*fs.PathError](err); !ok {
- t.Fatalf("ReadFile(dir) err type = %T, want *fs.PathError", err)
- }
- })
-}