Source file
src/io/fs/sub.go
Documentation: io/fs
1
2
3
4
5 package fs
6
7 import (
8 "errors"
9 "path"
10 )
11
12
13 type SubFS interface {
14 FS
15
16
17 Sub(dir string) (FS, error)
18 }
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 func Sub(fsys FS, dir string) (FS, error) {
35 if !ValidPath(dir) {
36 return nil, &PathError{Op: "sub", Path: dir, Err: ErrInvalid}
37 }
38 if dir == "." {
39 return fsys, nil
40 }
41 if fsys, ok := fsys.(SubFS); ok {
42 return fsys.Sub(dir)
43 }
44 return &subFS{fsys, dir}, nil
45 }
46
47 type subFS struct {
48 fsys FS
49 dir string
50 }
51
52
53 func (f *subFS) fullName(op string, name string) (string, error) {
54 if !ValidPath(name) {
55 return "", &PathError{Op: op, Path: name, Err: ErrInvalid}
56 }
57 return path.Join(f.dir, name), nil
58 }
59
60
61 func (f *subFS) shorten(name string) (rel string, ok bool) {
62 if name == f.dir {
63 return ".", true
64 }
65 if len(name) >= len(f.dir)+2 && name[len(f.dir)] == '/' && name[:len(f.dir)] == f.dir {
66 return name[len(f.dir)+1:], true
67 }
68 return "", false
69 }
70
71
72 func (f *subFS) fixErr(err error) error {
73 if e, ok := err.(*PathError); ok {
74 if short, ok := f.shorten(e.Path); ok {
75 e.Path = short
76 }
77 }
78 return err
79 }
80
81 func (f *subFS) Open(name string) (File, error) {
82 full, err := f.fullName("open", name)
83 if err != nil {
84 return nil, err
85 }
86 file, err := f.fsys.Open(full)
87 return file, f.fixErr(err)
88 }
89
90 func (f *subFS) ReadDir(name string) ([]DirEntry, error) {
91 full, err := f.fullName("read", name)
92 if err != nil {
93 return nil, err
94 }
95 dir, err := ReadDir(f.fsys, full)
96 return dir, f.fixErr(err)
97 }
98
99 func (f *subFS) ReadFile(name string) ([]byte, error) {
100 full, err := f.fullName("read", name)
101 if err != nil {
102 return nil, err
103 }
104 data, err := ReadFile(f.fsys, full)
105 return data, f.fixErr(err)
106 }
107
108 func (f *subFS) Glob(pattern string) ([]string, error) {
109
110 if _, err := path.Match(pattern, ""); err != nil {
111 return nil, err
112 }
113 if pattern == "." {
114 return []string{"."}, nil
115 }
116
117 full := f.dir + "/" + pattern
118 list, err := Glob(f.fsys, full)
119 for i, name := range list {
120 name, ok := f.shorten(name)
121 if !ok {
122 return nil, errors.New("invalid result from inner fsys Glob: " + name + " not in " + f.dir)
123 }
124 list[i] = name
125 }
126 return list, f.fixErr(err)
127 }
128
129 func (f *subFS) Sub(dir string) (FS, error) {
130 if dir == "." {
131 return f, nil
132 }
133 full, err := f.fullName("sub", dir)
134 if err != nil {
135 return nil, err
136 }
137 return &subFS{f.fsys, full}, nil
138 }
139
View as plain text