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
//!
//! See [`lb_fslib`] for items exported by this library.
use derive_more::From;
use luaffi::{cdef, metatype};
use std::{path::PathBuf, time::SystemTime};
use std::{
cell::{BorrowError, BorrowMutError, RefCell},
path::PathBuf,
time::SystemTime,
};
use thiserror::Error;
/// 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).
#[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),
@ -54,38 +63,85 @@ impl lb_fslib {
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>> {
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>> {
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<()> {
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<()> {
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> {
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> {
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 {
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)
}
/// 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();
@ -93,76 +149,128 @@ impl lb_fslib {
.add(globset::Glob::new(pattern)?)
.build()?;
Ok(lb_glob_dir {
iter,
matcher,
prefix,
})
Ok(lb_glob_dir::new(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> {
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> {
Ok(tempfile::tempdir_in(path)?.into())
Ok(lb_temp_dir::new(tempfile::tempdir_in(path)?))
}
}
/// Iterator over the entries in a directory.
#[derive(Debug, From)]
#[derive(Debug)]
#[cdef]
pub struct lb_read_dir(#[opaque] tokio::fs::ReadDir);
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 cannot be read.
#[call]
pub async extern "Lua-C" fn next(&mut self) -> Result<Option<lb_dir_entry>> {
Ok(self.0.next_entry().await?.map(Into::into))
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, From)]
#[derive(Debug)]
#[cdef]
pub struct lb_read_dir_sync(#[opaque] std::fs::ReadDir);
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 cannot be read.
#[call]
pub extern "Lua-C" fn next(&mut self) -> Result<Option<lb_dir_entry_sync>> {
Ok(self.0.next().transpose()?.map(Into::into))
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, From)]
#[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 cannot be determined.
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> {
Ok(self.0.metadata().await?.into())
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()
@ -170,34 +278,52 @@ impl lb_dir_entry {
}
/// Synchronous version of [`lb_dir_entry`].
#[derive(Debug, From)]
#[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 cannot be determined.
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> {
Ok(self.0.metadata()?.into())
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()
@ -205,24 +331,33 @@ impl lb_dir_entry_sync {
}
/// Structure representing the type of a file with accessors for each file type.
#[derive(Debug, From)]
#[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() {
@ -239,36 +374,51 @@ impl lb_file_type {
}
/// Metadata information about a file.
#[derive(Debug, From)]
#[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 {
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 {
self.0.len()
}
/// Returns the permissions of this file.
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> {
Ok(self
.0
@ -278,6 +428,11 @@ impl lb_file_meta {
.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> {
Ok(self
.0
@ -287,6 +442,11 @@ impl lb_file_meta {
.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> {
Ok(self
.0
@ -296,6 +456,7 @@ impl lb_file_meta {
.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();
@ -312,71 +473,107 @@ impl lb_file_meta {
}
/// Representation of the various permissions on a file.
#[derive(Debug, From)]
#[derive(Debug)]
#[cdef]
pub struct lb_file_perms(#[opaque] std::fs::Permissions);
pub struct lb_file_perms(#[opaque] RefCell<std::fs::Permissions>);
#[metatype]
impl lb_file_perms {
pub extern "Lua-C" fn readonly(&self) -> bool {
self.0.readonly()
fn new(perms: std::fs::Permissions) -> Self {
Self(RefCell::new(perms))
}
pub extern "Lua-C" fn set_readonly(&mut self, readonly: bool) {
self.0.set_readonly(readonly);
/// 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, From)]
#[derive(Debug)]
#[cdef]
pub struct lb_walk_dir(#[opaque] walkdir::IntoIter);
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 cannot be read.
#[call]
pub extern "Lua-C" fn next(&mut self) -> Result<Option<lb_walk_dir_entry>> {
Ok(self.0.next().transpose()?.map(Into::into))
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, From)]
#[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 {
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> {
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 {
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()
@ -388,7 +585,7 @@ impl lb_walk_dir_entry {
#[cdef]
pub struct lb_glob_dir {
#[opaque]
iter: walkdir::IntoIter,
iter: RefCell<walkdir::IntoIter>,
#[opaque]
matcher: globset::GlobSet,
#[opaque]
@ -397,13 +594,26 @@ pub struct lb_glob_dir {
#[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 cannot be read.
#[call]
pub extern "Lua-C" fn next(&mut self) -> Result<Option<lb_walk_dir_entry>> {
while let Some(res) = self.iter.next() {
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(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.
#[derive(Debug, From)]
#[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()

View File

@ -9,7 +9,7 @@
use derive_more::{From, FromStr};
use luaffi::{cdef, marker::OneOf, metatype};
use std::{
cell::{Ref, RefCell, RefMut},
cell::{BorrowError, BorrowMutError, Ref, RefCell, RefMut},
net::{AddrParseError, IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
time::Duration,
};
@ -25,13 +25,19 @@ use tokio::{
/// 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),
/// IP or socket address syntax error.
#[error("{0}")]
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")]
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
/// [`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`.
///
@ -303,7 +309,7 @@ impl lb_ipaddr {
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 {
if self.is_v6() { "v6" } else { "v4" }
}
@ -503,10 +509,12 @@ impl lb_socketaddr {
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 {
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 {
SocketAddr::new(self.ip().0, port).into()
}
@ -547,7 +555,7 @@ impl lb_tcpsocket {
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();
match *socket {
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.
///
/// 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<()> {
Ok(self.socket()?.set_nodelay(enabled)?)
}
@ -682,71 +690,45 @@ impl lb_tcpsocket {
/// TCP connection between a local and a remote socket.
#[derive(Debug)]
#[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]
impl lb_tcpstream {
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> {
self.0.borrow()
fn read_half<'s>(&'s self) -> Result<RefMut<'s, tokio::net::tcp::OwnedReadHalf>> {
Ok(self.read.try_borrow_mut()?)
}
fn stream_mut(&self) -> RefMut<TcpStream> {
self.0.borrow_mut()
fn write_half<'s>(&'s self) -> Result<RefMut<'s, tokio::net::tcp::OwnedWriteHalf>> {
Ok(self.write.try_borrow_mut()?)
}
/// Gets the remote address of this stream.
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.
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.
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.
/// Waits for this stream to be ready in the given half.
///
/// This enables or disables Nagle's algorithm which delays sending small packets.
pub extern "Lua-C" fn set_nodelay(&self, enabled: bool) -> Result<()> {
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).
/// `half` can be `"read"` for the readable half, `"write"` for the writable half, or `nil` for
/// both.
pub async extern "Lua-C" fn ready(&self, half: Option<&str>) -> Result<()> {
let ty = match half {
None => Interest::READABLE | Interest::WRITABLE,
@ -758,29 +740,29 @@ impl lb_tcpstream {
))?,
};
self.stream().ready(ty).await?;
self.read_half()?.ready(ty).await?;
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>>> {
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)),
Err(err) if err.kind() == std::io::ErrorKind::UnexpectedEof => Ok(None),
Err(err) => Err(err.into()),
}
}
/// Writes the given bytes to the stream.
pub async extern "Lua-C" fn write(&self, buf: &[u8]) -> Result<()> {
Ok(self.stream_mut().write_all(buf).await?)
}
/// Reads up to `len` bytes from the stream. Returns None on EOF.
/// Reads up to `len` bytes from this stream.
///
/// 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`.
pub async extern "Lua-C" fn read_partial(&self, len: u32) -> Result<Option<Vec<u8>>> {
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 {
Ok(None)
} else {
@ -789,15 +771,13 @@ impl lb_tcpstream {
}
}
/// Writes the given bytes to the stream and returns the number of bytes successfully written.
pub async extern "Lua-C" fn write_partial(&self, buf: &[u8]) -> Result<u32> {
Ok(self.stream_mut().write(buf).await? as u32)
}
/// Attempts to read up to `len` bytes from the stream without waiting. Returns None on EOF.
/// Attempts to read up to `len` bytes from this stream without waiting.
///
/// 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`.
pub extern "Lua-C" fn try_read(&self, len: u32) -> Result<Option<Vec<u8>>> {
let mut buf = vec![0u8; len as usize];
match self.stream_mut().try_read(&mut buf) {
let mut buf = vec![0; len as usize];
match self.read_half()?.try_read(&mut buf) {
Ok(0) => Ok(None),
Ok(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>>> {
let mut buf = vec![0u8; len as usize];
let n = self.stream_mut().peek(&mut buf).await?;
let mut buf = vec![0; len as usize];
let n = self.read_half()?.peek(&mut buf).await?;
if n == 0 {
Ok(None)
} else {
@ -822,11 +815,11 @@ impl lb_tcpstream {
/// Shuts down this connection.
pub async extern "Lua-C" fn shutdown(&self) -> Result<()> {
Ok(self.stream_mut().shutdown().await?)
Ok(self.write_half()?.shutdown().await?)
}
#[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)
}
}

View File

@ -13,7 +13,7 @@ use luaffi::{
metatype,
};
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};
/// 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
// 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.
assert(
r#type(f) == "function",
concat!("function expected in argument 'f', got ", r#type(f)),
);
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
});
lb_task {
handle: Some(handle),
__ref: key,
}
lb_task::new(handle, key)
}
}
@ -80,20 +81,27 @@ impl lb_tasklib {
#[cdef]
pub struct lb_task {
#[opaque]
handle: Option<JoinHandle<()>>,
handle: RefCell<Option<JoinHandle<()>>>,
__ref: c_int,
}
#[metatype]
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 {
self.__await();
let ret = __registry[self.__ref];
__tunpack(ret, 1, ret.n)
}
async extern "Lua-C" fn __await(&mut self) {
if let Some(handle) = self.handle.take() {
async extern "Lua-C" fn __await(&self) {
if let Some(handle) = self.handle.borrow_mut().take() {
handle
.await
.unwrap_or_else(|err| panic!("task handler panicked: {err}"));