v2 / vlib / io / fs / fs.v
163 lines · 141 sloc · 3.38 KB · 24cc5e620241ab06fb63816ec6c3525846d9c687
Raw
1module fs
2
3import io
4import os
5
6// FileInfo describes a file or directory entry.
7pub interface FileInfo {
8 name() string
9 size() u64
10 mode() os.FileMode
11 mod_time() i64
12 is_dir() bool
13}
14
15// DirEntry describes a single directory entry.
16pub interface DirEntry {
17 name() string
18 is_dir() bool
19 typ() os.FileType
20 info() !FileInfo
21}
22
23// File is the minimum interface required to read from a filesystem entry.
24pub interface File {
25 io.Reader
26 stat() !FileInfo
27mut:
28 close()
29}
30
31// FS opens slash-separated paths that pass `valid_path`.
32pub interface FS {
33 open(name string) !File
34}
35
36// ReadDirFile can read directory entries directly from an opened file.
37pub interface ReadDirFile {
38 File
39mut:
40 read_dir(n int) ![]DirEntry
41}
42
43// ReadDirFS can read a directory without first opening it as a `File`.
44pub interface ReadDirFS {
45 read_dir(name string) ![]DirEntry
46}
47
48// ReadFileFS can read a whole file without first opening it as a `File`.
49pub interface ReadFileFS {
50 read_file(name string) ![]u8
51}
52
53// StatFS can stat a path without first opening it as a `File`.
54pub interface StatFS {
55 stat(name string) !FileInfo
56}
57
58// GlobFS can expand glob patterns within a filesystem.
59pub interface GlobFS {
60 glob(pattern string) ![]string
61}
62
63// SubFS can return a filesystem rooted at a subdirectory.
64pub interface SubFS {
65 sub(dir string) !FS
66}
67
68struct FileInfoDirEntry {
69 source FileInfo
70}
71
72fn (entry FileInfoDirEntry) name() string {
73 return entry.source.name()
74}
75
76fn (entry FileInfoDirEntry) is_dir() bool {
77 return entry.source.is_dir()
78}
79
80fn (entry FileInfoDirEntry) typ() os.FileType {
81 return entry.source.mode().typ
82}
83
84fn (entry FileInfoDirEntry) info() !FileInfo {
85 return entry.source
86}
87
88fn sort_dir_entries(mut entries []DirEntry) {
89 entries.sort(a.name() < b.name())
90}
91
92// valid_path reports whether `name` is a valid slash-separated path for `FS.open`.
93pub fn valid_path(name string) bool {
94 if name == '.' {
95 return true
96 }
97 if name.len == 0 {
98 return false
99 }
100 for elem in name.split('/') {
101 if elem.len == 0 || elem == '.' || elem == '..' {
102 return false
103 }
104 }
105 return true
106}
107
108// file_info_to_dir_entry wraps a `FileInfo` in a `DirEntry`.
109pub fn file_info_to_dir_entry(info FileInfo) DirEntry {
110 return FileInfoDirEntry{
111 source: info
112 }
113}
114
115// read_file reads the named file from `filesystem`.
116pub fn read_file(filesystem FS, name string) ![]u8 {
117 if filesystem is ReadFileFS {
118 reader := filesystem as ReadFileFS
119 return reader.read_file(name)
120 }
121 mut file := filesystem.open(name)!
122 defer {
123 file.close()
124 }
125 reader := file as io.Reader
126 return io.read_all(
127 reader: reader
128 read_to_end_of_stream: true
129 )
130}
131
132// read_dir reads and sorts the named directory from `filesystem`.
133pub fn read_dir(filesystem FS, name string) ![]DirEntry {
134 if filesystem is ReadDirFS {
135 reader := filesystem as ReadDirFS
136 mut entries := reader.read_dir(name)!
137 sort_dir_entries(mut entries)
138 return entries
139 }
140 mut file := filesystem.open(name)!
141 defer {
142 file.close()
143 }
144 if mut file is ReadDirFile {
145 mut entries := file.read_dir(-1)!
146 sort_dir_entries(mut entries)
147 return entries
148 }
149 return error('fs: `${name}` does not support read_dir')
150}
151
152// stat returns file information for the named path from `filesystem`.
153pub fn stat(filesystem FS, name string) !FileInfo {
154 if filesystem is StatFS {
155 stats := filesystem as StatFS
156 return stats.stat(name)
157 }
158 mut file := filesystem.open(name)!
159 defer {
160 file.close()
161 }
162 return file.stat()
163}
164