diff --git a/src/conversion.rs b/src/conversion.rs index a28b93a..982c182 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -34,7 +34,13 @@ impl<'lua> ToLua<'lua> for String<'lua> { impl<'lua> FromLua<'lua> for String<'lua> { fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result> { + let ty = value.type_name(); lua.coerce_string(value) + .ok_or_else(|| Error::FromLuaConversionError { + from: ty, + to: "String", + message: Some("expected string or number".to_string()), + }) } } @@ -145,8 +151,8 @@ impl<'lua> FromLua<'lua> for Error { Value::Error(err) => Ok(err), val => Ok(Error::RuntimeError( lua.coerce_string(val) - .and_then(|s| Ok(s.to_str()?.to_owned())) - .unwrap_or_else(|_| "".to_owned()), + .and_then(|s| Some(s.to_str().ok()?.to_owned())) + .unwrap_or_else(|| "".to_owned()), )), } } @@ -195,7 +201,15 @@ impl<'lua> ToLua<'lua> for StdString { impl<'lua> FromLua<'lua> for StdString { fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result { - Ok(lua.coerce_string(value)?.to_str()?.to_owned()) + let ty = value.type_name(); + Ok(lua + .coerce_string(value) + .ok_or_else(|| Error::FromLuaConversionError { + from: ty, + to: "String", + message: Some("expected string or number".to_string()), + })?.to_str()? + .to_owned()) } } @@ -209,21 +223,39 @@ macro_rules! lua_convert_int { ($x:ty) => { impl<'lua> ToLua<'lua> for $x { fn to_lua(self, _: &'lua Lua) -> Result> { - cast(self) - .ok_or_else(|| Error::ToLuaConversionError { - from: stringify!($x), - to: "integer", - message: Some("integer out of range".to_owned()), - }).map(Value::Integer) + if let Some(i) = cast(self) { + Ok(Value::Integer(i)) + } else { + cast(self) + .ok_or_else(|| Error::ToLuaConversionError { + from: stringify!($x), + to: "number", + message: Some("out of range".to_owned()), + }).map(Value::Number) + } } } impl<'lua> FromLua<'lua> for $x { fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result { - cast(lua.coerce_integer(value)?).ok_or_else(|| Error::FromLuaConversionError { - from: "integer", + let ty = value.type_name(); + (if let Some(i) = lua.coerce_integer(value.clone()) { + cast(i) + } else { + cast( + lua.coerce_number(value) + .ok_or_else(|| Error::FromLuaConversionError { + from: ty, + to: stringify!($x), + message: Some( + "expected number or string coercible to number".to_string(), + ), + })?, + ) + }).ok_or_else(|| Error::FromLuaConversionError { + from: ty, to: stringify!($x), - message: Some("integer out of range".to_owned()), + message: Some("out of range".to_owned()), }) } } @@ -253,7 +285,19 @@ macro_rules! lua_convert_float { impl<'lua> FromLua<'lua> for $x { fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result { - Ok(lua.coerce_number(value)? as $x) + let ty = value.type_name(); + lua.coerce_number(value) + .ok_or_else(|| Error::FromLuaConversionError { + from: ty, + to: stringify!($x), + message: Some("expected number or string coercible to number".to_string()), + }).and_then(|n| { + cast(n).ok_or_else(|| Error::FromLuaConversionError { + from: ty, + to: stringify!($x), + message: Some("number out of range".to_string()), + }) + }) } } }; diff --git a/src/lua.rs b/src/lua.rs index 15abb91..c13eb2d 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -356,84 +356,73 @@ impl Lua { r } - /// Coerces a Lua value to a string. + /// Attempts to coerce a Lua value into a String in a manner consistent with Lua's internal + /// behavior. /// - /// The value must be a string (in which case this is a no-op) or a number. - pub fn coerce_string<'lua>(&'lua self, v: Value<'lua>) -> Result> { + /// To succeed, the value must be a string (in which case this is a no-op), an integer, or a + /// number. + pub fn coerce_string<'lua>(&'lua self, v: Value<'lua>) -> Option> { match v { - Value::String(s) => Ok(s), + Value::String(s) => Some(s), v => unsafe { let _sg = StackGuard::new(self.state); assert_stack(self.state, 4); - let ty = v.type_name(); self.push_value(v); - let s = - protect_lua_closure(self.state, 1, 1, |state| ffi::lua_tostring(state, -1))?; + let s = gc_guard(self.state, || ffi::lua_tostring(self.state, -1)); if s.is_null() { - Err(Error::FromLuaConversionError { - from: ty, - to: "String", - message: Some("expected string or number".to_string()), - }) + None } else { - Ok(String(self.pop_ref())) + Some(String(self.pop_ref())) } }, } } - /// Coerces a Lua value to an integer. + /// Attempts to coerce a Lua value into an integer in a manner consistent with Lua's internal + /// behavior. /// - /// The value must be an integer, or a floating point number, or a string that can be converted - /// to an integer. Refer to the Lua manual for details. - pub fn coerce_integer(&self, v: Value) -> Result { + /// To succeed, the value must be an integer, a floating point number that has an exact + /// representation as an integer, or a string that can be converted to an integer. Refer to the + /// Lua manual for details. + pub fn coerce_integer(&self, v: Value) -> Option { match v { - Value::Integer(i) => Ok(i), + Value::Integer(i) => Some(i), v => unsafe { let _sg = StackGuard::new(self.state); assert_stack(self.state, 2); - let ty = v.type_name(); self.push_value(v); let mut isint = 0; let i = ffi::lua_tointegerx(self.state, -1, &mut isint); if isint == 0 { - Err(Error::FromLuaConversionError { - from: ty, - to: "integer", - message: None, - }) + None } else { - Ok(i) + Some(i) } }, } } - /// Coerce a Lua value to a number. + /// Attempts to coerce a Lua value into a Number in a manner consistent with Lua's internal + /// behavior. /// - /// The value must be a number or a string that can be converted to a number. Refer to the Lua - /// manual for details. - pub fn coerce_number(&self, v: Value) -> Result { + /// To succeed, the value must be a number or a string that can be converted to a number. Refer + /// to the Lua manual for details. + pub fn coerce_number(&self, v: Value) -> Option { match v { - Value::Number(n) => Ok(n), + Value::Number(n) => Some(n), v => unsafe { let _sg = StackGuard::new(self.state); assert_stack(self.state, 2); - let ty = v.type_name(); self.push_value(v); let mut isnum = 0; let n = ffi::lua_tonumberx(self.state, -1, &mut isnum); if isnum == 0 { - Err(Error::FromLuaConversionError { - from: ty, - to: "number", - message: Some("number or string coercible to number".to_string()), - }) + None } else { - Ok(n) + Some(n) } }, } diff --git a/tests/tests.rs b/tests/tests.rs index 8383afe..23375e4 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -4,7 +4,7 @@ extern crate rlua; use std::iter::FromIterator; use std::panic::catch_unwind; use std::sync::Arc; -use std::{error, fmt}; +use std::{error, f32, f64, fmt}; use failure::err_msg; use rlua::{ @@ -354,29 +354,52 @@ fn test_result_conversions() { #[test] fn test_num_conversion() { let lua = Lua::new(); - let globals = lua.globals(); - globals.set("n", "1.0").unwrap(); - assert_eq!(globals.get::<_, i64>("n").unwrap(), 1); - assert_eq!(globals.get::<_, f64>("n").unwrap(), 1.0); - assert_eq!(globals.get::<_, String>("n").unwrap(), "1.0"); + assert_eq!( + lua.coerce_integer(Value::String(lua.create_string("1").unwrap())), + Some(1) + ); + assert_eq!( + lua.coerce_integer(Value::String(lua.create_string("1.0").unwrap())), + Some(1) + ); + assert_eq!( + lua.coerce_integer(Value::String(lua.create_string("1.5").unwrap())), + None + ); - globals.set("n", "1.5").unwrap(); - assert!(globals.get::<_, i64>("n").is_err()); - assert_eq!(globals.get::<_, f64>("n").unwrap(), 1.5); - assert_eq!(globals.get::<_, String>("n").unwrap(), "1.5"); + assert_eq!( + lua.coerce_number(Value::String(lua.create_string("1").unwrap())), + Some(1.0) + ); + assert_eq!( + lua.coerce_number(Value::String(lua.create_string("1.0").unwrap())), + Some(1.0) + ); + assert_eq!( + lua.coerce_number(Value::String(lua.create_string("1.5").unwrap())), + Some(1.5) + ); - globals.set("n", 1.5).unwrap(); - assert!(globals.get::<_, i64>("n").is_err()); - assert_eq!(globals.get::<_, f64>("n").unwrap(), 1.5); - assert_eq!(globals.get::<_, String>("n").unwrap(), "1.5"); + assert_eq!(lua.eval::("1.0", None).unwrap(), 1); + assert_eq!(lua.eval::("1.0", None).unwrap(), 1.0); + assert_eq!(lua.eval::("1.0", None).unwrap(), "1.0"); - lua.exec::<()>("a = math.huge", None).unwrap(); - assert!(globals.get::<_, i64>("n").is_err()); + assert_eq!(lua.eval::("1.5", None).unwrap(), 1); + assert_eq!(lua.eval::("1.5", None).unwrap(), 1.5); + assert_eq!(lua.eval::("1.5", None).unwrap(), "1.5"); assert!(lua.eval::("-1", None).is_err()); - assert!(lua.eval::("-1", None).is_ok()); - assert!(lua.pack(1u128 << 64).is_err()); + assert_eq!(lua.eval::("-1", None).unwrap(), -1); + + assert!(lua.unpack::(lua.pack(1u128 << 64).unwrap()).is_err()); + assert!(lua.eval::("math.huge", None).is_err()); + + assert_eq!( + lua.unpack::(lua.pack(f32::MAX).unwrap()).unwrap(), + f32::MAX as f64 + ); + assert!(lua.unpack::(lua.pack(f64::MAX).unwrap()).is_err()); } #[test]