diff options
author | Alex Orlenko <zxteam@protonmail.com> | 2022-04-14 20:48:00 +0100 |
---|---|---|
committer | Alex Orlenko <zxteam@protonmail.com> | 2022-04-14 20:48:00 +0100 |
commit | d3975bdf304969b16597fa9407960c718784ebaa (patch) | |
tree | b6f3e4da99db12127fcdfc3127731d57bb2c14ac /src | |
parent | 21affdadfd69428883dfeca28f457e02bad0a162 (diff) | |
download | mlua-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.rs | 167 | ||||
-rw-r--r-- | src/lua.rs | 27 |
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 + } } @@ -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>> { |