mirror of
https://github.com/vlang/v.git
synced 2025-08-04 02:07:28 -04:00
os: add os.stat() and helpers (#20739)
This commit is contained in:
parent
ee55e9b461
commit
205f2ddd30
15
vlib/os/os.v
15
vlib/os/os.v
@ -977,3 +977,18 @@ pub fn config_dir() !string {
|
||||
}
|
||||
return error('Cannot find config directory')
|
||||
}
|
||||
|
||||
pub struct Stat {
|
||||
pub:
|
||||
dev u64
|
||||
inode u64
|
||||
mode u32
|
||||
nlink u64
|
||||
uid u32
|
||||
gid u32
|
||||
rdev u64
|
||||
size u64
|
||||
atime i64
|
||||
mtime i64
|
||||
ctime i64
|
||||
}
|
||||
|
81
vlib/os/os_stat_default.c.v
Normal file
81
vlib/os/os_stat_default.c.v
Normal file
@ -0,0 +1,81 @@
|
||||
module os
|
||||
|
||||
// stat returns a platform-agnostic Stat struct comparable to what is
|
||||
// available in other programming languages and fails with the POSIX
|
||||
// error if the stat call fails. If a link is stat'd, the stat info
|
||||
// for the link is provided.
|
||||
pub fn stat(path string) !Stat {
|
||||
mut s := C.stat{}
|
||||
unsafe {
|
||||
res := C.lstat(&char(path.str), &s)
|
||||
if res != 0 {
|
||||
return error_posix()
|
||||
}
|
||||
return Stat{
|
||||
dev: s.st_dev
|
||||
inode: s.st_ino
|
||||
nlink: s.st_nlink
|
||||
mode: s.st_mode
|
||||
uid: s.st_uid
|
||||
gid: s.st_gid
|
||||
rdev: s.st_rdev
|
||||
size: s.st_size
|
||||
atime: s.st_atime
|
||||
mtime: s.st_mtime
|
||||
ctime: s.st_ctime
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get_filetype returns the FileType from the Stat struct
|
||||
pub fn (st Stat) get_filetype() FileType {
|
||||
match st.mode & u32(C.S_IFMT) {
|
||||
u32(C.S_IFREG) {
|
||||
return .regular
|
||||
}
|
||||
u32(C.S_IFDIR) {
|
||||
return .directory
|
||||
}
|
||||
u32(C.S_IFCHR) {
|
||||
return .character_device
|
||||
}
|
||||
u32(C.S_IFBLK) {
|
||||
return .block_device
|
||||
}
|
||||
u32(C.S_IFIFO) {
|
||||
return .fifo
|
||||
}
|
||||
u32(C.S_IFLNK) {
|
||||
return .symbolic_link
|
||||
}
|
||||
u32(C.S_IFSOCK) {
|
||||
return .socket
|
||||
}
|
||||
else {
|
||||
return .unknown
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get_mode returns the file type and permissions (readable, writable, executable)
|
||||
// in owner/group/others format
|
||||
pub fn (st Stat) get_mode() FileMode {
|
||||
return FileMode{
|
||||
typ: st.get_filetype()
|
||||
owner: FilePermission{
|
||||
read: (st.mode & u32(C.S_IRUSR)) != 0
|
||||
write: (st.mode & u32(C.S_IWUSR)) != 0
|
||||
execute: (st.mode & u32(C.S_IXUSR)) != 0
|
||||
}
|
||||
group: FilePermission{
|
||||
read: (st.mode & u32(C.S_IRGRP)) != 0
|
||||
write: (st.mode & u32(C.S_IWGRP)) != 0
|
||||
execute: (st.mode & u32(C.S_IXGRP)) != 0
|
||||
}
|
||||
others: FilePermission{
|
||||
read: (st.mode & u32(C.S_IROTH)) != 0
|
||||
write: (st.mode & u32(C.S_IWOTH)) != 0
|
||||
execute: (st.mode & u32(C.S_IXOTH)) != 0
|
||||
}
|
||||
}
|
||||
}
|
57
vlib/os/os_stat_test.v
Normal file
57
vlib/os/os_stat_test.v
Normal file
@ -0,0 +1,57 @@
|
||||
import os
|
||||
import rand
|
||||
import time
|
||||
|
||||
fn test_stat() {
|
||||
start_time := time.utc()
|
||||
|
||||
temp_dir := os.join_path(os.temp_dir(), rand.ulid())
|
||||
os.mkdir(temp_dir)!
|
||||
defer {
|
||||
os.rmdir(temp_dir) or {}
|
||||
}
|
||||
|
||||
test_file := os.join_path(temp_dir, rand.ulid())
|
||||
test_content := rand.ulid()
|
||||
os.write_file(test_file, test_content)!
|
||||
defer {
|
||||
os.rm(test_file) or {}
|
||||
}
|
||||
|
||||
end_time := time.utc()
|
||||
|
||||
mut fstat := os.stat(test_file)!
|
||||
assert fstat.get_filetype() == .regular
|
||||
assert fstat.size == u64(test_content.len)
|
||||
assert fstat.ctime >= start_time.unix
|
||||
assert fstat.ctime <= end_time.unix
|
||||
assert fstat.mtime >= start_time.unix
|
||||
assert fstat.mtime <= end_time.unix
|
||||
|
||||
$if !windows {
|
||||
os.chmod(test_file, 0o600)!
|
||||
fstat = os.stat(test_file)!
|
||||
|
||||
mut fmode := fstat.get_mode()
|
||||
assert fmode.typ == .regular
|
||||
assert fmode.owner.read && fmode.owner.write && !fmode.owner.execute
|
||||
assert !fmode.group.read && !fmode.group.write && !fmode.group.execute
|
||||
assert !fmode.others.read && !fmode.others.write && !fmode.others.execute
|
||||
|
||||
os.chmod(test_file, 0o421)!
|
||||
fstat = os.stat(test_file)!
|
||||
fmode = fstat.get_mode()
|
||||
assert fmode.owner.read && !fmode.owner.write && !fmode.owner.execute
|
||||
assert !fmode.group.read && fmode.group.write && !fmode.group.execute
|
||||
assert !fmode.others.read && !fmode.others.write && fmode.others.execute
|
||||
|
||||
os.chmod(test_file, 0o600)!
|
||||
}
|
||||
|
||||
// When using the Time struct, allow for up to 1 second difference due to nanoseconds
|
||||
// which are not captured in the timestamp
|
||||
dstat := os.stat(temp_dir)!
|
||||
assert dstat.get_filetype() == .directory
|
||||
assert fstat.dev == dstat.dev, 'File and directory should be created on same device'
|
||||
assert fstat.rdev == dstat.rdev, 'File and directory should have same device ID'
|
||||
}
|
63
vlib/os/os_stat_windows.c.v
Normal file
63
vlib/os/os_stat_windows.c.v
Normal file
@ -0,0 +1,63 @@
|
||||
module os
|
||||
|
||||
// stat returns a platform-agnostic Stat struct comparable to what is
|
||||
// available in other programming languages and fails with the POSIX
|
||||
// error if the stat call fails. If a link is stat'd, the stat info
|
||||
// for the link is provided.
|
||||
pub fn stat(path string) !Stat {
|
||||
mut s := C.__stat64{}
|
||||
unsafe {
|
||||
res := C._wstat64(path.to_wide(), &s)
|
||||
if res != 0 {
|
||||
return error_posix()
|
||||
}
|
||||
return Stat{
|
||||
dev: s.st_dev
|
||||
inode: s.st_ino
|
||||
nlink: s.st_nlink
|
||||
mode: s.st_mode
|
||||
uid: s.st_uid
|
||||
gid: s.st_gid
|
||||
rdev: s.st_rdev
|
||||
size: s.st_size
|
||||
atime: s.st_atime
|
||||
mtime: s.st_mtime
|
||||
ctime: s.st_ctime
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get_filetype returns the FileType from the Stat struct
|
||||
pub fn (st Stat) get_filetype() FileType {
|
||||
match st.mode & u32(C.S_IFMT) {
|
||||
u32(C.S_IFDIR) {
|
||||
return .directory
|
||||
}
|
||||
else {
|
||||
return .regular
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get_mode returns the file type and permissions (readable, writable, executable)
|
||||
// in owner/group/others format, however, they will all be the same for Windows
|
||||
pub fn (st Stat) get_mode() FileMode {
|
||||
return FileMode{
|
||||
typ: st.get_filetype()
|
||||
owner: FilePermission{
|
||||
read: (st.mode & u32(C.S_IREAD)) != 0
|
||||
write: (st.mode & u32(C.S_IWRITE)) != 0
|
||||
execute: (st.mode & u32(C.S_IEXEC)) != 0
|
||||
}
|
||||
group: FilePermission{
|
||||
read: (st.mode & u32(C.S_IREAD)) != 0
|
||||
write: (st.mode & u32(C.S_IWRITE)) != 0
|
||||
execute: (st.mode & u32(C.S_IEXEC)) != 0
|
||||
}
|
||||
others: FilePermission{
|
||||
read: (st.mode & u32(C.S_IREAD)) != 0
|
||||
write: (st.mode & u32(C.S_IWRITE)) != 0
|
||||
execute: (st.mode & u32(C.S_IEXEC)) != 0
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +1,19 @@
|
||||
module os
|
||||
|
||||
// Minimal stat struct as specified in
|
||||
// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_stat.h.html
|
||||
pub struct C.stat {
|
||||
st_size u64
|
||||
st_dev u64
|
||||
st_ino u64
|
||||
st_mode u32
|
||||
st_nlink u64
|
||||
st_uid u32
|
||||
st_gid u32
|
||||
st_rdev u64
|
||||
st_size u64
|
||||
st_atime int
|
||||
st_mtime int
|
||||
st_ctime int
|
||||
}
|
||||
|
||||
pub struct C.__stat64 {
|
||||
|
15
vlib/os/os_structs_stat_windows.v
Normal file
15
vlib/os/os_structs_stat_windows.v
Normal file
@ -0,0 +1,15 @@
|
||||
module os
|
||||
|
||||
pub struct C.__stat64 {
|
||||
st_dev u32 // 4
|
||||
st_ino u16 // 2
|
||||
st_mode u16 // 2
|
||||
st_nlink u16 // 2
|
||||
st_uid u16 // 2
|
||||
st_gid u16 // 2
|
||||
st_rdev u32 // 4
|
||||
st_size u64 // 8
|
||||
st_atime i64 // 8
|
||||
st_mtime i64 // 8
|
||||
st_ctime i64 // 8
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user