1// Copyright 2020 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package fs
6
7import (
8	"errors"
9	"internal/bytealg"
10	"slices"
11)
12
13// ReadDirFS is the interface implemented by a file system
14// that provides an optimized implementation of [ReadDir].
15type ReadDirFS interface {
16	FS
17
18	// ReadDir reads the named directory
19	// and returns a list of directory entries sorted by filename.
20	ReadDir(name string) ([]DirEntry, error)
21}
22
23// ReadDir reads the named directory
24// and returns a list of directory entries sorted by filename.
25//
26// If fs implements [ReadDirFS], ReadDir calls fs.ReadDir.
27// Otherwise ReadDir calls fs.Open and uses ReadDir and Close
28// on the returned file.
29func ReadDir(fsys FS, name string) ([]DirEntry, error) {
30	if fsys, ok := fsys.(ReadDirFS); ok {
31		return fsys.ReadDir(name)
32	}
33
34	file, err := fsys.Open(name)
35	if err != nil {
36		return nil, err
37	}
38	defer file.Close()
39
40	dir, ok := file.(ReadDirFile)
41	if !ok {
42		return nil, &PathError{Op: "readdir", Path: name, Err: errors.New("not implemented")}
43	}
44
45	list, err := dir.ReadDir(-1)
46	slices.SortFunc(list, func(a, b DirEntry) int {
47		return bytealg.CompareString(a.Name(), b.Name())
48	})
49	return list, err
50}
51
52// dirInfo is a DirEntry based on a FileInfo.
53type dirInfo struct {
54	fileInfo FileInfo
55}
56
57func (di dirInfo) IsDir() bool {
58	return di.fileInfo.IsDir()
59}
60
61func (di dirInfo) Type() FileMode {
62	return di.fileInfo.Mode().Type()
63}
64
65func (di dirInfo) Info() (FileInfo, error) {
66	return di.fileInfo, nil
67}
68
69func (di dirInfo) Name() string {
70	return di.fileInfo.Name()
71}
72
73func (di dirInfo) String() string {
74	return FormatDirEntry(di)
75}
76
77// FileInfoToDirEntry returns a [DirEntry] that returns information from info.
78// If info is nil, FileInfoToDirEntry returns nil.
79func FileInfoToDirEntry(info FileInfo) DirEntry {
80	if info == nil {
81		return nil
82	}
83	return dirInfo{fileInfo: info}
84}
85