Restructure fslib

This commit is contained in:
lumi 2025-06-30 19:23:44 +10:00
parent fc4d27abf1
commit e03ef8a495
Signed by: luaneko
GPG Key ID: 406809B8763FF07A
8 changed files with 686 additions and 646 deletions

View File

@ -1,645 +0,0 @@
//! Filesystem library.
//!
//! The `lb:fs` library provides synchronous and asynchronous utilities for interacting with the
//! filesystem.
//!
//! ## Asynchronous by default
//!
//! Filesystem 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 luaffi::{cdef, metatype};
use std::{
cell::{BorrowError, BorrowMutError, RefCell},
path::PathBuf,
time::SystemTime,
};
use thiserror::Error;
/// Errors that can be thrown by this library.
///
/// Functions which return this error will **throw**. The error message can be caught by using
/// [`pcall(f, ...)`](https://www.lua.org/manual/5.1/manual.html#pdf-pcall).
#[derive(Debug, Error)]
pub enum Error {
/// Attempt to access an object while it is being modified.
#[error("cannot access object while it is being modified")]
Borrow(#[from] BorrowError),
/// Attempt to modify an object while it is in use.
#[error("cannot modify object while it is in use")]
BorrowMut(#[from] BorrowMutError),
/// I/O error.
#[error("{0}")]
Io(#[from] std::io::Error),
/// Walk directory error.
#[error("{0}")]
Walk(#[from] walkdir::Error),
/// Glob pattern error.
#[error("{0}")]
Glob(#[from] globset::Error),
}
type Result<T> = std::result::Result<T, Error>;
/// Items exported by the `lb:fs` library.
///
/// This library can be acquired by calling
/// [`require("lb:fs")`](https://www.lua.org/manual/5.1/manual.html#pdf-require).
///
/// ```lua
/// local fs = require("lb:fs");
/// ```
#[cdef(module = "lb:fs")]
pub struct lb_fslib;
#[metatype]
impl lb_fslib {
#[new]
extern "Lua-C" fn new() -> Self {
Self
}
/// Reads the entire contents of a file.
///
/// # Errors
///
/// This function may throw if the file does not exist or could not be read.
pub async extern "Lua-C" fn read(path: &str) -> Result<Vec<u8>> {
Ok(tokio::fs::read(path).await?)
}
/// Reads the entire contents of a file synchronously.
///
/// This is a synchronous complement to [`read`](Self::read).
///
/// # Errors
///
/// This function may throw if the file does not exist or could not be read.
pub extern "Lua-C" fn read_sync(path: &str) -> Result<Vec<u8>> {
Ok(std::fs::read(path)?)
}
/// Writes the given contents to a file, replacing its contents if it exists.
///
/// # Errors
///
/// This function may throw if the file could not be written.
pub async extern "Lua-C" fn write(path: &str, contents: &[u8]) -> Result<()> {
Ok(tokio::fs::write(path, contents).await?)
}
/// Writes the given contents to a file synchronously, replacing its contents if it exists.
///
/// This is a synchronous complement to [`write`](Self::write).
///
/// # Errors
///
/// This function may throw if the file could not be written.
pub extern "Lua-C" fn write_sync(path: &str, contents: &[u8]) -> Result<()> {
Ok(std::fs::write(path, contents)?)
}
/// Reads the entries in a directory.
///
/// # Errors
///
/// This function may throw if the directory could not be read.
pub async extern "Lua-C" fn read_dir(path: &str) -> Result<lb_read_dir> {
Ok(lb_read_dir::new(tokio::fs::read_dir(path).await?))
}
/// Reads the entries in a directory synchronously.
///
/// This is a synchronous complement to [`read_dir`](Self::read_dir).
///
/// # Errors
///
/// This function may throw if the directory could not be read.
pub extern "Lua-C" fn read_dir_sync(path: &str) -> Result<lb_read_dir_sync> {
Ok(lb_read_dir_sync::new(std::fs::read_dir(path)?))
}
/// Recursively walks a directory, yielding all entries within it.
pub extern "Lua-C" fn walk_dir(path: &str) -> lb_walk_dir {
lb_walk_dir::new(walkdir::WalkDir::new(path).into_iter())
}
/// Returns an iterator over all files matching a glob pattern in the current directory.
///
/// # Errors
///
/// This function may throw if the pattern is invalid.
pub extern "Lua" fn glob(pattern: &str) -> Result<lb_glob_dir> {
Self::glob_dir(".", pattern)
}
/// Returns an iterator over all files matching a glob pattern in the given directory.
///
/// # Errors
///
/// This function may throw if the pattern is invalid.
pub extern "Lua-C" fn glob_dir(path: &str, pattern: &str) -> Result<lb_glob_dir> {
let prefix = PathBuf::from(path);
let iter = walkdir::WalkDir::new(path).min_depth(1).into_iter();
let matcher = globset::GlobSet::builder()
.add(globset::Glob::new(pattern)?)
.build()?;
Ok(lb_glob_dir::new(iter, matcher, prefix))
}
/// Creates a new temporary directory.
///
/// # Errors
///
/// This function may throw if the temporary directory could not be created.
pub extern "Lua-C" fn temp_dir() -> Result<lb_temp_dir> {
Ok(lb_temp_dir::new(tempfile::tempdir()?))
}
/// Creates a new temporary directory inside the specified path.
///
/// # Errors
///
/// This function may throw if the temporary directory could not be created.
pub extern "Lua-C" fn temp_dir_in(path: &str) -> Result<lb_temp_dir> {
Ok(lb_temp_dir::new(tempfile::tempdir_in(path)?))
}
}
/// Iterator over the entries in a directory.
#[derive(Debug)]
#[cdef]
pub struct lb_read_dir(#[opaque] RefCell<tokio::fs::ReadDir>);
#[metatype]
impl lb_read_dir {
fn new(iter: tokio::fs::ReadDir) -> Self {
Self(RefCell::new(iter))
}
/// Returns the next entry in the directory, or `nil` if there are no more entries.
///
/// # Errors
///
/// This function may throw if the directory could not be read.
#[call]
pub async extern "Lua-C" fn next(&self) -> Result<Option<lb_dir_entry>> {
Ok(self
.0
.try_borrow_mut()?
.next_entry()
.await?
.map(lb_dir_entry::new))
}
}
/// Synchronous version of [`lb_read_dir`].
#[derive(Debug)]
#[cdef]
pub struct lb_read_dir_sync(#[opaque] RefCell<std::fs::ReadDir>);
#[metatype]
impl lb_read_dir_sync {
fn new(iter: std::fs::ReadDir) -> Self {
Self(RefCell::new(iter))
}
/// Returns the next entry in the directory, or `nil` if there are no more entries.
///
/// # Errors
///
/// This function may throw if the directory could not be read.
#[call]
pub extern "Lua-C" fn next(&self) -> Result<Option<lb_dir_entry_sync>> {
Ok(self
.0
.try_borrow_mut()?
.next()
.transpose()?
.map(lb_dir_entry_sync::new))
}
}
/// Entry inside of a directory on the filesystem.
#[derive(Debug)]
#[cdef]
pub struct lb_dir_entry(#[opaque] tokio::fs::DirEntry);
#[metatype]
impl lb_dir_entry {
fn new(entry: tokio::fs::DirEntry) -> Self {
Self(entry)
}
/// Returns the full path of this entry.
pub extern "Lua-C" fn path(&self) -> String {
self.0.path().to_string_lossy().into()
}
/// Returns the file name of this entry.
pub extern "Lua-C" fn name(&self) -> String {
self.0.file_name().to_string_lossy().into()
}
/// Returns the type of this entry.
///
/// # Errors
///
/// This function may throw if the file type could not be determined.
pub async extern "Lua-C" fn r#type(&self) -> Result<lb_file_type> {
Ok(lb_file_type::new(self.0.file_type().await?))
}
/// Returns the metadata for this entry.
///
/// # Errors
///
/// This function may throw if the metadata could not be retrieved.
pub async extern "Lua-C" fn metadata(&self) -> Result<lb_file_meta> {
Ok(lb_file_meta::new(self.0.metadata().await?))
}
/// Returns the inode number for this entry.
#[cfg(unix)]
pub extern "Lua-C" fn ino(&self) -> u64 {
self.0.ino()
}
/// Returns the full path of this entry.
#[tostring]
pub extern "Lua" fn tostring(&self) -> String {
self.path()
}
}
/// Synchronous version of [`lb_dir_entry`].
#[derive(Debug)]
#[cdef]
pub struct lb_dir_entry_sync(#[opaque] std::fs::DirEntry);
#[metatype]
impl lb_dir_entry_sync {
fn new(entry: std::fs::DirEntry) -> Self {
Self(entry)
}
/// Returns the full path of this entry.
pub extern "Lua-C" fn path(&self) -> String {
self.0.path().to_string_lossy().into()
}
/// Returns the file name of this entry.
pub extern "Lua-C" fn name(&self) -> String {
self.0.file_name().to_string_lossy().into()
}
/// Returns the type of this entry.
///
/// # Errors
///
/// This function may throw if the file type could not be determined.
pub extern "Lua-C" fn r#type(&self) -> Result<lb_file_type> {
Ok(lb_file_type::new(self.0.file_type()?))
}
/// Returns the metadata for this entry.
///
/// # Errors
///
/// This function may throw if the metadata could not be retrieved.
pub extern "Lua-C" fn metadata(&self) -> Result<lb_file_meta> {
Ok(lb_file_meta::new(self.0.metadata()?))
}
/// Returns the inode number for this entry.
#[cfg(unix)]
pub extern "Lua-C" fn ino(&self) -> u64 {
use std::os::unix::fs::DirEntryExt;
self.0.ino()
}
/// Returns the full path of this entry.
#[tostring]
pub extern "Lua" fn tostring(&self) -> String {
self.path()
}
}
/// Structure representing the type of a file with accessors for each file type.
#[derive(Debug)]
#[cdef]
pub struct lb_file_type(#[opaque] std::fs::FileType);
#[metatype]
impl lb_file_type {
fn new(ty: std::fs::FileType) -> Self {
Self(ty)
}
/// Returns `true` if this file type is a directory.
pub extern "Lua-C" fn is_dir(&self) -> bool {
self.0.is_dir()
}
/// Returns `true` if this file type is a regular file.
pub extern "Lua-C" fn is_file(&self) -> bool {
self.0.is_file()
}
/// Returns `true` if this file type is a symbolic link.
pub extern "Lua-C" fn is_symlink(&self) -> bool {
self.0.is_file()
}
/// Returns the string `"file"` if this is a regular file, `"dir"` if this is a directory,
/// `"symlink"` if this is a symbolic link, or `"other"` if it is some other type of 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()
}
}
/// Metadata information about a file.
#[derive(Debug)]
#[cdef]
pub struct lb_file_meta(#[opaque] std::fs::Metadata);
#[metatype]
impl lb_file_meta {
fn new(meta: std::fs::Metadata) -> Self {
Self(meta)
}
/// Returns `true` if this file is a directory.
pub extern "Lua-C" fn is_dir(&self) -> bool {
self.0.is_dir()
}
/// Returns `true` if this file is a regular file.
pub extern "Lua-C" fn is_file(&self) -> bool {
self.0.is_file()
}
/// Returns `true` if this file is a symbolic link.
pub extern "Lua-C" fn is_symlink(&self) -> bool {
self.0.is_file()
}
/// Returns the type of this file.
pub extern "Lua-C" fn r#type(&self) -> lb_file_type {
lb_file_type::new(self.0.file_type())
}
/// Returns the size of this file in bytes.
pub extern "Lua-C" fn size(&self) -> u64 {
self.0.len()
}
/// Returns the permissions of this file.
pub extern "Lua-C" fn perms(&self) -> lb_file_perms {
lb_file_perms::new(self.0.permissions())
}
/// Returns the creation time of this file as seconds since the Unix epoch.
///
/// # Errors
///
/// This function may throw if the creation time could not be retrieved.
pub extern "Lua-C" fn created(&self) -> Result<f64> {
Ok(self
.0
.created()?
.duration_since(SystemTime::UNIX_EPOCH)
.map(|dur| dur.as_secs_f64())
.unwrap_or(0.))
}
/// Returns the modification time of this file as seconds since the Unix epoch.
///
/// # Errors
///
/// This function may throw if the modification time could not be retrieved.
pub extern "Lua-C" fn modified(&self) -> Result<f64> {
Ok(self
.0
.modified()?
.duration_since(SystemTime::UNIX_EPOCH)
.map(|dur| dur.as_secs_f64())
.unwrap_or(0.))
}
/// Returns the last access time of this file as seconds since the Unix epoch.
///
/// # Errors
///
/// This function may throw if the access time could not be retrieved.
pub extern "Lua-C" fn accessed(&self) -> Result<f64> {
Ok(self
.0
.accessed()?
.duration_since(SystemTime::UNIX_EPOCH)
.map(|dur| dur.as_secs_f64())
.unwrap_or(0.))
}
/// Returns a string representation of this file's metadata.
#[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()
}
}
}
/// Representation of the various permissions on a file.
#[derive(Debug)]
#[cdef]
pub struct lb_file_perms(#[opaque] RefCell<std::fs::Permissions>);
#[metatype]
impl lb_file_perms {
fn new(perms: std::fs::Permissions) -> Self {
Self(RefCell::new(perms))
}
/// Returns `true` if the readonly flag is set.
pub extern "Lua-C" fn readonly(&self) -> bool {
self.0.borrow().readonly()
}
/// Sets the readonly flag.
pub extern "Lua-C" fn set_readonly(&self, readonly: bool) {
self.0.borrow_mut().set_readonly(readonly);
}
}
/// Iterator for recursively descending into a directory.
#[derive(Debug)]
#[cdef]
pub struct lb_walk_dir(#[opaque] RefCell<walkdir::IntoIter>);
#[metatype]
impl lb_walk_dir {
fn new(iter: walkdir::IntoIter) -> Self {
Self(RefCell::new(iter))
}
/// Returns the next entry in the walk, or `nil` if there are no more entries.
///
/// # Errors
///
/// This function may throw if the directory could not be read.
#[call]
pub extern "Lua-C" fn next(&self) -> Result<Option<lb_walk_dir_entry>> {
Ok(self
.0
.try_borrow_mut()?
.next()
.transpose()?
.map(lb_walk_dir_entry::new))
}
}
/// Entry inside of a directory on the filesystem obtained from [`lb_walk_dir`].
#[derive(Debug)]
#[cdef]
pub struct lb_walk_dir_entry(#[opaque] walkdir::DirEntry);
#[metatype]
impl lb_walk_dir_entry {
fn new(entry: walkdir::DirEntry) -> Self {
Self(entry)
}
/// Returns the full path of this entry.
pub extern "Lua-C" fn path(&self) -> String {
self.0.path().to_string_lossy().into()
}
/// Returns the file name of this entry.
pub extern "Lua-C" fn name(&self) -> String {
self.0.file_name().to_string_lossy().into()
}
/// Returns the type of this entry.
pub extern "Lua-C" fn r#type(&self) -> lb_file_type {
lb_file_type::new(self.0.file_type())
}
/// Returns the metadata for this entry.
///
/// # Errors
///
/// This function may throw if the metadata could not be retrieved.
pub extern "Lua-C" fn metadata(&self) -> Result<lb_file_meta> {
Ok(lb_file_meta::new(self.0.metadata()?))
}
/// Returns `true` if this entry was created from a symbolic link.
pub extern "Lua-C" fn is_symlink(&self) -> bool {
self.0.path_is_symlink()
}
/// Returns the depth of this entry in the walk.
pub extern "Lua-C" fn depth(&self) -> u32 {
self.0.depth() as u32
}
/// Returns the inode number for this entry.
#[cfg(unix)]
pub extern "Lua-C" fn ino(&self) -> u64 {
use walkdir::DirEntryExt;
self.0.ino()
}
/// Returns the full path of this entry.
#[tostring]
pub extern "Lua" fn tostring(&self) -> String {
self.path()
}
}
/// Iterator that yields paths from the filesystem that match a particular pattern.
#[derive(Debug)]
#[cdef]
pub struct lb_glob_dir {
#[opaque]
iter: RefCell<walkdir::IntoIter>,
#[opaque]
matcher: globset::GlobSet,
#[opaque]
prefix: PathBuf,
}
#[metatype]
impl lb_glob_dir {
fn new(iter: walkdir::IntoIter, matcher: globset::GlobSet, prefix: PathBuf) -> Self {
Self {
iter: RefCell::new(iter),
matcher,
prefix,
}
}
/// Returns the next entry matching the glob pattern, or `nil` if there are no more entries.
///
/// # Errors
///
/// This function may throw if the directory could not be read.
#[call]
pub extern "Lua-C" fn next(&self) -> Result<Option<lb_walk_dir_entry>> {
while let Some(res) = self.iter.try_borrow_mut()?.next() {
let entry = res?;
let path = entry.path().strip_prefix(&self.prefix).unwrap();
if self.matcher.is_match(path) {
return Ok(Some(lb_walk_dir_entry::new(entry)));
}
}
Ok(None)
}
}
/// Directory in the filesystem that is automatically deleted when it is garbage-collected.
#[derive(Debug)]
#[cdef]
pub struct lb_temp_dir(#[opaque] tempfile::TempDir);
#[metatype]
impl lb_temp_dir {
fn new(dir: tempfile::TempDir) -> Self {
Self(dir)
}
/// Returns the full path of this temporary directory.
pub extern "Lua-C" fn path(&self) -> String {
self.0.path().to_string_lossy().into()
}
/// Returns the full path of this temporary directory.
#[tostring]
pub extern "Lua" fn tostring(&self) -> String {
self.path()
}
}

83
crates/lb/src/fs/async.rs Normal file
View File

@ -0,0 +1,83 @@
use super::*;
use luaffi::{cdef, metatype};
use std::cell::RefCell;
use tokio::fs::{DirEntry, ReadDir};
/// Iterator over the entries in a directory.
#[derive(Debug)]
#[cdef]
pub struct lb_read_dir(#[opaque] RefCell<ReadDir>);
#[metatype]
impl lb_read_dir {
pub(super) fn new(iter: ReadDir) -> Self {
Self(RefCell::new(iter))
}
/// Returns the next entry in the directory, or `nil` if there are no more entries.
///
/// # Errors
///
/// This function may throw if the directory could not be read.
#[call]
pub async extern "Lua-C" fn next(&self) -> Result<Option<lb_dir_entry>> {
Ok(self
.0
.try_borrow_mut()?
.next_entry()
.await?
.map(lb_dir_entry::new))
}
}
/// Entry inside of a directory on the filesystem.
#[derive(Debug)]
#[cdef]
pub struct lb_dir_entry(#[opaque] DirEntry);
#[metatype]
impl lb_dir_entry {
pub(super) fn new(entry: DirEntry) -> Self {
Self(entry)
}
/// Returns the full path of this entry.
pub extern "Lua-C" fn path(&self) -> String {
self.0.path().to_string_lossy().into()
}
/// Returns the file name of this entry.
pub extern "Lua-C" fn name(&self) -> String {
self.0.file_name().to_string_lossy().into()
}
/// Returns the type of this entry.
///
/// # Errors
///
/// This function may throw if the file type could not be determined.
pub async extern "Lua-C" fn r#type(&self) -> Result<lb_file_type> {
Ok(lb_file_type::new(self.0.file_type().await?))
}
/// Returns the metadata for this entry.
///
/// # Errors
///
/// This function may throw if the metadata could not be retrieved.
pub async extern "Lua-C" fn metadata(&self) -> Result<lb_file_meta> {
Ok(lb_file_meta::new(self.0.metadata().await?))
}
/// Returns the inode number for this entry.
#[cfg(unix)]
pub extern "Lua-C" fn ino(&self) -> u64 {
self.0.ino()
}
/// Returns the full path of this entry.
#[tostring]
pub extern "Lua" fn tostring(&self) -> String {
self.path()
}
}

46
crates/lb/src/fs/glob.rs Normal file
View File

@ -0,0 +1,46 @@
use super::*;
use globset::GlobSet;
use luaffi::{cdef, metatype};
use std::cell::RefCell;
use walkdir::IntoIter;
/// Iterator that yields paths from the filesystem that match a particular pattern.
#[derive(Debug)]
#[cdef]
pub struct lb_glob_dir {
#[opaque]
iter: RefCell<IntoIter>,
#[opaque]
matcher: GlobSet,
#[opaque]
prefix: PathBuf,
}
#[metatype]
impl lb_glob_dir {
pub(super) fn new(iter: IntoIter, matcher: GlobSet, prefix: PathBuf) -> Self {
Self {
iter: RefCell::new(iter),
matcher,
prefix,
}
}
/// Returns the next entry matching the glob pattern, or `nil` if there are no more entries.
///
/// # Errors
///
/// This function may throw if the directory could not be read.
#[call]
pub extern "Lua-C" fn next(&self) -> Result<Option<lb_walk_dir_entry>> {
while let Some(res) = self.iter.try_borrow_mut()?.next() {
let entry = res?;
let path = entry.path().strip_prefix(&self.prefix).unwrap();
if self.matcher.is_match(path) {
return Ok(Some(lb_walk_dir_entry::new(entry)));
}
}
Ok(None)
}
}

190
crates/lb/src/fs/mod.rs Normal file
View File

@ -0,0 +1,190 @@
//! Filesystem library.
//!
//! The `lb:fs` library provides synchronous and asynchronous utilities for interacting with the
//! filesystem.
//!
//! ## Asynchronous by default
//!
//! Filesystem 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 luaffi::{cdef, metatype};
use std::{
cell::{BorrowError, BorrowMutError},
path::PathBuf,
};
use thiserror::Error;
mod r#async;
mod glob;
mod sync;
mod temp;
mod walk;
pub use r#async::*;
pub use glob::*;
pub use sync::*;
pub use temp::*;
pub use walk::*;
/// Errors that can be thrown by this library.
///
/// Functions which return this error will **throw**. The error message can be caught by using
/// [`pcall(f, ...)`](https://www.lua.org/manual/5.1/manual.html#pdf-pcall).
#[derive(Debug, Error)]
pub enum Error {
/// Attempt to access an object while it is being modified.
#[error("cannot access object while it is being modified")]
Borrow(#[from] BorrowError),
/// Attempt to modify an object while it is in use.
#[error("cannot modify object while it is in use")]
BorrowMut(#[from] BorrowMutError),
/// I/O error.
#[error("{0}")]
Io(#[from] std::io::Error),
/// Walk directory error.
#[error("{0}")]
Walk(#[from] walkdir::Error),
/// Glob pattern error.
#[error("{0}")]
Glob(#[from] globset::Error),
}
type Result<T> = std::result::Result<T, Error>;
/// Items exported by the `lb:fs` library.
///
/// This library can be acquired by calling
/// [`require("lb:fs")`](https://www.lua.org/manual/5.1/manual.html#pdf-require).
///
/// ```lua
/// local fs = require("lb:fs")
/// ```
#[cdef(module = "lb:fs")]
pub struct lb_fslib;
#[metatype]
impl lb_fslib {
#[new]
extern "Lua-C" fn new() -> Self {
Self
}
/// Reads the entire contents of a file.
///
/// # Errors
///
/// This function may throw if the file does not exist or could not be read.
pub async extern "Lua-C" fn read(path: &str) -> Result<Vec<u8>> {
Ok(tokio::fs::read(path).await?)
}
/// Reads the entire contents of a file synchronously.
///
/// This is a synchronous complement to [`read`](Self::read).
///
/// # Errors
///
/// This function may throw if the file does not exist or could not be read.
pub extern "Lua-C" fn read_sync(path: &str) -> Result<Vec<u8>> {
Ok(std::fs::read(path)?)
}
/// Writes the given contents to a file, replacing its contents if it exists.
///
/// # Errors
///
/// This function may throw if the file could not be written.
pub async extern "Lua-C" fn write(path: &str, contents: &[u8]) -> Result<()> {
Ok(tokio::fs::write(path, contents).await?)
}
/// Writes the given contents to a file synchronously, replacing its contents if it exists.
///
/// This is a synchronous complement to [`write`](Self::write).
///
/// # Errors
///
/// This function may throw if the file could not be written.
pub extern "Lua-C" fn write_sync(path: &str, contents: &[u8]) -> Result<()> {
Ok(std::fs::write(path, contents)?)
}
/// Reads the entries in a directory.
///
/// # Errors
///
/// This function may throw if the directory could not be read.
pub async extern "Lua-C" fn read_dir(path: &str) -> Result<lb_read_dir> {
Ok(lb_read_dir::new(tokio::fs::read_dir(path).await?))
}
/// Reads the entries in a directory synchronously.
///
/// This is a synchronous complement to [`read_dir`](Self::read_dir).
///
/// # Errors
///
/// This function may throw if the directory could not be read.
pub extern "Lua-C" fn read_dir_sync(path: &str) -> Result<lb_read_dir_sync> {
Ok(lb_read_dir_sync::new(std::fs::read_dir(path)?))
}
/// Recursively walks the current directory, yielding all entries within it.
pub extern "Lua" fn walk(pattern: &str) -> Result<lb_walk_dir> {
Self::walk_dir(".")
}
/// Recursively walks a directory, yielding all entries within it.
pub extern "Lua-C" fn walk_dir(path: &str) -> lb_walk_dir {
lb_walk_dir::new(walkdir::WalkDir::new(path).into_iter())
}
/// Recursively walks the current directory, yielding all entries within it that match the given
/// glob pattern.
///
/// # Errors
///
/// This function may throw if the pattern is invalid.
pub extern "Lua" fn glob(pattern: &str) -> Result<lb_glob_dir> {
Self::glob_dir(".", pattern)
}
/// Recursively walks a directory, yielding all entries within it that match the given glob
/// pattern.
///
/// # Errors
///
/// This function may throw if the pattern is invalid.
pub extern "Lua-C" fn glob_dir(path: &str, pattern: &str) -> Result<lb_glob_dir> {
let prefix = PathBuf::from(path);
let iter = walkdir::WalkDir::new(path).min_depth(1).into_iter();
let matcher = globset::GlobSet::builder()
.add(globset::Glob::new(pattern)?)
.build()?;
Ok(lb_glob_dir::new(iter, matcher, prefix))
}
/// Creates a new temporary directory.
///
/// # Errors
///
/// This function may throw if the temporary directory could not be created.
pub extern "Lua-C" fn temp_dir() -> Result<lb_temp_dir> {
Ok(lb_temp_dir::new(tempfile::tempdir()?))
}
/// Creates a new temporary directory inside the specified path.
///
/// # Errors
///
/// This function may throw if the temporary directory could not be created.
pub extern "Lua-C" fn temp_dir_in(path: &str) -> Result<lb_temp_dir> {
Ok(lb_temp_dir::new(tempfile::tempdir_in(path)?))
}
}

251
crates/lb/src/fs/sync.rs Normal file
View File

@ -0,0 +1,251 @@
use super::*;
use luaffi::{cdef, metatype};
use std::{
cell::RefCell,
fs::{DirEntry, FileType, Metadata, Permissions, ReadDir},
time::SystemTime,
};
/// Structure representing the type of a file with accessors for each file type.
#[derive(Debug)]
#[cdef]
pub struct lb_file_type(#[opaque] FileType);
#[metatype]
impl lb_file_type {
pub(super) fn new(ty: FileType) -> Self {
Self(ty)
}
/// Returns `true` if this file type is a directory.
pub extern "Lua-C" fn is_dir(&self) -> bool {
self.0.is_dir()
}
/// Returns `true` if this file type is a regular file.
pub extern "Lua-C" fn is_file(&self) -> bool {
self.0.is_file()
}
/// Returns `true` if this file type is a symbolic link.
pub extern "Lua-C" fn is_symlink(&self) -> bool {
self.0.is_file()
}
/// Returns the string `"file"` if this is a regular file, `"dir"` if this is a directory,
/// `"symlink"` if this is a symbolic link, or `"other"` if it is some other type of 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()
}
}
/// Metadata information about a file.
#[derive(Debug)]
#[cdef]
pub struct lb_file_meta(#[opaque] Metadata);
#[metatype]
impl lb_file_meta {
pub(super) fn new(meta: Metadata) -> Self {
Self(meta)
}
/// Returns `true` if this file is a directory.
pub extern "Lua-C" fn is_dir(&self) -> bool {
self.0.is_dir()
}
/// Returns `true` if this file is a regular file.
pub extern "Lua-C" fn is_file(&self) -> bool {
self.0.is_file()
}
/// Returns `true` if this file is a symbolic link.
pub extern "Lua-C" fn is_symlink(&self) -> bool {
self.0.is_file()
}
/// Returns the type of this file.
pub extern "Lua-C" fn r#type(&self) -> lb_file_type {
lb_file_type::new(self.0.file_type())
}
/// Returns the size of this file in bytes.
pub extern "Lua-C" fn size(&self) -> u64 {
self.0.len()
}
/// Returns the permissions of this file.
pub extern "Lua-C" fn perms(&self) -> lb_file_perms {
lb_file_perms::new(self.0.permissions())
}
/// Returns the creation time of this file as seconds since the Unix epoch.
///
/// # Errors
///
/// This function may throw if the creation time could not be retrieved.
pub extern "Lua-C" fn created(&self) -> Result<f64> {
Ok(self
.0
.created()?
.duration_since(SystemTime::UNIX_EPOCH)
.map(|dur| dur.as_secs_f64())
.unwrap_or(0.))
}
/// Returns the modification time of this file as seconds since the Unix epoch.
///
/// # Errors
///
/// This function may throw if the modification time could not be retrieved.
pub extern "Lua-C" fn modified(&self) -> Result<f64> {
Ok(self
.0
.modified()?
.duration_since(SystemTime::UNIX_EPOCH)
.map(|dur| dur.as_secs_f64())
.unwrap_or(0.))
}
/// Returns the last access time of this file as seconds since the Unix epoch.
///
/// # Errors
///
/// This function may throw if the access time could not be retrieved.
pub extern "Lua-C" fn accessed(&self) -> Result<f64> {
Ok(self
.0
.accessed()?
.duration_since(SystemTime::UNIX_EPOCH)
.map(|dur| dur.as_secs_f64())
.unwrap_or(0.))
}
/// Returns a string representation of this file's metadata.
#[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()
}
}
}
/// Representation of the various permissions on a file.
#[derive(Debug)]
#[cdef]
pub struct lb_file_perms(#[opaque] RefCell<Permissions>);
#[metatype]
impl lb_file_perms {
pub(super) fn new(perms: Permissions) -> Self {
Self(RefCell::new(perms))
}
/// Returns `true` if the readonly flag is set.
pub extern "Lua-C" fn readonly(&self) -> bool {
self.0.borrow().readonly()
}
/// Sets the readonly flag.
pub extern "Lua-C" fn set_readonly(&self, readonly: bool) {
self.0.borrow_mut().set_readonly(readonly);
}
}
/// Synchronous version of [`lb_read_dir`].
#[derive(Debug)]
#[cdef]
pub struct lb_read_dir_sync(#[opaque] RefCell<ReadDir>);
#[metatype]
impl lb_read_dir_sync {
pub(super) fn new(iter: ReadDir) -> Self {
Self(RefCell::new(iter))
}
/// Returns the next entry in the directory, or `nil` if there are no more entries.
///
/// # Errors
///
/// This function may throw if the directory could not be read.
#[call]
pub extern "Lua-C" fn next(&self) -> Result<Option<lb_dir_entry_sync>> {
Ok(self
.0
.try_borrow_mut()?
.next()
.transpose()?
.map(lb_dir_entry_sync::new))
}
}
/// Synchronous version of [`lb_dir_entry`].
#[derive(Debug)]
#[cdef]
pub struct lb_dir_entry_sync(#[opaque] DirEntry);
#[metatype]
impl lb_dir_entry_sync {
pub(super) fn new(entry: DirEntry) -> Self {
Self(entry)
}
/// Returns the full path of this entry.
pub extern "Lua-C" fn path(&self) -> String {
self.0.path().to_string_lossy().into()
}
/// Returns the file name of this entry.
pub extern "Lua-C" fn name(&self) -> String {
self.0.file_name().to_string_lossy().into()
}
/// Returns the type of this entry.
///
/// # Errors
///
/// This function may throw if the file type could not be determined.
pub extern "Lua-C" fn r#type(&self) -> Result<lb_file_type> {
Ok(lb_file_type::new(self.0.file_type()?))
}
/// Returns the metadata for this entry.
///
/// # Errors
///
/// This function may throw if the metadata could not be retrieved.
pub extern "Lua-C" fn metadata(&self) -> Result<lb_file_meta> {
Ok(lb_file_meta::new(self.0.metadata()?))
}
/// Returns the inode number for this entry.
#[cfg(unix)]
pub extern "Lua-C" fn ino(&self) -> u64 {
use std::os::unix::fs::DirEntryExt;
self.0.ino()
}
/// Returns the full path of this entry.
#[tostring]
pub extern "Lua" fn tostring(&self) -> String {
self.path()
}
}

25
crates/lb/src/fs/temp.rs Normal file
View File

@ -0,0 +1,25 @@
use luaffi::{cdef, metatype};
use tempfile::TempDir;
/// Directory in the filesystem that is automatically deleted when it is garbage-collected.
#[derive(Debug)]
#[cdef]
pub struct lb_temp_dir(#[opaque] TempDir);
#[metatype]
impl lb_temp_dir {
pub(super) fn new(dir: TempDir) -> Self {
Self(dir)
}
/// Returns the full path of this temporary directory.
pub extern "Lua-C" fn path(&self) -> String {
self.0.path().to_string_lossy().into()
}
/// Returns the full path of this temporary directory.
#[tostring]
pub extern "Lua" fn tostring(&self) -> String {
self.path()
}
}

90
crates/lb/src/fs/walk.rs Normal file
View File

@ -0,0 +1,90 @@
use super::*;
use luaffi::{cdef, metatype};
use std::cell::RefCell;
use walkdir::{DirEntry, IntoIter};
/// Iterator for recursively descending into a directory.
#[derive(Debug)]
#[cdef]
pub struct lb_walk_dir(#[opaque] RefCell<IntoIter>);
#[metatype]
impl lb_walk_dir {
pub(super) fn new(iter: IntoIter) -> Self {
Self(RefCell::new(iter))
}
/// Returns the next entry in the walk, or `nil` if there are no more entries.
///
/// # Errors
///
/// This function may throw if the directory could not be read.
#[call]
pub extern "Lua-C" fn next(&self) -> Result<Option<lb_walk_dir_entry>> {
Ok(self
.0
.try_borrow_mut()?
.next()
.transpose()?
.map(lb_walk_dir_entry::new))
}
}
/// Entry inside of a directory on the filesystem obtained from [`lb_walk_dir`].
#[derive(Debug)]
#[cdef]
pub struct lb_walk_dir_entry(#[opaque] DirEntry);
#[metatype]
impl lb_walk_dir_entry {
pub(super) fn new(entry: DirEntry) -> Self {
Self(entry)
}
/// Returns the full path of this entry.
pub extern "Lua-C" fn path(&self) -> String {
self.0.path().to_string_lossy().into()
}
/// Returns the file name of this entry.
pub extern "Lua-C" fn name(&self) -> String {
self.0.file_name().to_string_lossy().into()
}
/// Returns the type of this entry.
pub extern "Lua-C" fn r#type(&self) -> lb_file_type {
lb_file_type::new(self.0.file_type())
}
/// Returns the metadata for this entry.
///
/// # Errors
///
/// This function may throw if the metadata could not be retrieved.
pub extern "Lua-C" fn metadata(&self) -> Result<lb_file_meta> {
Ok(lb_file_meta::new(self.0.metadata()?))
}
/// Returns `true` if this entry was created from a symbolic link.
pub extern "Lua-C" fn is_symlink(&self) -> bool {
self.0.path_is_symlink()
}
/// Returns the depth of this entry in the walk.
pub extern "Lua-C" fn depth(&self) -> u32 {
self.0.depth() as u32
}
/// Returns the inode number for this entry.
#[cfg(unix)]
pub extern "Lua-C" fn ino(&self) -> u64 {
use walkdir::DirEntryExt;
self.0.ino()
}
/// Returns the full path of this entry.
#[tostring]
pub extern "Lua" fn tostring(&self) -> String {
self.path()
}
}

View File

@ -13,7 +13,7 @@ use luaffi::{cdef, metatype};
/// [`require("lb:sqlite")`](https://www.lua.org/manual/5.1/manual.html#pdf-require). /// [`require("lb:sqlite")`](https://www.lua.org/manual/5.1/manual.html#pdf-require).
/// ///
/// ```lua /// ```lua
/// local sqlite = require("lb:sqlite"); /// local sqlite = require("lb:sqlite")
/// ``` /// ```
#[cdef(module = "lb:time")] #[cdef(module = "lb:time")]
pub struct lb_sqlitelib; pub struct lb_sqlitelib;