164 lines
3.4 KiB
V
164 lines
3.4 KiB
V
module fs
|
|
|
|
import io
|
|
import os
|
|
|
|
// FileInfo describes a file or directory entry.
|
|
pub interface FileInfo {
|
|
name() string
|
|
size() u64
|
|
mode() os.FileMode
|
|
mod_time() i64
|
|
is_dir() bool
|
|
}
|
|
|
|
// DirEntry describes a single directory entry.
|
|
pub interface DirEntry {
|
|
name() string
|
|
is_dir() bool
|
|
typ() os.FileType
|
|
info() !FileInfo
|
|
}
|
|
|
|
// File is the minimum interface required to read from a filesystem entry.
|
|
pub interface File {
|
|
io.Reader
|
|
stat() !FileInfo
|
|
mut:
|
|
close()
|
|
}
|
|
|
|
// FS opens slash-separated paths that pass `valid_path`.
|
|
pub interface FS {
|
|
open(name string) !File
|
|
}
|
|
|
|
// ReadDirFile can read directory entries directly from an opened file.
|
|
pub interface ReadDirFile {
|
|
File
|
|
mut:
|
|
read_dir(n int) ![]DirEntry
|
|
}
|
|
|
|
// ReadDirFS can read a directory without first opening it as a `File`.
|
|
pub interface ReadDirFS {
|
|
read_dir(name string) ![]DirEntry
|
|
}
|
|
|
|
// ReadFileFS can read a whole file without first opening it as a `File`.
|
|
pub interface ReadFileFS {
|
|
read_file(name string) ![]u8
|
|
}
|
|
|
|
// StatFS can stat a path without first opening it as a `File`.
|
|
pub interface StatFS {
|
|
stat(name string) !FileInfo
|
|
}
|
|
|
|
// GlobFS can expand glob patterns within a filesystem.
|
|
pub interface GlobFS {
|
|
glob(pattern string) ![]string
|
|
}
|
|
|
|
// SubFS can return a filesystem rooted at a subdirectory.
|
|
pub interface SubFS {
|
|
sub(dir string) !FS
|
|
}
|
|
|
|
struct FileInfoDirEntry {
|
|
source FileInfo
|
|
}
|
|
|
|
fn (entry FileInfoDirEntry) name() string {
|
|
return entry.source.name()
|
|
}
|
|
|
|
fn (entry FileInfoDirEntry) is_dir() bool {
|
|
return entry.source.is_dir()
|
|
}
|
|
|
|
fn (entry FileInfoDirEntry) typ() os.FileType {
|
|
return entry.source.mode().typ
|
|
}
|
|
|
|
fn (entry FileInfoDirEntry) info() !FileInfo {
|
|
return entry.source
|
|
}
|
|
|
|
fn sort_dir_entries(mut entries []DirEntry) {
|
|
entries.sort(a.name() < b.name())
|
|
}
|
|
|
|
// valid_path reports whether `name` is a valid slash-separated path for `FS.open`.
|
|
pub fn valid_path(name string) bool {
|
|
if name == '.' {
|
|
return true
|
|
}
|
|
if name.len == 0 {
|
|
return false
|
|
}
|
|
for elem in name.split('/') {
|
|
if elem.len == 0 || elem == '.' || elem == '..' {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// file_info_to_dir_entry wraps a `FileInfo` in a `DirEntry`.
|
|
pub fn file_info_to_dir_entry(info FileInfo) DirEntry {
|
|
return FileInfoDirEntry{
|
|
source: info
|
|
}
|
|
}
|
|
|
|
// read_file reads the named file from `filesystem`.
|
|
pub fn read_file(filesystem FS, name string) ![]u8 {
|
|
if filesystem is ReadFileFS {
|
|
reader := filesystem as ReadFileFS
|
|
return reader.read_file(name)
|
|
}
|
|
mut file := filesystem.open(name)!
|
|
defer {
|
|
file.close()
|
|
}
|
|
reader := file as io.Reader
|
|
return io.read_all(
|
|
reader: reader
|
|
read_to_end_of_stream: true
|
|
)
|
|
}
|
|
|
|
// read_dir reads and sorts the named directory from `filesystem`.
|
|
pub fn read_dir(filesystem FS, name string) ![]DirEntry {
|
|
if filesystem is ReadDirFS {
|
|
reader := filesystem as ReadDirFS
|
|
mut entries := reader.read_dir(name)!
|
|
sort_dir_entries(mut entries)
|
|
return entries
|
|
}
|
|
mut file := filesystem.open(name)!
|
|
defer {
|
|
file.close()
|
|
}
|
|
if mut file is ReadDirFile {
|
|
mut entries := file.read_dir(-1)!
|
|
sort_dir_entries(mut entries)
|
|
return entries
|
|
}
|
|
return error('fs: `${name}` does not support read_dir')
|
|
}
|
|
|
|
// stat returns file information for the named path from `filesystem`.
|
|
pub fn stat(filesystem FS, name string) !FileInfo {
|
|
if filesystem is StatFS {
|
|
stats := filesystem as StatFS
|
|
return stats.stat(name)
|
|
}
|
|
mut file := filesystem.open(name)!
|
|
defer {
|
|
file.close()
|
|
}
|
|
return file.stat()
|
|
}
|