diff --git a/RELEASES.md b/RELEASES.md index c8821ac9..57645f5f 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -4,6 +4,10 @@ Rhai Release Notes Version 0.17.0 ============== +Breaking changes +---------------- + +* `EvalAltResult::ErrorMismatchOutputType` has an extra argument containing the name of the requested type. Version 0.16.1 ============== diff --git a/src/any.rs b/src/any.rs index 6a14cb4e..0a89e57a 100644 --- a/src/any.rs +++ b/src/any.rs @@ -202,6 +202,27 @@ impl Dynamic { } } +/// Map the name of a standard type into a friendly form. +pub(crate) fn map_std_type_name(name: &str) -> &str { + if name == type_name::() { + "string" + } else if name == type_name::() { + "string" + } else if name == type_name::<&str>() { + "string" + } else if name == type_name::() { + "map" + } else if name == type_name::() { + "array" + } else if name == type_name::() { + "Fn" + } else if name == type_name::() { + "timestamp" + } else { + name + } +} + impl fmt::Display for Dynamic { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match &self.0 { diff --git a/src/api.rs b/src/api.rs index 2f0e3e19..190e69db 100644 --- a/src/api.rs +++ b/src/api.rs @@ -976,11 +976,12 @@ impl Engine { let mut mods = Imports::new(); let (result, _) = self.eval_ast_with_scope_raw(scope, &mut mods, ast)?; - let return_type = self.map_type_name(result.type_name()); + let typ = self.map_type_name(result.type_name()); return result.try_cast::().ok_or_else(|| { Box::new(EvalAltResult::ErrorMismatchOutputType( - return_type.into(), + self.map_type_name(type_name::()).into(), + typ.into(), Position::none(), )) }); @@ -1123,11 +1124,12 @@ impl Engine { let mut arg_values = args.into_vec(); let result = self.call_fn_dynamic_raw(scope, ast, name, arg_values.as_mut())?; - let return_type = self.map_type_name(result.type_name()); + let typ = self.map_type_name(result.type_name()); return result.try_cast().ok_or_else(|| { Box::new(EvalAltResult::ErrorMismatchOutputType( - return_type.into(), + self.map_type_name(type_name::()).into(), + typ.into(), Position::none(), )) }); diff --git a/src/engine.rs b/src/engine.rs index 96514f48..4f6ec6f3 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1,6 +1,6 @@ //! Main module defining the script evaluation `Engine`. -use crate::any::{Dynamic, Union, Variant}; +use crate::any::{map_std_type_name, Dynamic, Union, Variant}; use crate::calc_fn_hash; use crate::error::ParseErrorType; use crate::fn_native::{CallableFunction, Callback, FnCallArgs, FnPtr}; @@ -18,7 +18,7 @@ use crate::utils::StaticVec; use crate::parser::FLOAT; use crate::stdlib::{ - any::TypeId, + any::{type_name, TypeId}, borrow::Cow, boxed::Box, collections::HashMap, @@ -816,9 +816,10 @@ impl Engine { // See if the function match print/debug (which requires special processing) return Ok(match fn_name { KEYWORD_PRINT => ( - (self.print)(result.as_str().map_err(|type_name| { + (self.print)(result.as_str().map_err(|typ| { Box::new(EvalAltResult::ErrorMismatchOutputType( - type_name.into(), + self.map_type_name(type_name::()).into(), + typ.into(), Position::none(), )) })?) @@ -826,9 +827,10 @@ impl Engine { false, ), KEYWORD_DEBUG => ( - (self.debug)(result.as_str().map_err(|type_name| { + (self.debug)(result.as_str().map_err(|typ| { Box::new(EvalAltResult::ErrorMismatchOutputType( - type_name.into(), + self.map_type_name(type_name::()).into(), + typ.into(), Position::none(), )) })?) @@ -1064,8 +1066,12 @@ impl Engine { lib: &Module, script: &Dynamic, ) -> Result> { - let script = script.as_str().map_err(|type_name| { - EvalAltResult::ErrorMismatchOutputType(type_name.into(), Position::none()) + let script = script.as_str().map_err(|typ| { + EvalAltResult::ErrorMismatchOutputType( + self.map_type_name(type_name::()).into(), + typ.into(), + Position::none(), + ) })?; // Compile the script text @@ -1873,9 +1879,10 @@ impl Engine { self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?; return arg_value .take_immutable_string() - .map_err(|type_name| { + .map_err(|typ| { Box::new(EvalAltResult::ErrorMismatchOutputType( - type_name.into(), + self.map_type_name(type_name::()).into(), + typ.into(), expr.position(), )) }) @@ -2524,7 +2531,7 @@ impl Engine { self.type_names .get(name) .map(String::as_str) - .unwrap_or(name) + .unwrap_or(map_std_type_name(name)) } } diff --git a/src/result.rs b/src/result.rs index d6351b25..7dd3d6fa 100644 --- a/src/result.rs +++ b/src/result.rs @@ -74,8 +74,8 @@ pub enum EvalAltResult { /// Assignment to a constant variable. ErrorAssignmentToConstant(String, Position), /// Returned type is not the same as the required output type. - /// Wrapped value is the type of the actual result. - ErrorMismatchOutputType(String, Position), + /// Wrapped values are the type requested and type of the actual result. + ErrorMismatchOutputType(String, String, Position), /// Inappropriate member access. ErrorDotExpr(String, Position), /// Arithmetic error encountered. Wrapped value is the error message. @@ -141,7 +141,7 @@ impl EvalAltResult { "Assignment to an unsupported left-hand side expression" } Self::ErrorAssignmentToConstant(_, _) => "Assignment to a constant variable", - Self::ErrorMismatchOutputType(_, _) => "Output type is incorrect", + Self::ErrorMismatchOutputType(_, _, _) => "Output type is incorrect", Self::ErrorInExpr(_) => "Malformed 'in' expression", Self::ErrorDotExpr(_, _) => "Malformed dot expression", Self::ErrorArithmetic(_, _) => "Arithmetic error", @@ -202,7 +202,9 @@ impl fmt::Display for EvalAltResult { Self::ErrorRuntime(s, _) => f.write_str(if s.is_empty() { desc } else { s })?, Self::ErrorAssignmentToConstant(s, _) => write!(f, "{}: '{}'", desc, s)?, - Self::ErrorMismatchOutputType(s, _) => write!(f, "{}: {}", desc, s)?, + Self::ErrorMismatchOutputType(r, s, _) => { + write!(f, "{} (expecting {}): {}", desc, s, r)? + } Self::ErrorArithmetic(s, _) => f.write_str(s)?, Self::ErrorLoopBreak(_, _) => f.write_str(desc)?, @@ -289,7 +291,7 @@ impl EvalAltResult { | Self::ErrorModuleNotFound(_, pos) | Self::ErrorAssignmentToUnknownLHS(pos) | Self::ErrorAssignmentToConstant(_, pos) - | Self::ErrorMismatchOutputType(_, pos) + | Self::ErrorMismatchOutputType(_, _, pos) | Self::ErrorInExpr(pos) | Self::ErrorDotExpr(_, pos) | Self::ErrorArithmetic(_, pos) @@ -329,7 +331,7 @@ impl EvalAltResult { | Self::ErrorModuleNotFound(_, pos) | Self::ErrorAssignmentToUnknownLHS(pos) | Self::ErrorAssignmentToConstant(_, pos) - | Self::ErrorMismatchOutputType(_, pos) + | Self::ErrorMismatchOutputType(_, _, pos) | Self::ErrorInExpr(pos) | Self::ErrorDotExpr(_, pos) | Self::ErrorArithmetic(_, pos) diff --git a/tests/mismatched_op.rs b/tests/mismatched_op.rs index b129f29b..1199bf94 100644 --- a/tests/mismatched_op.rs +++ b/tests/mismatched_op.rs @@ -6,14 +6,14 @@ fn test_mismatched_op() { assert!(matches!( *engine.eval::(r#""hello, " + "world!""#).expect_err("expects error"), - EvalAltResult::ErrorMismatchOutputType(err, _) if err == "string" + EvalAltResult::ErrorMismatchOutputType(need, actual, _) if need == std::any::type_name::() && actual == "string" )); } #[test] #[cfg(not(feature = "no_object"))] fn test_mismatched_op_custom_type() { - #[derive(Clone)] + #[derive(Debug, Clone)] struct TestStruct { x: INT, } @@ -28,19 +28,14 @@ fn test_mismatched_op_custom_type() { engine.register_type_with_name::("TestStruct"); engine.register_fn("new_ts", TestStruct::new); - let r = engine - .eval::("60 + new_ts()") - .expect_err("expects error"); - - #[cfg(feature = "only_i32")] assert!(matches!( - *r, - EvalAltResult::ErrorFunctionNotFound(err, _) if err == "+ (i32, TestStruct)" + *engine.eval::("60 + new_ts()").expect_err("should error"), + EvalAltResult::ErrorFunctionNotFound(err, _) if err == format!("+ ({}, TestStruct)", std::any::type_name::()) )); - #[cfg(not(feature = "only_i32"))] assert!(matches!( - *r, - EvalAltResult::ErrorFunctionNotFound(err, _) if err == "+ (i64, TestStruct)" + *engine.eval::("42").expect_err("should error"), + EvalAltResult::ErrorMismatchOutputType(need, actual, _) + if need == "TestStruct" && actual == std::any::type_name::() )); }