From 679ffed8072925e303413d8ebbecd14ca2e420e8 Mon Sep 17 00:00:00 2001 From: luaneko Date: Thu, 26 Jun 2025 22:46:27 +1000 Subject: [PATCH] Implement lsdir and file info functions --- crates/lb/src/fs.rs | 236 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 230 insertions(+), 6 deletions(-) diff --git a/crates/lb/src/fs.rs b/crates/lb/src/fs.rs index be01602..bbf298f 100644 --- a/crates/lb/src/fs.rs +++ b/crates/lb/src/fs.rs @@ -1,14 +1,21 @@ -//! Filesystem library +//! # File system library //! //! The `lb:fs` library provides synchronous and asynchronous utilities for interacting with the //! file system. //! +//! ## Asynchronous by default +//! +//! File system operations are blocking by nature; to provide asynchronicity, luby performs blocking +//! operations in a background thread pool by default. Synchronous complements to all asynchronous +//! functions are always provided. +//! //! ## Exports //! //! See [`lb_fslib`] for items exported by this library. +use derive_more::From; use luaffi::{cdef, metatype}; +use std::time::SystemTime; use thiserror::Error; -use tokio::fs; /// Errors that can be thrown by this library. /// @@ -41,7 +48,7 @@ impl lb_fslib { } pub async extern "Lua-C" fn read(&self, path: &str) -> Result> { - Ok(fs::read(path).await?) + Ok(tokio::fs::read(path).await?) } pub extern "Lua-C" fn read_sync(&self, path: &str) -> Result> { @@ -49,14 +56,231 @@ impl lb_fslib { } pub async extern "Lua-C" fn write(&self, path: &str, contents: &[u8]) -> Result<()> { - Ok(fs::write(path, contents).await?) + Ok(tokio::fs::write(path, contents).await?) } pub extern "Lua-C" fn write_sync(&self, path: &str, contents: &[u8]) -> Result<()> { Ok(std::fs::write(path, contents)?) } - pub extern "Lua-C" fn readdir_sync(&self, _path: &str) -> Result<()> { - Ok(()) + pub async extern "Lua-C" fn lsdir(&self, path: &str) -> Result { + Ok(tokio::fs::read_dir(path).await?.into()) + } + + pub extern "Lua-C" fn lsdir_sync(&self, path: &str) -> Result { + Ok(std::fs::read_dir(path)?.into()) + } +} + +/// Iterator over the entries in a directory. +#[derive(Debug, From)] +#[cdef] +pub struct lb_lsdir(#[opaque] tokio::fs::ReadDir); + +#[metatype] +impl lb_lsdir { + #[call] + pub async extern "Lua-C" fn next(&mut self) -> Result> { + Ok(self.0.next_entry().await?.map(Into::into)) + } +} + +/// Synchronous version of [`lb_lsdir`]. +#[derive(Debug, From)] +#[cdef] +pub struct lb_lsdir_sync(#[opaque] std::fs::ReadDir); + +#[metatype] +impl lb_lsdir_sync { + #[call] + pub extern "Lua-C" fn next(&mut self) -> Result> { + Ok(self.0.next().transpose()?.map(Into::into)) + } +} + +#[derive(Debug, From)] +#[cdef] +pub struct lb_dirent(#[opaque] tokio::fs::DirEntry); + +#[metatype] +impl lb_dirent { + pub extern "Lua-C" fn path(&self) -> String { + self.0.path().to_string_lossy().into() + } + + pub extern "Lua-C" fn name(&self) -> String { + self.0.file_name().to_string_lossy().into() + } + + pub async extern "Lua-C" fn r#type(&self) -> Result { + Ok(self.0.file_type().await?.into()) + } + + pub async extern "Lua-C" fn metadata(&self) -> Result { + Ok(self.0.metadata().await?.into()) + } + + #[cfg(unix)] + pub extern "Lua-C" fn ino(&self) -> u64 { + self.0.ino() + } + + #[tostring] + pub extern "Lua" fn tostring(&self) -> String { + self.path() + } +} + +#[derive(Debug, From)] +#[cdef] +pub struct lb_dirent_sync(#[opaque] std::fs::DirEntry); + +#[metatype] +impl lb_dirent_sync { + pub extern "Lua-C" fn path(&self) -> String { + self.0.path().to_string_lossy().into() + } + + pub extern "Lua-C" fn name(&self) -> String { + self.0.file_name().to_string_lossy().into() + } + + pub extern "Lua-C" fn r#type(&self) -> Result { + Ok(self.0.file_type()?.into()) + } + + pub extern "Lua-C" fn metadata(&self) -> Result { + Ok(self.0.metadata()?.into()) + } + + #[cfg(unix)] + pub extern "Lua-C" fn ino(&self) -> u64 { + use std::os::unix::fs::DirEntryExt; + self.0.ino() + } + + #[tostring] + pub extern "Lua" fn tostring(&self) -> String { + self.path() + } +} + +#[derive(Debug, From)] +#[cdef] +pub struct lb_file_type(#[opaque] std::fs::FileType); + +#[metatype] +impl lb_file_type { + pub extern "Lua-C" fn is_dir(&self) -> bool { + self.0.is_dir() + } + + pub extern "Lua-C" fn is_file(&self) -> bool { + self.0.is_file() + } + + pub extern "Lua-C" fn is_symlink(&self) -> bool { + self.0.is_file() + } + + #[tostring] + pub extern "Lua-C" fn tostring(&self) -> String { + if self.0.is_file() { + "file" + } else if self.0.is_dir() { + "dir" + } else if self.0.is_symlink() { + "symlink" + } else { + "other" + } + .into() + } +} + +#[derive(Debug, From)] +#[cdef] +pub struct lb_file_meta(#[opaque] std::fs::Metadata); + +#[metatype] +impl lb_file_meta { + pub extern "Lua-C" fn is_dir(&self) -> bool { + self.0.is_dir() + } + + pub extern "Lua-C" fn is_file(&self) -> bool { + self.0.is_file() + } + + pub extern "Lua-C" fn is_symlink(&self) -> bool { + self.0.is_file() + } + + pub extern "Lua-C" fn r#type(&self) -> lb_file_type { + self.0.file_type().into() + } + + pub extern "Lua-C" fn size(&self) -> u64 { + self.0.len() + } + + pub extern "Lua-C" fn perms(&self) -> lb_file_perms { + self.0.permissions().into() + } + + pub extern "Lua-C" fn created(&self) -> Result { + Ok(self + .0 + .created()? + .duration_since(SystemTime::UNIX_EPOCH) + .map(|dur| dur.as_secs_f64()) + .unwrap_or(0.)) + } + + pub extern "Lua-C" fn modified(&self) -> Result { + Ok(self + .0 + .modified()? + .duration_since(SystemTime::UNIX_EPOCH) + .map(|dur| dur.as_secs_f64()) + .unwrap_or(0.)) + } + + pub extern "Lua-C" fn accessed(&self) -> Result { + Ok(self + .0 + .accessed()? + .duration_since(SystemTime::UNIX_EPOCH) + .map(|dur| dur.as_secs_f64()) + .unwrap_or(0.)) + } + + #[tostring] + pub extern "Lua-C" fn tostring(&self) -> String { + let ty = self.0.file_type(); + if ty.is_file() { + format!("file {}", self.0.len()) + } else if ty.is_dir() { + "dir".into() + } else if ty.is_symlink() { + "symlink".into() + } else { + "other".into() + } + } +} + +#[derive(Debug, From)] +#[cdef] +pub struct lb_file_perms(#[opaque] std::fs::Permissions); + +#[metatype] +impl lb_file_perms { + pub extern "Lua-C" fn readonly(&self) -> bool { + self.0.readonly() + } + + pub extern "Lua-C" fn set_readonly(&mut self, readonly: bool) { + self.0.set_readonly(readonly); } }