github.com/cozy/cozy-stack@v0.0.0-20240603063001-31110fa4cae1/model/vfs/iter.go (about)

     1  package vfs
     2  
     3  import (
     4  	"path"
     5  
     6  	"github.com/cozy/cozy-stack/pkg/consts"
     7  	"github.com/cozy/cozy-stack/pkg/couchdb"
     8  	"github.com/cozy/cozy-stack/pkg/couchdb/mango"
     9  	"github.com/cozy/cozy-stack/pkg/prefixer"
    10  )
    11  
    12  const iterMaxFetchSize = 256
    13  
    14  // iter is a struct allowing to iterate over the children of a
    15  // directory. The iterator is not thread-safe.
    16  type iter struct {
    17  	db       prefixer.Prefixer
    18  	sel      mango.Filter
    19  	opt      *IteratorOptions
    20  	list     []*DirOrFileDoc
    21  	path     string
    22  	bookmark string
    23  	index    int
    24  	done     bool
    25  }
    26  
    27  // NewIterator return a new iterator.
    28  func NewIterator(db prefixer.Prefixer, dir *DirDoc, opt *IteratorOptions) DirIterator {
    29  	if opt == nil {
    30  		opt = &IteratorOptions{ByFetch: iterMaxFetchSize}
    31  	}
    32  	if opt.ByFetch == 0 || opt.ByFetch > iterMaxFetchSize {
    33  		opt.ByFetch = iterMaxFetchSize
    34  	}
    35  	sel := mango.Equal("dir_id", dir.DocID)
    36  	if opt.AfterID == "" {
    37  		sel = mango.And(sel, mango.Exists("_id"))
    38  	} else {
    39  		sel = mango.And(sel, mango.Gt("_id", opt.AfterID))
    40  	}
    41  	return &iter{
    42  		db:   db,
    43  		sel:  sel,
    44  		opt:  opt,
    45  		path: dir.Fullpath,
    46  	}
    47  }
    48  
    49  // Next should be called to get the next directory or file children of the
    50  // parent directory. If the error is ErrIteratorDone
    51  func (i *iter) Next() (*DirDoc, *FileDoc, error) {
    52  	if i.done {
    53  		return nil, nil, ErrIteratorDone
    54  	}
    55  	if i.index >= len(i.list) {
    56  		if err := i.fetch(); err != nil {
    57  			return nil, nil, err
    58  		}
    59  	}
    60  	d, f := i.list[i.index].Refine()
    61  	if f != nil {
    62  		f.fullpath = path.Join(i.path, f.DocName)
    63  	}
    64  	i.index++
    65  	return d, f, nil
    66  }
    67  
    68  // fetch should be called when the index is out of the list boundary.
    69  func (i *iter) fetch() error {
    70  	l := len(i.list)
    71  	if l > 0 && l < i.opt.ByFetch {
    72  		i.done = true
    73  		return ErrIteratorDone
    74  	}
    75  
    76  	i.index = 0
    77  	i.list = i.list[:0]
    78  
    79  	req := &couchdb.FindRequest{
    80  		UseIndex: "dir-children",
    81  		Selector: i.sel,
    82  		Limit:    i.opt.ByFetch,
    83  		Bookmark: i.bookmark,
    84  	}
    85  	resp, err := couchdb.FindDocsRaw(i.db, consts.Files, req, &i.list)
    86  	if err != nil {
    87  		return err
    88  	}
    89  	if len(i.list) == 0 {
    90  		return ErrIteratorDone
    91  	}
    92  	i.bookmark = resp.Bookmark
    93  	return nil
    94  }