Refactor `AsChunk` trait.
Remove blanket implementation for T: AsRef<[u8]> Implement for `std::path::Path`
This commit is contained in:
parent
21affdadfd
commit
d3975bdf30
|
@ -62,6 +62,8 @@ pub fn chunk(input: TokenStream) -> TokenStream {
|
||||||
|
|
||||||
let wrapped_code = quote! {{
|
let wrapped_code = quote! {{
|
||||||
use ::mlua::{AsChunk, ChunkMode, Lua, Result, Value};
|
use ::mlua::{AsChunk, ChunkMode, Lua, Result, Value};
|
||||||
|
use ::std::borrow::Cow;
|
||||||
|
use ::std::io::Result as IoResult;
|
||||||
use ::std::marker::PhantomData;
|
use ::std::marker::PhantomData;
|
||||||
use ::std::sync::Mutex;
|
use ::std::sync::Mutex;
|
||||||
|
|
||||||
|
@ -73,8 +75,8 @@ pub fn chunk(input: TokenStream) -> TokenStream {
|
||||||
where
|
where
|
||||||
F: FnOnce(&'lua Lua) -> Result<Value<'lua>>,
|
F: FnOnce(&'lua Lua) -> Result<Value<'lua>>,
|
||||||
{
|
{
|
||||||
fn source(&self) -> &[u8] {
|
fn source(&self) -> IoResult<Cow<[u8]>> {
|
||||||
(#source).as_bytes()
|
Ok(Cow::Borrowed((#source).as_bytes()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn env(&self, lua: &'lua Lua) -> Result<Option<Value<'lua>>> {
|
fn env(&self, lua: &'lua Lua) -> Result<Option<Value<'lua>>> {
|
||||||
|
|
165
src/chunk.rs
165
src/chunk.rs
|
@ -1,5 +1,8 @@
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::ffi::CString;
|
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::error::{Error, Result};
|
||||||
use crate::ffi;
|
use crate::ffi;
|
||||||
|
@ -16,10 +19,10 @@ use {futures_core::future::LocalBoxFuture, futures_util::future};
|
||||||
/// [`Chunk`]: crate::Chunk
|
/// [`Chunk`]: crate::Chunk
|
||||||
pub trait AsChunk<'lua> {
|
pub trait AsChunk<'lua> {
|
||||||
/// Returns chunk data (can be text or binary)
|
/// Returns chunk data (can be text or binary)
|
||||||
fn source(&self) -> &[u8];
|
fn source(&self) -> IoResult<Cow<[u8]>>;
|
||||||
|
|
||||||
/// Returns optional chunk name
|
/// Returns optional chunk name
|
||||||
fn name(&self) -> Option<CString> {
|
fn name(&self) -> Option<StdString> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,9 +39,47 @@ pub trait AsChunk<'lua> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua, T: AsRef<[u8]> + ?Sized> AsChunk<'lua> for T {
|
impl<'lua> AsChunk<'lua> for str {
|
||||||
fn source(&self) -> &[u8] {
|
fn source(&self) -> IoResult<Cow<[u8]>> {
|
||||||
self.as_ref()
|
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"]
|
#[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 struct Chunk<'lua, 'a> {
|
||||||
pub(crate) lua: &'lua Lua,
|
pub(crate) lua: &'lua Lua,
|
||||||
pub(crate) source: Cow<'a, [u8]>,
|
pub(crate) source: IoResult<Cow<'a, [u8]>>,
|
||||||
pub(crate) name: Option<CString>,
|
pub(crate) name: Option<StdString>,
|
||||||
pub(crate) env: Result<Option<Value<'lua>>>,
|
pub(crate) env: Result<Option<Value<'lua>>>,
|
||||||
pub(crate) mode: Option<ChunkMode>,
|
pub(crate) mode: Option<ChunkMode>,
|
||||||
#[cfg(feature = "luau")]
|
#[cfg(feature = "luau")]
|
||||||
|
@ -174,7 +215,7 @@ impl Compiler {
|
||||||
.map(|s| s.as_ptr())
|
.map(|s| s.as_ptr())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let mut mutable_globals_ptr = ptr::null_mut();
|
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.push(ptr::null());
|
||||||
mutable_globals_ptr = mutable_globals.as_mut_ptr();
|
mutable_globals_ptr = mutable_globals.as_mut_ptr();
|
||||||
}
|
}
|
||||||
|
@ -195,14 +236,10 @@ impl Compiler {
|
||||||
|
|
||||||
impl<'lua, 'a> Chunk<'lua, 'a> {
|
impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||||
/// Sets the name of this chunk, which results in more informative error traces.
|
/// 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> {
|
pub fn set_name(mut self, name: impl AsRef<str>) -> Result<Self> {
|
||||||
let name =
|
self.name = Some(name.as_ref().to_string());
|
||||||
CString::new(name.as_ref().to_vec()).map_err(|e| Error::ToLuaConversionError {
|
// Do extra validation
|
||||||
from: "&str",
|
let _ = self.convert_name()?;
|
||||||
to: "string",
|
|
||||||
message: Some(e.to_string()),
|
|
||||||
})?;
|
|
||||||
self.name = Some(name);
|
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,23 +281,6 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||||
self
|
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.
|
/// Execute this chunk of code.
|
||||||
///
|
///
|
||||||
/// This is equivalent to calling the chunk function with no arguments and no return values.
|
/// 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`.
|
/// Load this chunk into a regular `Function`.
|
||||||
///
|
///
|
||||||
/// This simply compiles the chunk without actually executing it.
|
/// This simply compiles the chunk without actually executing it.
|
||||||
pub fn into_function(self) -> Result<Function<'lua>> {
|
#[cfg_attr(not(feature = "luau"), allow(unused_mut))]
|
||||||
#[cfg(not(feature = "luau"))]
|
pub fn into_function(mut self) -> Result<Function<'lua>> {
|
||||||
let self_ = self;
|
|
||||||
#[cfg(feature = "luau")]
|
#[cfg(feature = "luau")]
|
||||||
let self_ = match self.compiler {
|
if self.compiler.is_some() {
|
||||||
// We don't need to compile source if no compiler options set
|
// We don't need to compile source if no compiler set
|
||||||
Some(_) => self.compile(),
|
self.compile();
|
||||||
_ => self,
|
|
||||||
};
|
|
||||||
|
|
||||||
self_.lua.load_chunk(
|
|
||||||
self_.source.as_ref(),
|
|
||||||
self_.name.as_ref(),
|
|
||||||
self_.env()?,
|
|
||||||
self_.mode,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn env(&self) -> Result<Option<Value<'lua>>> {
|
let name = self.convert_name()?;
|
||||||
self.env.clone()
|
self.lua
|
||||||
|
.load_chunk(self.source?.as_ref(), name.as_deref(), self.env?, self.mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expression_source(&self) -> Vec<u8> {
|
/// Compiles the chunk and changes mode to binary.
|
||||||
let mut buf = Vec::with_capacity(b"return ".len() + self.source.len());
|
///
|
||||||
buf.extend(b"return ");
|
/// It does nothing if the chunk is already binary.
|
||||||
buf.extend(self.source.as_ref());
|
#[cfg(feature = "luau")]
|
||||||
buf
|
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>> {
|
fn to_expression(&self) -> Result<Function<'lua>> {
|
||||||
// We assume that mode is Text
|
// 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
|
// We don't need to compile source if no compiler options set
|
||||||
#[cfg(feature = "luau")]
|
#[cfg(feature = "luau")]
|
||||||
let source = self
|
let source = self
|
||||||
|
@ -398,24 +421,42 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||||
.map(|c| c.compile(&source))
|
.map(|c| c.compile(&source))
|
||||||
.unwrap_or(source);
|
.unwrap_or(source);
|
||||||
|
|
||||||
|
let name = self.convert_name()?;
|
||||||
self.lua
|
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 {
|
fn detect_mode(&self) -> ChunkMode {
|
||||||
match self.mode {
|
match (self.mode, &self.source) {
|
||||||
Some(mode) => mode,
|
(Some(mode), _) => mode,
|
||||||
None => {
|
(None, Ok(source)) if source.len() == 0 => ChunkMode::Text,
|
||||||
|
(None, Ok(source)) => {
|
||||||
#[cfg(not(feature = "luau"))]
|
#[cfg(not(feature = "luau"))]
|
||||||
if self.source.starts_with(ffi::LUA_SIGNATURE) {
|
if source.starts_with(ffi::LUA_SIGNATURE) {
|
||||||
return ChunkMode::Binary;
|
return ChunkMode::Binary;
|
||||||
}
|
}
|
||||||
#[cfg(feature = "luau")]
|
#[cfg(feature = "luau")]
|
||||||
if self.source[0] < b'\n' {
|
if source[0] < b'\n' {
|
||||||
return ChunkMode::Binary;
|
return ChunkMode::Binary;
|
||||||
}
|
}
|
||||||
ChunkMode::Text
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
27
src/lua.rs
27
src/lua.rs
|
@ -1,8 +1,7 @@
|
||||||
use std::any::{Any, TypeId};
|
use std::any::{Any, TypeId};
|
||||||
use std::borrow::Cow;
|
|
||||||
use std::cell::{Ref, RefCell, RefMut, UnsafeCell};
|
use std::cell::{Ref, RefCell, RefMut, UnsafeCell};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::ffi::CString;
|
use std::ffi::{CStr, CString};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::mem::ManuallyDrop;
|
use std::mem::ManuallyDrop;
|
||||||
|
@ -41,10 +40,7 @@ use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Va
|
||||||
#[cfg(not(feature = "lua54"))]
|
#[cfg(not(feature = "lua54"))]
|
||||||
use crate::util::push_userdata;
|
use crate::util::push_userdata;
|
||||||
#[cfg(feature = "lua54")]
|
#[cfg(feature = "lua54")]
|
||||||
use {
|
use crate::{types::WarnCallback, userdata::USER_VALUE_MAXSLOT, util::push_userdata_uv};
|
||||||
crate::{types::WarnCallback, userdata::USER_VALUE_MAXSLOT, util::push_userdata_uv},
|
|
||||||
std::ffi::CStr,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(not(feature = "luau"))]
|
#[cfg(not(feature = "luau"))]
|
||||||
use crate::{hook::HookTriggers, types::HookCallback};
|
use crate::{hook::HookTriggers, types::HookCallback};
|
||||||
|
@ -1359,19 +1355,20 @@ impl Lua {
|
||||||
///
|
///
|
||||||
/// [`Chunk::exec`]: crate::Chunk::exec
|
/// [`Chunk::exec`]: crate::Chunk::exec
|
||||||
#[track_caller]
|
#[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
|
where
|
||||||
S: AsChunk<'lua> + ?Sized,
|
S: AsChunk<'lua> + ?Sized,
|
||||||
{
|
{
|
||||||
|
let name = chunk
|
||||||
|
.name()
|
||||||
|
.unwrap_or_else(|| Location::caller().to_string());
|
||||||
|
|
||||||
Chunk {
|
Chunk {
|
||||||
lua: self,
|
lua: self,
|
||||||
source: Cow::Borrowed(source.source()),
|
source: chunk.source(),
|
||||||
name: match source.name() {
|
name: Some(name),
|
||||||
Some(name) => Some(name),
|
env: chunk.env(self),
|
||||||
None => CString::new(Location::caller().to_string()).ok(),
|
mode: chunk.mode(),
|
||||||
},
|
|
||||||
env: source.env(self),
|
|
||||||
mode: source.mode(),
|
|
||||||
#[cfg(feature = "luau")]
|
#[cfg(feature = "luau")]
|
||||||
compiler: self.compiler.clone(),
|
compiler: self.compiler.clone(),
|
||||||
}
|
}
|
||||||
|
@ -1380,7 +1377,7 @@ impl Lua {
|
||||||
pub(crate) fn load_chunk<'lua>(
|
pub(crate) fn load_chunk<'lua>(
|
||||||
&'lua self,
|
&'lua self,
|
||||||
source: &[u8],
|
source: &[u8],
|
||||||
name: Option<&CString>,
|
name: Option<&CStr>,
|
||||||
env: Option<Value<'lua>>,
|
env: Option<Value<'lua>>,
|
||||||
mode: Option<ChunkMode>,
|
mode: Option<ChunkMode>,
|
||||||
) -> Result<Function<'lua>> {
|
) -> Result<Function<'lua>> {
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
use std::fs;
|
||||||
|
use std::io;
|
||||||
|
|
||||||
|
use mlua::{Error, Lua, Result};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_chunk_path() -> Result<()> {
|
||||||
|
let lua = Lua::new();
|
||||||
|
|
||||||
|
let temp_dir = tempfile::tempdir().unwrap();
|
||||||
|
fs::write(
|
||||||
|
temp_dir.path().join("module.lua"),
|
||||||
|
r#"
|
||||||
|
return 321
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
let i: i32 = lua.load(&temp_dir.path().join("module.lua")).eval()?;
|
||||||
|
assert_eq!(i, 321);
|
||||||
|
|
||||||
|
match lua.load(&temp_dir.path().join("module2.lua")).exec() {
|
||||||
|
Err(Error::ExternalError(err))
|
||||||
|
if err.downcast_ref::<io::Error>().unwrap().kind() == io::ErrorKind::NotFound => {}
|
||||||
|
res => panic!("expected io::Error, got {:?}", res),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "macros")]
|
||||||
|
fn test_chunk_macro() -> Result<()> {
|
||||||
|
let lua = Lua::new();
|
||||||
|
|
||||||
|
let name = "Rustacean";
|
||||||
|
let table = vec![1];
|
||||||
|
|
||||||
|
let data = lua.create_table()?;
|
||||||
|
data.raw_set("num", 1)?;
|
||||||
|
|
||||||
|
lua.globals().set("g", 123)?;
|
||||||
|
|
||||||
|
lua.load(mlua::chunk! {
|
||||||
|
assert($name == "Rustacean")
|
||||||
|
assert($table[1] == 1)
|
||||||
|
assert($data.num == 1)
|
||||||
|
assert(g == 123)
|
||||||
|
s = 321
|
||||||
|
})
|
||||||
|
.exec()?;
|
||||||
|
|
||||||
|
assert_eq!(lua.globals().get::<_, i32>("s")?, 321);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -1,29 +0,0 @@
|
||||||
#![cfg(feature = "macros")]
|
|
||||||
|
|
||||||
use mlua::{chunk, Lua, Result};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_chunk_macro() -> Result<()> {
|
|
||||||
let lua = Lua::new();
|
|
||||||
|
|
||||||
let name = "Rustacean";
|
|
||||||
let table = vec![1];
|
|
||||||
|
|
||||||
let data = lua.create_table()?;
|
|
||||||
data.raw_set("num", 1)?;
|
|
||||||
|
|
||||||
lua.globals().set("g", 123)?;
|
|
||||||
|
|
||||||
lua.load(chunk! {
|
|
||||||
assert($name == "Rustacean")
|
|
||||||
assert($table[1] == 1)
|
|
||||||
assert($data.num == 1)
|
|
||||||
assert(g == 123)
|
|
||||||
s = 321
|
|
||||||
})
|
|
||||||
.exec()?;
|
|
||||||
|
|
||||||
assert_eq!(lua.globals().get::<_, i32>("s")?, 321);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
Loading…
Reference in New Issue