1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: Apache-2.0
16 "git.curoverse.com/arvados.git/sdk/go/manifest"
26 type keepClient interface {
27 ManifestFileReader(manifest.Manifest, string) (File, error)
30 type collectionFile struct {
32 collection *Collection
37 func (cf *collectionFile) Size() int64 {
41 func (cf *collectionFile) Readdir(count int) ([]os.FileInfo, error) {
45 func (cf *collectionFile) Stat() (os.FileInfo, error) {
46 return collectionDirent{
47 collection: cf.collection,
54 type collectionDir struct {
55 collection *Collection
60 // Readdir implements os.File.
61 func (cd *collectionDir) Readdir(count int) ([]os.FileInfo, error) {
66 } else if len(ret) == 0 {
70 if count >= len(ret) {
74 cd.dirents = cd.dirents[count:]
75 return ret[:count], err
78 // Stat implements os.File.
79 func (cd *collectionDir) Stat() (os.FileInfo, error) {
80 return collectionDirent{
81 collection: cd.collection,
82 name: path.Base(cd.stream),
84 size: int64(len(cd.dirents)),
88 // Close implements os.File.
89 func (cd *collectionDir) Close() error {
93 // Read implements os.File.
94 func (cd *collectionDir) Read([]byte) (int, error) {
98 // Seek implements os.File.
99 func (cd *collectionDir) Seek(int64, int) (int64, error) {
103 // collectionDirent implements os.FileInfo.
104 type collectionDirent struct {
105 collection *Collection
112 // Name implements os.FileInfo.
113 func (e collectionDirent) Name() string {
117 // ModTime implements os.FileInfo.
118 func (e collectionDirent) ModTime() time.Time {
119 if e.collection.ModifiedAt == nil {
122 return *e.collection.ModifiedAt
125 // Mode implements os.FileInfo.
126 func (e collectionDirent) Mode() os.FileMode {
134 // IsDir implements os.FileInfo.
135 func (e collectionDirent) IsDir() bool {
139 // Size implements os.FileInfo.
140 func (e collectionDirent) Size() int64 {
144 // Sys implements os.FileInfo.
145 func (e collectionDirent) Sys() interface{} {
149 // collectionFS implements http.FileSystem.
150 type collectionFS struct {
151 collection *Collection
154 sizes map[string]int64
158 // FileSystem returns an http.FileSystem for the collection.
159 func (c *Collection) FileSystem(client *Client, kc keepClient) http.FileSystem {
160 return &collectionFS{
167 func (c *collectionFS) Open(name string) (http.File, error) {
168 // Ensure name looks the way it does in a manifest.
169 name = path.Clean("/" + name)
170 if name == "/" || name == "./" {
172 } else if strings.HasPrefix(name, "/") {
176 m := manifest.Manifest{Text: c.collection.ManifestText}
178 filesizes := c.fileSizes()
180 // Return a file if it exists.
181 if size, ok := filesizes[name]; ok {
182 reader, err := c.kc.ManifestFileReader(m, name)
186 return &collectionFile{
188 collection: c.collection,
189 name: path.Base(name),
194 // Return a directory if it's the root dir or there are file
196 children := map[string]collectionDirent{}
197 for fnm, size := range filesizes {
198 if !strings.HasPrefix(fnm, name+"/") {
202 ent := fnm[len(name)+1:]
203 if i := strings.Index(ent, "/"); i >= 0 {
208 e.collection = c.collection
214 if len(children) == 0 && name != "." {
215 return nil, os.ErrNotExist
217 dirents := make([]os.FileInfo, 0, len(children))
218 for _, ent := range children {
219 dirents = append(dirents, ent)
221 return &collectionDir{
222 collection: c.collection,
228 // fileSizes returns a map of files that can be opened. Each key
230 func (c *collectionFS) fileSizes() map[string]int64 {
231 c.sizesOnce.Do(func() {
232 c.sizes = map[string]int64{}
233 m := manifest.Manifest{Text: c.collection.ManifestText}
234 for ms := range m.StreamIter() {
235 for _, fss := range ms.FileStreamSegments {
236 c.sizes[ms.StreamName+"/"+fss.Name] += int64(fss.SegLen)