Fix all borrow errors

This commit is contained in:
lumi 2025-06-28 17:20:40 +10:00
parent 124e9bedfe
commit eaa40ff3bc
Signed by: luaneko
GPG Key ID: 406809B8763FF07A
3 changed files with 349 additions and 132 deletions

View File

@ -12,9 +12,12 @@
//! ## Exports //! ## Exports
//! //!
//! See [`lb_fslib`] for items exported by this library. //! See [`lb_fslib`] for items exported by this library.
use derive_more::From;
use luaffi::{cdef, metatype}; use luaffi::{cdef, metatype};
use std::{path::PathBuf, time::SystemTime}; use std::{
cell::{BorrowError, BorrowMutError, RefCell},
path::PathBuf,
time::SystemTime,
};
use thiserror::Error; use thiserror::Error;
/// Errors that can be thrown by this library. /// Errors that can be thrown by this library.
@ -23,6 +26,12 @@ use thiserror::Error;
/// using [`pcall(f, ...)`](https://www.lua.org/manual/5.1/manual.html#pdf-pcall). /// using [`pcall(f, ...)`](https://www.lua.org/manual/5.1/manual.html#pdf-pcall).
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum 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. /// I/O error.
#[error("{0}")] #[error("{0}")]
Io(#[from] std::io::Error), Io(#[from] std::io::Error),
@ -54,38 +63,85 @@ impl lb_fslib {
Self Self
} }
/// Reads the entire contents of a file.
///
/// # Errors
///
/// This function may throw if the file does not exist or cannot be read.
pub async extern "Lua-C" fn read(path: &str) -> Result<Vec<u8>> { pub async extern "Lua-C" fn read(path: &str) -> Result<Vec<u8>> {
Ok(tokio::fs::read(path).await?) 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 cannot be read.
pub extern "Lua-C" fn read_sync(path: &str) -> Result<Vec<u8>> { pub extern "Lua-C" fn read_sync(path: &str) -> Result<Vec<u8>> {
Ok(std::fs::read(path)?) 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 cannot be written.
pub async extern "Lua-C" fn write(path: &str, contents: &[u8]) -> Result<()> { pub async extern "Lua-C" fn write(path: &str, contents: &[u8]) -> Result<()> {
Ok(tokio::fs::write(path, contents).await?) 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 cannot be written.
pub extern "Lua-C" fn write_sync(path: &str, contents: &[u8]) -> Result<()> { pub extern "Lua-C" fn write_sync(path: &str, contents: &[u8]) -> Result<()> {
Ok(std::fs::write(path, contents)?) Ok(std::fs::write(path, contents)?)
} }
/// Reads the entries in a directory.
///
/// # Errors
///
/// This function may throw if the directory cannot be read.
pub async extern "Lua-C" fn read_dir(path: &str) -> Result<lb_read_dir> { pub async extern "Lua-C" fn read_dir(path: &str) -> Result<lb_read_dir> {
Ok(tokio::fs::read_dir(path).await?.into()) 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 cannot be read.
pub extern "Lua-C" fn read_dir_sync(path: &str) -> Result<lb_read_dir_sync> { pub extern "Lua-C" fn read_dir_sync(path: &str) -> Result<lb_read_dir_sync> {
Ok(std::fs::read_dir(path)?.into()) 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 { pub extern "Lua-C" fn walk_dir(path: &str) -> lb_walk_dir {
walkdir::WalkDir::new(path).into_iter().into() lb_walk_dir::new(walkdir::WalkDir::new(path).into_iter())
} }
pub extern "Lua-C" fn glob(pattern: &str) -> Result<lb_glob_dir> { /// 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) 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> { pub extern "Lua-C" fn glob_dir(path: &str, pattern: &str) -> Result<lb_glob_dir> {
let prefix = PathBuf::from(path); let prefix = PathBuf::from(path);
let iter = walkdir::WalkDir::new(path).min_depth(1).into_iter(); let iter = walkdir::WalkDir::new(path).min_depth(1).into_iter();
@ -93,76 +149,128 @@ impl lb_fslib {
.add(globset::Glob::new(pattern)?) .add(globset::Glob::new(pattern)?)
.build()?; .build()?;
Ok(lb_glob_dir { Ok(lb_glob_dir::new(iter, matcher, prefix))
iter,
matcher,
prefix,
})
} }
/// Creates a new temporary directory.
///
/// # Errors
///
/// This function may throw if the temporary directory cannot be created.
pub extern "Lua-C" fn temp_dir() -> Result<lb_temp_dir> { pub extern "Lua-C" fn temp_dir() -> Result<lb_temp_dir> {
Ok(tempfile::tempdir()?.into()) 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 cannot be created.
pub extern "Lua-C" fn temp_dir_in(path: &str) -> Result<lb_temp_dir> { pub extern "Lua-C" fn temp_dir_in(path: &str) -> Result<lb_temp_dir> {
Ok(tempfile::tempdir_in(path)?.into()) Ok(lb_temp_dir::new(tempfile::tempdir_in(path)?))
} }
} }
/// Iterator over the entries in a directory. /// Iterator over the entries in a directory.
#[derive(Debug, From)] #[derive(Debug)]
#[cdef] #[cdef]
pub struct lb_read_dir(#[opaque] tokio::fs::ReadDir); pub struct lb_read_dir(#[opaque] RefCell<tokio::fs::ReadDir>);
#[metatype] #[metatype]
impl lb_read_dir { 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 cannot be read.
#[call] #[call]
pub async extern "Lua-C" fn next(&mut self) -> Result<Option<lb_dir_entry>> { pub async extern "Lua-C" fn next(&self) -> Result<Option<lb_dir_entry>> {
Ok(self.0.next_entry().await?.map(Into::into)) Ok(self
.0
.try_borrow_mut()?
.next_entry()
.await?
.map(lb_dir_entry::new))
} }
} }
/// Synchronous version of [`lb_read_dir`]. /// Synchronous version of [`lb_read_dir`].
#[derive(Debug, From)] #[derive(Debug)]
#[cdef] #[cdef]
pub struct lb_read_dir_sync(#[opaque] std::fs::ReadDir); pub struct lb_read_dir_sync(#[opaque] RefCell<std::fs::ReadDir>);
#[metatype] #[metatype]
impl lb_read_dir_sync { 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 cannot be read.
#[call] #[call]
pub extern "Lua-C" fn next(&mut self) -> Result<Option<lb_dir_entry_sync>> { pub extern "Lua-C" fn next(&self) -> Result<Option<lb_dir_entry_sync>> {
Ok(self.0.next().transpose()?.map(Into::into)) Ok(self
.0
.try_borrow_mut()?
.next()
.transpose()?
.map(lb_dir_entry_sync::new))
} }
} }
/// Entry inside of a directory on the filesystem. /// Entry inside of a directory on the filesystem.
#[derive(Debug, From)] #[derive(Debug)]
#[cdef] #[cdef]
pub struct lb_dir_entry(#[opaque] tokio::fs::DirEntry); pub struct lb_dir_entry(#[opaque] tokio::fs::DirEntry);
#[metatype] #[metatype]
impl lb_dir_entry { 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 { pub extern "Lua-C" fn path(&self) -> String {
self.0.path().to_string_lossy().into() self.0.path().to_string_lossy().into()
} }
/// Returns the file name of this entry.
pub extern "Lua-C" fn name(&self) -> String { pub extern "Lua-C" fn name(&self) -> String {
self.0.file_name().to_string_lossy().into() self.0.file_name().to_string_lossy().into()
} }
/// Returns the type of this entry.
///
/// # Errors
///
/// This function may throw if the file type cannot be determined.
pub async extern "Lua-C" fn r#type(&self) -> Result<lb_file_type> { pub async extern "Lua-C" fn r#type(&self) -> Result<lb_file_type> {
Ok(self.0.file_type().await?.into()) Ok(lb_file_type::new(self.0.file_type().await?))
} }
/// Returns the metadata for this entry.
///
/// # Errors
///
/// This function may throw if the metadata cannot be retrieved.
pub async extern "Lua-C" fn metadata(&self) -> Result<lb_file_meta> { pub async extern "Lua-C" fn metadata(&self) -> Result<lb_file_meta> {
Ok(self.0.metadata().await?.into()) Ok(lb_file_meta::new(self.0.metadata().await?))
} }
/// Returns the inode number for this entry.
#[cfg(unix)] #[cfg(unix)]
pub extern "Lua-C" fn ino(&self) -> u64 { pub extern "Lua-C" fn ino(&self) -> u64 {
self.0.ino() self.0.ino()
} }
/// Returns the full path of this entry.
#[tostring] #[tostring]
pub extern "Lua" fn tostring(&self) -> String { pub extern "Lua" fn tostring(&self) -> String {
self.path() self.path()
@ -170,34 +278,52 @@ impl lb_dir_entry {
} }
/// Synchronous version of [`lb_dir_entry`]. /// Synchronous version of [`lb_dir_entry`].
#[derive(Debug, From)] #[derive(Debug)]
#[cdef] #[cdef]
pub struct lb_dir_entry_sync(#[opaque] std::fs::DirEntry); pub struct lb_dir_entry_sync(#[opaque] std::fs::DirEntry);
#[metatype] #[metatype]
impl lb_dir_entry_sync { 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 { pub extern "Lua-C" fn path(&self) -> String {
self.0.path().to_string_lossy().into() self.0.path().to_string_lossy().into()
} }
/// Returns the file name of this entry.
pub extern "Lua-C" fn name(&self) -> String { pub extern "Lua-C" fn name(&self) -> String {
self.0.file_name().to_string_lossy().into() self.0.file_name().to_string_lossy().into()
} }
/// Returns the type of this entry.
///
/// # Errors
///
/// This function may throw if the file type cannot be determined.
pub extern "Lua-C" fn r#type(&self) -> Result<lb_file_type> { pub extern "Lua-C" fn r#type(&self) -> Result<lb_file_type> {
Ok(self.0.file_type()?.into()) Ok(lb_file_type::new(self.0.file_type()?))
} }
/// Returns the metadata for this entry.
///
/// # Errors
///
/// This function may throw if the metadata cannot be retrieved.
pub extern "Lua-C" fn metadata(&self) -> Result<lb_file_meta> { pub extern "Lua-C" fn metadata(&self) -> Result<lb_file_meta> {
Ok(self.0.metadata()?.into()) Ok(lb_file_meta::new(self.0.metadata()?))
} }
/// Returns the inode number for this entry.
#[cfg(unix)] #[cfg(unix)]
pub extern "Lua-C" fn ino(&self) -> u64 { pub extern "Lua-C" fn ino(&self) -> u64 {
use std::os::unix::fs::DirEntryExt; use std::os::unix::fs::DirEntryExt;
self.0.ino() self.0.ino()
} }
/// Returns the full path of this entry.
#[tostring] #[tostring]
pub extern "Lua" fn tostring(&self) -> String { pub extern "Lua" fn tostring(&self) -> String {
self.path() self.path()
@ -205,24 +331,33 @@ impl lb_dir_entry_sync {
} }
/// Structure representing the type of a file with accessors for each file type. /// Structure representing the type of a file with accessors for each file type.
#[derive(Debug, From)] #[derive(Debug)]
#[cdef] #[cdef]
pub struct lb_file_type(#[opaque] std::fs::FileType); pub struct lb_file_type(#[opaque] std::fs::FileType);
#[metatype] #[metatype]
impl lb_file_type { 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 { pub extern "Lua-C" fn is_dir(&self) -> bool {
self.0.is_dir() self.0.is_dir()
} }
/// Returns `true` if this file type is a regular file.
pub extern "Lua-C" fn is_file(&self) -> bool { pub extern "Lua-C" fn is_file(&self) -> bool {
self.0.is_file() self.0.is_file()
} }
/// Returns `true` if this file type is a symbolic link.
pub extern "Lua-C" fn is_symlink(&self) -> bool { pub extern "Lua-C" fn is_symlink(&self) -> bool {
self.0.is_file() 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] #[tostring]
pub extern "Lua-C" fn tostring(&self) -> String { pub extern "Lua-C" fn tostring(&self) -> String {
if self.0.is_file() { if self.0.is_file() {
@ -239,36 +374,51 @@ impl lb_file_type {
} }
/// Metadata information about a file. /// Metadata information about a file.
#[derive(Debug, From)] #[derive(Debug)]
#[cdef] #[cdef]
pub struct lb_file_meta(#[opaque] std::fs::Metadata); pub struct lb_file_meta(#[opaque] std::fs::Metadata);
#[metatype] #[metatype]
impl lb_file_meta { 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 { pub extern "Lua-C" fn is_dir(&self) -> bool {
self.0.is_dir() self.0.is_dir()
} }
/// Returns `true` if this file is a regular file.
pub extern "Lua-C" fn is_file(&self) -> bool { pub extern "Lua-C" fn is_file(&self) -> bool {
self.0.is_file() self.0.is_file()
} }
/// Returns `true` if this file is a symbolic link.
pub extern "Lua-C" fn is_symlink(&self) -> bool { pub extern "Lua-C" fn is_symlink(&self) -> bool {
self.0.is_file() self.0.is_file()
} }
/// Returns the type of this file.
pub extern "Lua-C" fn r#type(&self) -> lb_file_type { pub extern "Lua-C" fn r#type(&self) -> lb_file_type {
self.0.file_type().into() lb_file_type::new(self.0.file_type())
} }
/// Returns the size of this file in bytes.
pub extern "Lua-C" fn size(&self) -> u64 { pub extern "Lua-C" fn size(&self) -> u64 {
self.0.len() self.0.len()
} }
/// Returns the permissions of this file.
pub extern "Lua-C" fn perms(&self) -> lb_file_perms { pub extern "Lua-C" fn perms(&self) -> lb_file_perms {
self.0.permissions().into() 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 cannot be retrieved.
pub extern "Lua-C" fn created(&self) -> Result<f64> { pub extern "Lua-C" fn created(&self) -> Result<f64> {
Ok(self Ok(self
.0 .0
@ -278,6 +428,11 @@ impl lb_file_meta {
.unwrap_or(0.)) .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 cannot be retrieved.
pub extern "Lua-C" fn modified(&self) -> Result<f64> { pub extern "Lua-C" fn modified(&self) -> Result<f64> {
Ok(self Ok(self
.0 .0
@ -287,6 +442,11 @@ impl lb_file_meta {
.unwrap_or(0.)) .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 cannot be retrieved.
pub extern "Lua-C" fn accessed(&self) -> Result<f64> { pub extern "Lua-C" fn accessed(&self) -> Result<f64> {
Ok(self Ok(self
.0 .0
@ -296,6 +456,7 @@ impl lb_file_meta {
.unwrap_or(0.)) .unwrap_or(0.))
} }
/// Returns a string representation of this file's metadata.
#[tostring] #[tostring]
pub extern "Lua-C" fn tostring(&self) -> String { pub extern "Lua-C" fn tostring(&self) -> String {
let ty = self.0.file_type(); let ty = self.0.file_type();
@ -312,71 +473,107 @@ impl lb_file_meta {
} }
/// Representation of the various permissions on a file. /// Representation of the various permissions on a file.
#[derive(Debug, From)] #[derive(Debug)]
#[cdef] #[cdef]
pub struct lb_file_perms(#[opaque] std::fs::Permissions); pub struct lb_file_perms(#[opaque] RefCell<std::fs::Permissions>);
#[metatype] #[metatype]
impl lb_file_perms { impl lb_file_perms {
pub extern "Lua-C" fn readonly(&self) -> bool { fn new(perms: std::fs::Permissions) -> Self {
self.0.readonly() Self(RefCell::new(perms))
} }
pub extern "Lua-C" fn set_readonly(&mut self, readonly: bool) { /// Returns `true` if the readonly flag is set.
self.0.set_readonly(readonly); 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. /// Iterator for recursively descending into a directory.
#[derive(Debug, From)] #[derive(Debug)]
#[cdef] #[cdef]
pub struct lb_walk_dir(#[opaque] walkdir::IntoIter); pub struct lb_walk_dir(#[opaque] RefCell<walkdir::IntoIter>);
#[metatype] #[metatype]
impl lb_walk_dir { 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 cannot be read.
#[call] #[call]
pub extern "Lua-C" fn next(&mut self) -> Result<Option<lb_walk_dir_entry>> { pub extern "Lua-C" fn next(&self) -> Result<Option<lb_walk_dir_entry>> {
Ok(self.0.next().transpose()?.map(Into::into)) 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`]. /// Entry inside of a directory on the filesystem obtained from [`lb_walk_dir`].
#[derive(Debug, From)] #[derive(Debug)]
#[cdef] #[cdef]
pub struct lb_walk_dir_entry(#[opaque] walkdir::DirEntry); pub struct lb_walk_dir_entry(#[opaque] walkdir::DirEntry);
#[metatype] #[metatype]
impl lb_walk_dir_entry { 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 { pub extern "Lua-C" fn path(&self) -> String {
self.0.path().to_string_lossy().into() self.0.path().to_string_lossy().into()
} }
/// Returns the file name of this entry.
pub extern "Lua-C" fn name(&self) -> String { pub extern "Lua-C" fn name(&self) -> String {
self.0.file_name().to_string_lossy().into() 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 { pub extern "Lua-C" fn r#type(&self) -> lb_file_type {
self.0.file_type().into() lb_file_type::new(self.0.file_type())
} }
/// Returns the metadata for this entry.
///
/// # Errors
///
/// This function may throw if the metadata cannot be retrieved.
pub extern "Lua-C" fn metadata(&self) -> Result<lb_file_meta> { pub extern "Lua-C" fn metadata(&self) -> Result<lb_file_meta> {
Ok(self.0.metadata()?.into()) 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 { pub extern "Lua-C" fn is_symlink(&self) -> bool {
self.0.path_is_symlink() self.0.path_is_symlink()
} }
/// Returns the depth of this entry in the walk.
pub extern "Lua-C" fn depth(&self) -> u32 { pub extern "Lua-C" fn depth(&self) -> u32 {
self.0.depth() as u32 self.0.depth() as u32
} }
/// Returns the inode number for this entry.
#[cfg(unix)] #[cfg(unix)]
pub extern "Lua-C" fn ino(&self) -> u64 { pub extern "Lua-C" fn ino(&self) -> u64 {
use walkdir::DirEntryExt; use walkdir::DirEntryExt;
self.0.ino() self.0.ino()
} }
/// Returns the full path of this entry.
#[tostring] #[tostring]
pub extern "Lua" fn tostring(&self) -> String { pub extern "Lua" fn tostring(&self) -> String {
self.path() self.path()
@ -388,7 +585,7 @@ impl lb_walk_dir_entry {
#[cdef] #[cdef]
pub struct lb_glob_dir { pub struct lb_glob_dir {
#[opaque] #[opaque]
iter: walkdir::IntoIter, iter: RefCell<walkdir::IntoIter>,
#[opaque] #[opaque]
matcher: globset::GlobSet, matcher: globset::GlobSet,
#[opaque] #[opaque]
@ -397,13 +594,26 @@ pub struct lb_glob_dir {
#[metatype] #[metatype]
impl lb_glob_dir { 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 cannot be read.
#[call] #[call]
pub extern "Lua-C" fn next(&mut self) -> Result<Option<lb_walk_dir_entry>> { pub extern "Lua-C" fn next(&self) -> Result<Option<lb_walk_dir_entry>> {
while let Some(res) = self.iter.next() { while let Some(res) = self.iter.try_borrow_mut()?.next() {
let entry = res?; let entry = res?;
let path = entry.path().strip_prefix(&self.prefix).unwrap(); let path = entry.path().strip_prefix(&self.prefix).unwrap();
if self.matcher.is_match(path) { if self.matcher.is_match(path) {
return Ok(Some(entry.into())); return Ok(Some(lb_walk_dir_entry::new(entry)));
} }
} }
@ -412,16 +622,22 @@ impl lb_glob_dir {
} }
/// Directory in the filesystem that is automatically deleted when it is garbage-collected. /// Directory in the filesystem that is automatically deleted when it is garbage-collected.
#[derive(Debug, From)] #[derive(Debug)]
#[cdef] #[cdef]
pub struct lb_temp_dir(#[opaque] tempfile::TempDir); pub struct lb_temp_dir(#[opaque] tempfile::TempDir);
#[metatype] #[metatype]
impl lb_temp_dir { 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 { pub extern "Lua-C" fn path(&self) -> String {
self.0.path().to_string_lossy().into() self.0.path().to_string_lossy().into()
} }
/// Returns the full path of this temporary directory.
#[tostring] #[tostring]
pub extern "Lua" fn tostring(&self) -> String { pub extern "Lua" fn tostring(&self) -> String {
self.path() self.path()

View File

@ -9,7 +9,7 @@
use derive_more::{From, FromStr}; use derive_more::{From, FromStr};
use luaffi::{cdef, marker::OneOf, metatype}; use luaffi::{cdef, marker::OneOf, metatype};
use std::{ use std::{
cell::{Ref, RefCell, RefMut}, cell::{BorrowError, BorrowMutError, Ref, RefCell, RefMut},
net::{AddrParseError, IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}, net::{AddrParseError, IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
time::Duration, time::Duration,
}; };
@ -25,13 +25,19 @@ use tokio::{
/// using [`pcall(f, ...)`](https://www.lua.org/manual/5.1/manual.html#pdf-pcall). /// using [`pcall(f, ...)`](https://www.lua.org/manual/5.1/manual.html#pdf-pcall).
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum 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. /// I/O error.
#[error("{0}")] #[error("{0}")]
Io(#[from] std::io::Error), Io(#[from] std::io::Error),
/// IP or socket address syntax error. /// IP or socket address syntax error.
#[error("{0}")] #[error("{0}")]
InvalidAddr(#[from] AddrParseError), InvalidAddr(#[from] AddrParseError),
/// Socket was already converted and cannot be used anymore. /// Socket was already converted and can no longer be used.
#[error("socket was already converted")] #[error("socket was already converted")]
SocketConsumed, SocketConsumed,
} }
@ -85,7 +91,7 @@ impl lb_netlib {
/// ///
/// If `addr` is an [`lb_ipaddr`], a copy of that value is returned. If `addr` is an /// If `addr` is an [`lb_ipaddr`], a copy of that value is returned. If `addr` is an
/// [`lb_socketaddr`], the IP part of the socket address is returned. Otherwise, parses `addr` /// [`lb_socketaddr`], the IP part of the socket address is returned. Otherwise, parses `addr`
/// as an IP address string. Both IPv4 or IPv6 addresses are accepted. /// as an IP address string. Both IPv4 and IPv6 addresses are accepted.
/// ///
/// An address string may be something like `127.0.0.1`. /// An address string may be something like `127.0.0.1`.
/// ///
@ -303,7 +309,7 @@ impl lb_ipaddr {
self.0.is_ipv6() self.0.is_ipv6()
} }
/// Returns the string `"v4"` if this is an IPv4 address or `"v6"` if this is an IPv6 address. /// Returns the string `"v4"` if this is an IPv4 address, or `"v6"` if this is an IPv6 address.
pub extern "Lua" fn family(&self) -> String { pub extern "Lua" fn family(&self) -> String {
if self.is_v6() { "v6" } else { "v4" } if self.is_v6() { "v6" } else { "v4" }
} }
@ -503,10 +509,12 @@ impl lb_socketaddr {
self.0.port() self.0.port()
} }
/// Returns a new socket address with the given IP address and the same port.
pub extern "Lua-C" fn with_ip(&self, ip: &lb_ipaddr) -> Self { pub extern "Lua-C" fn with_ip(&self, ip: &lb_ipaddr) -> Self {
SocketAddr::new(ip.0, self.port()).into() SocketAddr::new(ip.0, self.port()).into()
} }
/// Returns a new socket address with the given port and the same IP address.
pub extern "Lua-C" fn with_port(&self, port: u16) -> Self { pub extern "Lua-C" fn with_port(&self, port: u16) -> Self {
SocketAddr::new(self.ip().0, port).into() SocketAddr::new(self.ip().0, port).into()
} }
@ -547,7 +555,7 @@ impl lb_tcpsocket {
Self(RefCell::new(Some(socket))) Self(RefCell::new(Some(socket)))
} }
fn socket(&self) -> Result<Ref<TcpSocket>> { fn socket<'s>(&'s self) -> Result<Ref<'s, TcpSocket>> {
let socket = self.0.borrow(); let socket = self.0.borrow();
match *socket { match *socket {
Some(_) => Ok(Ref::map(socket, |s| s.as_ref().unwrap())), Some(_) => Ok(Ref::map(socket, |s| s.as_ref().unwrap())),
@ -640,7 +648,7 @@ impl lb_tcpsocket {
/// Sets the value of the `TCP_NODELAY` option on this socket. /// Sets the value of the `TCP_NODELAY` option on this socket.
/// ///
/// This enables or disables Nagle's algorithm which delays sending small packets. /// This enables or disables Nagle's algorithm, which delays sending small packets.
pub extern "Lua-C" fn set_nodelay(&self, enabled: bool) -> Result<()> { pub extern "Lua-C" fn set_nodelay(&self, enabled: bool) -> Result<()> {
Ok(self.socket()?.set_nodelay(enabled)?) Ok(self.socket()?.set_nodelay(enabled)?)
} }
@ -682,71 +690,45 @@ impl lb_tcpsocket {
/// TCP connection between a local and a remote socket. /// TCP connection between a local and a remote socket.
#[derive(Debug)] #[derive(Debug)]
#[cdef] #[cdef]
pub struct lb_tcpstream(#[opaque] RefCell<TcpStream>); pub struct lb_tcpstream {
#[opaque]
read: RefCell<tokio::net::tcp::OwnedReadHalf>,
#[opaque]
write: RefCell<tokio::net::tcp::OwnedWriteHalf>,
}
#[metatype] #[metatype]
impl lb_tcpstream { impl lb_tcpstream {
fn new(stream: TcpStream) -> Self { fn new(stream: TcpStream) -> Self {
Self(RefCell::new(stream)) let (read, write) = stream.into_split();
Self {
read: RefCell::new(read),
write: RefCell::new(write),
}
} }
fn stream(&self) -> Ref<TcpStream> { fn read_half<'s>(&'s self) -> Result<RefMut<'s, tokio::net::tcp::OwnedReadHalf>> {
self.0.borrow() Ok(self.read.try_borrow_mut()?)
} }
fn stream_mut(&self) -> RefMut<TcpStream> { fn write_half<'s>(&'s self) -> Result<RefMut<'s, tokio::net::tcp::OwnedWriteHalf>> {
self.0.borrow_mut() Ok(self.write.try_borrow_mut()?)
} }
/// Gets the remote address of this stream. /// Gets the remote address of this stream.
pub extern "Lua-C" fn peer_addr(&self) -> Result<lb_socketaddr> { pub extern "Lua-C" fn peer_addr(&self) -> Result<lb_socketaddr> {
Ok(self.stream().peer_addr()?.into()) Ok(self.read_half()?.peer_addr()?.into())
} }
/// Gets the local address of this stream. /// Gets the local address of this stream.
pub extern "Lua-C" fn local_addr(&self) -> Result<lb_socketaddr> { pub extern "Lua-C" fn local_addr(&self) -> Result<lb_socketaddr> {
Ok(self.stream().local_addr()?.into()) Ok(self.read_half()?.local_addr()?.into())
} }
/// Gets the value of the `SO_LINGER` option on this stream, in seconds. /// Waits for this stream to be ready in the given half.
pub extern "Lua-C" fn linger(&self) -> Result<f64> {
Ok(self
.stream()
.linger()?
.map(|n| n.as_secs_f64())
.unwrap_or(0.))
}
/// Sets the value of the `SO_LINGER` option on this stream.
pub extern "Lua-C" fn set_linger(&self, secs: f64) -> Result<()> {
Ok(self
.stream()
.set_linger((secs != 0.).then_some(Duration::from_secs_f64(secs)))?)
}
/// Gets the value of the `TCP_NODELAY` option on this stream.
pub extern "Lua-C" fn nodelay(&self) -> Result<bool> {
Ok(self.stream().nodelay()?)
}
/// Sets the value of the `TCP_NODELAY` option on this stream.
/// ///
/// This enables or disables Nagle's algorithm which delays sending small packets. /// `half` can be `"read"` for the readable half, `"write"` for the writable half, or `nil` for
pub extern "Lua-C" fn set_nodelay(&self, enabled: bool) -> Result<()> { /// both.
Ok(self.stream().set_nodelay(enabled)?)
}
/// Gets the value of the `IP_TTL` option on this stream.
pub extern "Lua-C" fn ttl(&self) -> Result<u32> {
Ok(self.stream().ttl()?)
}
/// Sets the value of the `IP_TTL` option on this stream.
pub extern "Lua-C" fn set_ttl(&self, ttl: u32) -> Result<()> {
Ok(self.stream().set_ttl(ttl)?)
}
/// Waits for the stream to be ready for the given half (`"read"`, `"write"`, or `nil` for both halves).
pub async extern "Lua-C" fn ready(&self, half: Option<&str>) -> Result<()> { pub async extern "Lua-C" fn ready(&self, half: Option<&str>) -> Result<()> {
let ty = match half { let ty = match half {
None => Interest::READABLE | Interest::WRITABLE, None => Interest::READABLE | Interest::WRITABLE,
@ -758,29 +740,29 @@ impl lb_tcpstream {
))?, ))?,
}; };
self.stream().ready(ty).await?; self.read_half()?.ready(ty).await?;
Ok(()) Ok(())
} }
/// Reads exactly `len` bytes from the stream. Returns None on EOF. /// Reads exactly `len` bytes from this stream.
///
/// If the connection was closed, this returns `nil`.
pub async extern "Lua-C" fn read(&self, len: u32) -> Result<Option<Vec<u8>>> { pub async extern "Lua-C" fn read(&self, len: u32) -> Result<Option<Vec<u8>>> {
let mut buf = vec![0; len as usize]; let mut buf = vec![0; len as usize];
match self.stream_mut().read_exact(&mut buf).await { match self.read_half()?.read_exact(&mut buf).await {
Ok(_) => Ok(Some(buf)), Ok(_) => Ok(Some(buf)),
Err(err) if err.kind() == std::io::ErrorKind::UnexpectedEof => Ok(None), Err(err) if err.kind() == std::io::ErrorKind::UnexpectedEof => Ok(None),
Err(err) => Err(err.into()), Err(err) => Err(err.into()),
} }
} }
/// Writes the given bytes to the stream. /// Reads up to `len` bytes from this stream.
pub async extern "Lua-C" fn write(&self, buf: &[u8]) -> Result<()> { ///
Ok(self.stream_mut().write_all(buf).await?) /// The returned bytes may be less than `len` in length if the stream had less data available in
} /// queue. If the connection was closed, this returns `nil`.
/// Reads up to `len` bytes from the stream. Returns None on EOF.
pub async extern "Lua-C" fn read_partial(&self, len: u32) -> Result<Option<Vec<u8>>> { pub async extern "Lua-C" fn read_partial(&self, len: u32) -> Result<Option<Vec<u8>>> {
let mut buf = vec![0; len as usize]; let mut buf = vec![0; len as usize];
let n = self.stream_mut().read(&mut buf).await?; let n = self.read_half()?.read(&mut buf).await?;
if n == 0 { if n == 0 {
Ok(None) Ok(None)
} else { } else {
@ -789,15 +771,13 @@ impl lb_tcpstream {
} }
} }
/// Writes the given bytes to the stream and returns the number of bytes successfully written. /// Attempts to read up to `len` bytes from this stream without waiting.
pub async extern "Lua-C" fn write_partial(&self, buf: &[u8]) -> Result<u32> { ///
Ok(self.stream_mut().write(buf).await? as u32) /// The returned bytes may be less than `len` in length if the stream had less data available in
} /// queue. If there was no data available or the connection was closed, this returns `nil`.
/// Attempts to read up to `len` bytes from the stream without waiting. Returns None on EOF.
pub extern "Lua-C" fn try_read(&self, len: u32) -> Result<Option<Vec<u8>>> { pub extern "Lua-C" fn try_read(&self, len: u32) -> Result<Option<Vec<u8>>> {
let mut buf = vec![0u8; len as usize]; let mut buf = vec![0; len as usize];
match self.stream_mut().try_read(&mut buf) { match self.read_half()?.try_read(&mut buf) {
Ok(0) => Ok(None), Ok(0) => Ok(None),
Ok(n) => { Ok(n) => {
buf.truncate(n); buf.truncate(n);
@ -808,10 +788,23 @@ impl lb_tcpstream {
} }
} }
/// Peeks up to `len` bytes at incoming data without consuming it. Returns None on EOF. /// Writes the given bytes to this stream.
pub async extern "Lua-C" fn write(&self, buf: &[u8]) -> Result<()> {
Ok(self.write_half()?.write_all(buf).await?)
}
/// Writes the given bytes to this stream and returns the number of bytes successfully written.
pub async extern "Lua-C" fn write_partial(&self, buf: &[u8]) -> Result<u32> {
Ok(self.write_half()?.write(buf).await? as u32)
}
/// Peeks up to `len` bytes at incoming data without consuming it.
///
/// Successive calls will return the same data until it is consumed by the [`read`](Self::read)
/// family of functions.
pub async extern "Lua-C" fn peek(&self, len: u32) -> Result<Option<Vec<u8>>> { pub async extern "Lua-C" fn peek(&self, len: u32) -> Result<Option<Vec<u8>>> {
let mut buf = vec![0u8; len as usize]; let mut buf = vec![0; len as usize];
let n = self.stream_mut().peek(&mut buf).await?; let n = self.read_half()?.peek(&mut buf).await?;
if n == 0 { if n == 0 {
Ok(None) Ok(None)
} else { } else {
@ -822,11 +815,11 @@ impl lb_tcpstream {
/// Shuts down this connection. /// Shuts down this connection.
pub async extern "Lua-C" fn shutdown(&self) -> Result<()> { pub async extern "Lua-C" fn shutdown(&self) -> Result<()> {
Ok(self.stream_mut().shutdown().await?) Ok(self.write_half()?.shutdown().await?)
} }
#[call] #[call]
pub async extern "Lua" fn __call(&self, len: u32) -> Result<Option<Vec<u8>>> { pub async extern "Lua" fn call(&self, len: u32) -> Result<Option<Vec<u8>>> {
self.read_partial(len) self.read_partial(len)
} }
} }

View File

@ -13,7 +13,7 @@ use luaffi::{
metatype, metatype,
}; };
use luajit::{LUA_MULTRET, Type}; use luajit::{LUA_MULTRET, Type};
use std::{ffi::c_int, time::Duration}; use std::{cell::RefCell, ffi::c_int, time::Duration};
use tokio::{task::JoinHandle, time::sleep}; use tokio::{task::JoinHandle, time::sleep};
/// Items exported by the `lb:task` library. /// Items exported by the `lb:task` library.
@ -45,6 +45,10 @@ impl lb_tasklib {
// this table is used from rust-side to call the function with its args, and it's also // this table is used from rust-side to call the function with its args, and it's also
// reused to store its return values that the task handle can return when awaited. the ref // reused to store its return values that the task handle can return when awaited. the ref
// is owned by the task handle and unref'ed when it's gc'ed. // is owned by the task handle and unref'ed when it's gc'ed.
assert(
r#type(f) == "function",
concat!("function expected in argument 'f', got ", r#type(f)),
);
Self::__spawn(__ref(__tpack(f, variadic!()))) Self::__spawn(__ref(__tpack(f, variadic!())))
} }
@ -69,10 +73,7 @@ impl lb_tasklib {
let _ = arg.into_raw(); // the original ref is owned by the task handle and unref'ed there let _ = arg.into_raw(); // the original ref is owned by the task handle and unref'ed there
}); });
lb_task { lb_task::new(handle, key)
handle: Some(handle),
__ref: key,
}
} }
} }
@ -80,20 +81,27 @@ impl lb_tasklib {
#[cdef] #[cdef]
pub struct lb_task { pub struct lb_task {
#[opaque] #[opaque]
handle: Option<JoinHandle<()>>, handle: RefCell<Option<JoinHandle<()>>>,
__ref: c_int, __ref: c_int,
} }
#[metatype] #[metatype]
impl lb_task { impl lb_task {
fn new(handle: JoinHandle<()>, ref_key: c_int) -> Self {
lb_task {
handle: RefCell::new(Some(handle)),
__ref: ref_key,
}
}
pub async extern "Lua" fn r#await(&self) -> many { pub async extern "Lua" fn r#await(&self) -> many {
self.__await(); self.__await();
let ret = __registry[self.__ref]; let ret = __registry[self.__ref];
__tunpack(ret, 1, ret.n) __tunpack(ret, 1, ret.n)
} }
async extern "Lua-C" fn __await(&mut self) { async extern "Lua-C" fn __await(&self) {
if let Some(handle) = self.handle.take() { if let Some(handle) = self.handle.borrow_mut().take() {
handle handle
.await .await
.unwrap_or_else(|err| panic!("task handler panicked: {err}")); .unwrap_or_else(|err| panic!("task handler panicked: {err}"));