summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlex Orlenko <zxteam@protonmail.com>2022-04-14 20:48:00 +0100
committerAlex Orlenko <zxteam@protonmail.com>2022-04-14 20:48:00 +0100
commitd3975bdf304969b16597fa9407960c718784ebaa (patch)
treeb6f3e4da99db12127fcdfc3127731d57bb2c14ac /src
parent21affdadfd69428883dfeca28f457e02bad0a162 (diff)
downloadmlua-d3975bdf304969b16597fa9407960c718784ebaa.zip
Refactor `AsChunk` trait.
Remove blanket implementation for T: AsRef<[u8]> Implement for `std::path::Path`
Diffstat (limited to 'src')
-rw-r--r--src/chunk.rs167
-rw-r--r--src/lua.rs27
2 files changed, 116 insertions, 78 deletions
diff --git a/src/chunk.rs b/src/chunk.rs
index 44076e1..050475d 100644
--- a/src/chunk.rs
+++ b/src/chunk.rs
@@ -1,5 +1,8 @@
use std::borrow::Cow;
use std::ffi::CString;
+use std::io::Result as IoResult;
+use std::path::{Path, PathBuf};
+use std::string::String as StdString;
use crate::error::{Error, Result};
use crate::ffi;
@@ -16,10 +19,10 @@ use {futures_core::future::LocalBoxFuture, futures_util::future};
/// [`Chunk`]: crate::Chunk
pub trait AsChunk<'lua> {
/// Returns chunk data (can be text or binary)
- fn source(&self) -> &[u8];
+ fn source(&self) -> IoResult<Cow<[u8]>>;
/// Returns optional chunk name
- fn name(&self) -> Option<CString> {
+ fn name(&self) -> Option<StdString> {
None
}
@@ -36,9 +39,47 @@ pub trait AsChunk<'lua> {
}
}
-impl<'lua, T: AsRef<[u8]> + ?Sized> AsChunk<'lua> for T {
- fn source(&self) -> &[u8] {
- self.as_ref()
+impl<'lua> AsChunk<'lua> for str {
+ fn source(&self) -> IoResult<Cow<[u8]>> {
+ Ok(Cow::Borrowed(self.as_ref()))
+ }
+}
+
+impl<'lua> AsChunk<'lua> for StdString {
+ fn source(&self) -> IoResult<Cow<[u8]>> {
+ Ok(Cow::Borrowed(self.as_ref()))
+ }
+}
+
+impl<'lua> AsChunk<'lua> for [u8] {
+ fn source(&self) -> IoResult<Cow<[u8]>> {
+ Ok(Cow::Borrowed(self))
+ }
+}
+
+impl<'lua> AsChunk<'lua> for Vec<u8> {
+ fn source(&self) -> IoResult<Cow<[u8]>> {
+ Ok(Cow::Borrowed(self))
+ }
+}
+
+impl<'lua> AsChunk<'lua> for Path {
+ fn source(&self) -> IoResult<Cow<[u8]>> {
+ std::fs::read(self).map(Cow::Owned)
+ }
+
+ fn name(&self) -> Option<StdString> {
+ Some(format!("@{}", self.display()))
+ }
+}
+
+impl<'lua> AsChunk<'lua> for PathBuf {
+ fn source(&self) -> IoResult<Cow<[u8]>> {
+ std::fs::read(self).map(Cow::Owned)
+ }
+
+ fn name(&self) -> Option<StdString> {
+ Some(format!("@{}", self.display()))
}
}
@@ -48,8 +89,8 @@ impl<'lua, T: AsRef<[u8]> + ?Sized> AsChunk<'lua> for T {
#[must_use = "`Chunk`s do nothing unless one of `exec`, `eval`, `call`, or `into_function` are called on them"]
pub struct Chunk<'lua, 'a> {
pub(crate) lua: &'lua Lua,
- pub(crate) source: Cow<'a, [u8]>,
- pub(crate) name: Option<CString>,
+ pub(crate) source: IoResult<Cow<'a, [u8]>>,
+ pub(crate) name: Option<StdString>,
pub(crate) env: Result<Option<Value<'lua>>>,
pub(crate) mode: Option<ChunkMode>,
#[cfg(feature = "luau")]
@@ -174,7 +215,7 @@ impl Compiler {
.map(|s| s.as_ptr())
.collect::<Vec<_>>();
let mut mutable_globals_ptr = ptr::null_mut();
- if mutable_globals.len() > 0 {
+ if !mutable_globals.is_empty() {
mutable_globals.push(ptr::null());
mutable_globals_ptr = mutable_globals.as_mut_ptr();
}
@@ -195,14 +236,10 @@ impl Compiler {
impl<'lua, 'a> Chunk<'lua, 'a> {
/// Sets the name of this chunk, which results in more informative error traces.
- pub fn set_name<S: AsRef<[u8]> + ?Sized>(mut self, name: &S) -> Result<Self> {
- let name =
- CString::new(name.as_ref().to_vec()).map_err(|e| Error::ToLuaConversionError {
- from: "&str",
- to: "string",
- message: Some(e.to_string()),
- })?;
- self.name = Some(name);
+ pub fn set_name(mut self, name: impl AsRef<str>) -> Result<Self> {
+ self.name = Some(name.as_ref().to_string());
+ // Do extra validation
+ let _ = self.convert_name()?;
Ok(self)
}
@@ -244,23 +281,6 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
self
}
- /// Compiles the chunk and changes mode to binary.
- ///
- /// It does nothing if the chunk is already binary.
- #[cfg(feature = "luau")]
- #[doc(hidden)]
- pub fn compile(mut self) -> Self {
- if self.detect_mode() == ChunkMode::Text {
- let data = self
- .compiler
- .get_or_insert_with(Default::default)
- .compile(self.source.as_ref());
- self.mode = Some(ChunkMode::Binary);
- self.source = Cow::Owned(data);
- }
- self
- }
-
/// Execute this chunk of code.
///
/// This is equivalent to calling the chunk function with no arguments and no return values.
@@ -358,38 +378,41 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
/// Load this chunk into a regular `Function`.
///
/// This simply compiles the chunk without actually executing it.
- pub fn into_function(self) -> Result<Function<'lua>> {
- #[cfg(not(feature = "luau"))]
- let self_ = self;
+ #[cfg_attr(not(feature = "luau"), allow(unused_mut))]
+ pub fn into_function(mut self) -> Result<Function<'lua>> {
#[cfg(feature = "luau")]
- let self_ = match self.compiler {
- // We don't need to compile source if no compiler options set
- Some(_) => self.compile(),
- _ => self,
- };
-
- self_.lua.load_chunk(
- self_.source.as_ref(),
- self_.name.as_ref(),
- self_.env()?,
- self_.mode,
- )
- }
+ if self.compiler.is_some() {
+ // We don't need to compile source if no compiler set
+ self.compile();
+ }
- fn env(&self) -> Result<Option<Value<'lua>>> {
- self.env.clone()
+ let name = self.convert_name()?;
+ self.lua
+ .load_chunk(self.source?.as_ref(), name.as_deref(), self.env?, self.mode)
}
- fn expression_source(&self) -> Vec<u8> {
- let mut buf = Vec::with_capacity(b"return ".len() + self.source.len());
- buf.extend(b"return ");
- buf.extend(self.source.as_ref());
- buf
+ /// Compiles the chunk and changes mode to binary.
+ ///
+ /// It does nothing if the chunk is already binary.
+ #[cfg(feature = "luau")]
+ fn compile(&mut self) {
+ if let Ok(ref source) = self.source {
+ if self.detect_mode() == ChunkMode::Text {
+ let data = self
+ .compiler
+ .get_or_insert_with(Default::default)
+ .compile(source);
+ self.mode = Some(ChunkMode::Binary);
+ self.source = Ok(Cow::Owned(data));
+ }
+ }
}
fn to_expression(&self) -> Result<Function<'lua>> {
// We assume that mode is Text
- let source = self.expression_source();
+ let source = self.source.as_ref();
+ let source = source.map_err(|err| Error::RuntimeError(err.to_string()))?;
+ let source = Self::expression_source(source);
// We don't need to compile source if no compiler options set
#[cfg(feature = "luau")]
let source = self
@@ -398,24 +421,42 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
.map(|c| c.compile(&source))
.unwrap_or(source);
+ let name = self.convert_name()?;
self.lua
- .load_chunk(&source, self.name.as_ref(), self.env()?, None)
+ .load_chunk(&source, name.as_deref(), self.env.clone()?, None)
}
fn detect_mode(&self) -> ChunkMode {
- match self.mode {
- Some(mode) => mode,
- None => {
+ match (self.mode, &self.source) {
+ (Some(mode), _) => mode,
+ (None, Ok(source)) if source.len() == 0 => ChunkMode::Text,
+ (None, Ok(source)) => {
#[cfg(not(feature = "luau"))]
- if self.source.starts_with(ffi::LUA_SIGNATURE) {
+ if source.starts_with(ffi::LUA_SIGNATURE) {
return ChunkMode::Binary;
}
#[cfg(feature = "luau")]
- if self.source[0] < b'\n' {
+ if source[0] < b'\n' {
return ChunkMode::Binary;
}
ChunkMode::Text
}
+ (None, Err(_)) => ChunkMode::Text, // any value is fine
}
}
+
+ fn convert_name(&self) -> Result<Option<CString>> {
+ self.name
+ .clone()
+ .map(CString::new)
+ .transpose()
+ .map_err(|err| Error::RuntimeError(format!("invalid name: {err}")))
+ }
+
+ fn expression_source(source: &[u8]) -> Vec<u8> {
+ let mut buf = Vec::with_capacity(b"return ".len() + source.len());
+ buf.extend(b"return ");
+ buf.extend(source);
+ buf
+ }
}
diff --git a/src/lua.rs b/src/lua.rs
index be7b907..773281a 100644
--- a/src/lua.rs
+++ b/src/lua.rs
@@ -1,8 +1,7 @@
use std::any::{Any, TypeId};
-use std::borrow::Cow;
use std::cell::{Ref, RefCell, RefMut, UnsafeCell};
use std::collections::HashMap;
-use std::ffi::CString;
+use std::ffi::{CStr, CString};
use std::fmt;
use std::marker::PhantomData;
use std::mem::ManuallyDrop;
@@ -41,10 +40,7 @@ use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Va
#[cfg(not(feature = "lua54"))]
use crate::util::push_userdata;
#[cfg(feature = "lua54")]
-use {
- crate::{types::WarnCallback, userdata::USER_VALUE_MAXSLOT, util::push_userdata_uv},
- std::ffi::CStr,
-};
+use crate::{types::WarnCallback, userdata::USER_VALUE_MAXSLOT, util::push_userdata_uv};
#[cfg(not(feature = "luau"))]
use crate::{hook::HookTriggers, types::HookCallback};
@@ -1359,19 +1355,20 @@ impl Lua {
///
/// [`Chunk::exec`]: crate::Chunk::exec
#[track_caller]
- pub fn load<'lua, 'a, S>(&'lua self, source: &'a S) -> Chunk<'lua, 'a>
+ pub fn load<'lua, 'a, S>(&'lua self, chunk: &'a S) -> Chunk<'lua, 'a>
where
S: AsChunk<'lua> + ?Sized,
{
+ let name = chunk
+ .name()
+ .unwrap_or_else(|| Location::caller().to_string());
+
Chunk {
lua: self,
- source: Cow::Borrowed(source.source()),
- name: match source.name() {
- Some(name) => Some(name),
- None => CString::new(Location::caller().to_string()).ok(),
- },
- env: source.env(self),
- mode: source.mode(),
+ source: chunk.source(),
+ name: Some(name),
+ env: chunk.env(self),
+ mode: chunk.mode(),
#[cfg(feature = "luau")]
compiler: self.compiler.clone(),
}
@@ -1380,7 +1377,7 @@ impl Lua {
pub(crate) fn load_chunk<'lua>(
&'lua self,
source: &[u8],
- name: Option<&CString>,
+ name: Option<&CStr>,
env: Option<Value<'lua>>,
mode: Option<ChunkMode>,
) -> Result<Function<'lua>> {