diff --git a/crates/lb/src/fs.rs b/crates/lb/src/fs.rs index 21eb60b..a100c99 100644 --- a/crates/lb/src/fs.rs +++ b/crates/lb/src/fs.rs @@ -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> { 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> { 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 { - 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 { - 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 { + /// 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 { 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 { 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 { - 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 { - 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); #[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> { - Ok(self.0.next_entry().await?.map(Into::into)) + pub async extern "Lua-C" fn next(&self) -> Result> { + 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); #[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> { - Ok(self.0.next().transpose()?.map(Into::into)) + pub extern "Lua-C" fn next(&self) -> Result> { + 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 { - 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 { - 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 { - 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 { - 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 { 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 { 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 { 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); #[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); #[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> { - Ok(self.0.next().transpose()?.map(Into::into)) + pub extern "Lua-C" fn next(&self) -> Result> { + 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 { - 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, #[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> { - while let Some(res) = self.iter.next() { + pub extern "Lua-C" fn next(&self) -> Result> { + 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() diff --git a/crates/lb/src/net.rs b/crates/lb/src/net.rs index 8c40c6b..f19246d 100644 --- a/crates/lb/src/net.rs +++ b/crates/lb/src/net.rs @@ -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> { + fn socket<'s>(&'s self) -> Result> { 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); +pub struct lb_tcpstream { + #[opaque] + read: RefCell, + #[opaque] + write: RefCell, +} #[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 { - self.0.borrow() + fn read_half<'s>(&'s self) -> Result> { + Ok(self.read.try_borrow_mut()?) } - fn stream_mut(&self) -> RefMut { - self.0.borrow_mut() + fn write_half<'s>(&'s self) -> Result> { + Ok(self.write.try_borrow_mut()?) } /// Gets the remote address of this stream. pub extern "Lua-C" fn peer_addr(&self) -> Result { - 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 { - 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 { - 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 { - 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 { - 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>> { 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>> { 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 { - 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>> { - 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 { + 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>> { - 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>> { + pub async extern "Lua" fn call(&self, len: u32) -> Result>> { self.read_partial(len) } } diff --git a/crates/lb/src/task.rs b/crates/lb/src/task.rs index 3e5cc85..4909d1c 100644 --- a/crates/lb/src/task.rs +++ b/crates/lb/src/task.rs @@ -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>, + handle: RefCell>>, __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}"));