Rename downcast to try_cast and add cast for Dynamic.

This commit is contained in:
Stephen Chung
2020-04-02 12:35:44 +08:00
parent c4a51b1390
commit 5e7c9b47d5
6 changed files with 169 additions and 115 deletions

View File

@@ -89,7 +89,14 @@ impl Clone for Dynamic {
/// An extension trait that allows down-casting a `Dynamic` value to a specific type.
pub trait AnyExt: Sized {
/// Get a copy of a `Dynamic` value as a specific type.
fn downcast<T: Any + Clone>(self) -> Result<Box<T>, Self>;
fn try_cast<T: Any + Clone>(self) -> Result<T, Self>;
/// Get a copy of a `Dynamic` value as a specific type.
///
/// # Panics
///
/// Panics if the cast fails (e.g. the type of the actual value is not the same as the specified type).
fn cast<T: Any + Clone>(self) -> T;
/// This trait may only be implemented by `rhai`.
#[doc(hidden)]
@@ -106,19 +113,38 @@ impl AnyExt for Dynamic {
///
/// let x: Dynamic = 42_u32.into_dynamic();
///
/// assert_eq!(*x.downcast::<u32>().unwrap(), 42);
/// assert_eq!(x.try_cast::<u32>().unwrap(), 42);
/// ```
fn downcast<T: Any + Clone>(self) -> Result<Box<T>, Self> {
fn try_cast<T: Any + Clone>(self) -> Result<T, Self> {
if self.is::<T>() {
unsafe {
let raw: *mut Variant = Box::into_raw(self);
Ok(Box::from_raw(raw as *mut T))
Ok(*Box::from_raw(raw as *mut T))
}
} else {
Err(self)
}
}
/// Get a copy of the `Dynamic` value as a specific type.
///
/// # Panics
///
/// Panics if the cast fails (e.g. the type of the actual value is not the same as the specified type).
///
/// # Example
///
/// ```
/// use rhai::{Dynamic, Any, AnyExt};
///
/// let x: Dynamic = 42_u32.into_dynamic();
///
/// assert_eq!(x.cast::<u32>(), 42);
/// ```
fn cast<T: Any + Clone>(self) -> T {
self.try_cast::<T>().expect("cast failed")
}
fn _closed(&self) -> _Private {
_Private
}

View File

@@ -692,8 +692,7 @@ impl<'e> Engine<'e> {
ast: &AST,
) -> Result<T, EvalAltResult> {
self.eval_ast_with_scope_raw(scope, false, ast)?
.downcast::<T>()
.map(|v| *v)
.try_cast::<T>()
.map_err(|a| {
EvalAltResult::ErrorMismatchOutputType(
self.map_type_name((*a).type_name()).to_string(),
@@ -735,9 +734,8 @@ impl<'e> Engine<'e> {
/// Evaluate a file, but throw away the result and only return error (if any).
/// Useful for when you don't need the result, but still need to keep track of possible errors.
///
/// # Note
///
/// If `retain_functions` is set to `true`, functions defined by previous scripts are _retained_ and not cleared from run to run.
/// If `retain_functions` is set to `true`, functions defined by previous scripts are _retained_
/// and not cleared from run to run.
#[cfg(not(feature = "no_std"))]
pub fn consume_file(
&mut self,
@@ -750,9 +748,8 @@ impl<'e> Engine<'e> {
/// Evaluate a file with own scope, but throw away the result and only return error (if any).
/// Useful for when you don't need the result, but still need to keep track of possible errors.
///
/// # Note
///
/// If `retain_functions` is set to `true`, functions defined by previous scripts are _retained_ and not cleared from run to run.
/// If `retain_functions` is set to `true`, functions defined by previous scripts are _retained_
/// and not cleared from run to run.
#[cfg(not(feature = "no_std"))]
pub fn consume_file_with_scope(
&mut self,
@@ -767,9 +764,8 @@ impl<'e> Engine<'e> {
/// Evaluate a string, but throw away the result and only return error (if any).
/// Useful for when you don't need the result, but still need to keep track of possible errors.
///
/// # Note
///
/// If `retain_functions` is set to `true`, functions defined by previous scripts are _retained_and not cleared from run to run.
/// If `retain_functions` is set to `true`, functions defined by previous scripts are _retained_
/// and not cleared from run to run.
pub fn consume(&mut self, retain_functions: bool, input: &str) -> Result<(), EvalAltResult> {
self.consume_with_scope(&mut Scope::new(), retain_functions, input)
}
@@ -777,9 +773,8 @@ impl<'e> Engine<'e> {
/// Evaluate a string with own scope, but throw away the result and only return error (if any).
/// Useful for when you don't need the result, but still need to keep track of possible errors.
///
/// # Note
///
/// If `retain_functions` is set to `true`, functions defined by previous scripts are _retained_and not cleared from run to run.
/// If `retain_functions` is set to `true`, functions defined by previous scripts are _retained_
/// and not cleared from run to run.
pub fn consume_with_scope(
&mut self,
scope: &mut Scope,
@@ -797,9 +792,8 @@ impl<'e> Engine<'e> {
/// Evaluate an AST, but throw away the result and only return error (if any).
/// Useful for when you don't need the result, but still need to keep track of possible errors.
///
/// # Note
///
/// If `retain_functions` is set to `true`, functions defined by previous scripts are _retained_and not cleared from run to run.
/// If `retain_functions` is set to `true`, functions defined by previous scripts are _retained_
/// and not cleared from run to run.
pub fn consume_ast(&mut self, retain_functions: bool, ast: &AST) -> Result<(), EvalAltResult> {
self.consume_ast_with_scope(&mut Scope::new(), retain_functions, ast)
}
@@ -807,9 +801,8 @@ impl<'e> Engine<'e> {
/// Evaluate an `AST` with own scope, but throw away the result and only return error (if any).
/// Useful for when you don't need the result, but still need to keep track of possible errors.
///
/// # Note
///
/// If `retain_functions` is set to `true`, functions defined by previous scripts are _retained_and not cleared from run to run.
/// If `retain_functions` is set to `true`, functions defined by previous scripts are _retained_
/// and not cleared from run to run.
pub fn consume_ast_with_scope(
&mut self,
scope: &mut Scope,
@@ -884,8 +877,7 @@ impl<'e> Engine<'e> {
let mut arg_values: Vec<_> = values.iter_mut().map(Dynamic::as_mut).collect();
self.call_fn_raw(name, &mut arg_values, None, Position::none(), 0)?
.downcast()
.map(|b| *b)
.try_cast()
.map_err(|a| {
EvalAltResult::ErrorMismatchOutputType(
self.map_type_name((*a).type_name()).into(),

View File

@@ -628,9 +628,9 @@ impl Engine<'_> {
// val_array[idx]
if let Some(arr) = val.downcast_ref::<Array>() {
let idx = *self
let idx = self
.eval_expr(scope, idx_expr, level)?
.downcast::<INT>()
.try_cast::<INT>()
.map_err(|_| EvalAltResult::ErrorNumericIndexExpr(idx_expr.position()))?;
return if idx >= 0 {
@@ -647,9 +647,9 @@ impl Engine<'_> {
{
// val_map[idx]
if let Some(map) = val.downcast_ref::<Map>() {
let idx = *self
let idx = self
.eval_expr(scope, idx_expr, level)?
.downcast::<String>()
.try_cast::<String>()
.map_err(|_| EvalAltResult::ErrorStringIndexExpr(idx_expr.position()))?;
return Ok((
@@ -662,9 +662,9 @@ impl Engine<'_> {
// val_string[idx]
if let Some(s) = val.downcast_ref::<String>() {
let idx = *self
let idx = self
.eval_expr(scope, idx_expr, level)?
.downcast::<INT>()
.try_cast::<INT>()
.map_err(|_| EvalAltResult::ErrorNumericIndexExpr(idx_expr.position()))?;
return if idx >= 0 {
@@ -795,9 +795,9 @@ impl Engine<'_> {
let s = scope.get_mut_by_type::<String>(src);
let pos = new_val.1;
// Value must be a character
let ch = *new_val
let ch = new_val
.0
.downcast::<char>()
.try_cast::<char>()
.map_err(|_| EvalAltResult::ErrorCharMismatch(pos))?;
Self::str_replace_char(s, idx.as_num(), ch);
Ok(().into_dynamic())
@@ -830,8 +830,8 @@ impl Engine<'_> {
if let Some(s) = target.downcast_mut::<String>() {
// Value must be a character
let ch = *new_val
.downcast::<char>()
let ch = new_val
.try_cast::<char>()
.map_err(|_| EvalAltResult::ErrorCharMismatch(pos))?;
Self::str_replace_char(s, idx.as_num(), ch);
return Ok(target);
@@ -1258,32 +1258,32 @@ impl Engine<'_> {
}
Expr::And(lhs, rhs) => Ok(Box::new(
*self
self
.eval_expr(scope, &*lhs, level)?
.downcast::<bool>()
.try_cast::<bool>()
.map_err(|_| {
EvalAltResult::ErrorBooleanArgMismatch("AND".into(), lhs.position())
})?
&& // Short-circuit using &&
*self
self
.eval_expr(scope, &*rhs, level)?
.downcast::<bool>()
.try_cast::<bool>()
.map_err(|_| {
EvalAltResult::ErrorBooleanArgMismatch("AND".into(), rhs.position())
})?,
)),
Expr::Or(lhs, rhs) => Ok(Box::new(
*self
self
.eval_expr(scope, &*lhs, level)?
.downcast::<bool>()
.try_cast::<bool>()
.map_err(|_| {
EvalAltResult::ErrorBooleanArgMismatch("OR".into(), lhs.position())
})?
|| // Short-circuit using ||
*self
self
.eval_expr(scope, &*rhs, level)?
.downcast::<bool>()
.try_cast::<bool>()
.map_err(|_| {
EvalAltResult::ErrorBooleanArgMismatch("OR".into(), rhs.position())
})?,
@@ -1334,10 +1334,10 @@ impl Engine<'_> {
// If-else statement
Stmt::IfThenElse(guard, if_body, else_body) => self
.eval_expr(scope, guard, level)?
.downcast::<bool>()
.try_cast::<bool>()
.map_err(|_| EvalAltResult::ErrorLogicGuard(guard.position()))
.and_then(|guard_val| {
if *guard_val {
if guard_val {
self.eval_stmt(scope, if_body, level)
} else if let Some(stmt) = else_body {
self.eval_stmt(scope, stmt.as_ref(), level)
@@ -1348,8 +1348,8 @@ impl Engine<'_> {
// While loop
Stmt::While(guard, body) => loop {
match self.eval_expr(scope, guard, level)?.downcast::<bool>() {
Ok(guard_val) if *guard_val => match self.eval_stmt(scope, body, level) {
match self.eval_expr(scope, guard, level)?.try_cast::<bool>() {
Ok(guard_val) if guard_val => match self.eval_stmt(scope, body, level) {
Ok(_) | Err(EvalAltResult::ErrorLoopBreak(false, _)) => (),
Err(EvalAltResult::ErrorLoopBreak(true, _)) => return Ok(().into_dynamic()),
Err(x) => return Err(x),
@@ -1425,9 +1425,7 @@ impl Engine<'_> {
Stmt::ReturnWithVal(Some(a), ReturnType::Exception, pos) => {
let val = self.eval_expr(scope, a, level)?;
Err(EvalAltResult::ErrorRuntime(
val.downcast::<String>()
.map(|s| *s)
.unwrap_or_else(|_| "".to_string()),
val.try_cast::<String>().unwrap_or_else(|_| "".to_string()),
*pos,
))
}

View File

@@ -2673,41 +2673,21 @@ pub fn parse<'a, 'e>(
pub fn map_dynamic_to_expr(value: Dynamic, pos: Position) -> (Option<Expr>, Dynamic) {
if value.is::<INT>() {
let value2 = value.clone();
(
Some(Expr::IntegerConstant(
*value.downcast::<INT>().expect("value should be INT"),
pos,
)),
value2,
)
(Some(Expr::IntegerConstant(value.cast(), pos)), value2)
} else if value.is::<char>() {
let value2 = value.clone();
(
Some(Expr::CharConstant(
*value.downcast::<char>().expect("value should be char"),
pos,
)),
value2,
)
(Some(Expr::CharConstant(value.cast(), pos)), value2)
} else if value.is::<String>() {
let value2 = value.clone();
(
Some(Expr::StringConstant(
*value.downcast::<String>().expect("value should be String"),
pos,
)),
value2,
)
(Some(Expr::StringConstant(value.cast(), pos)), value2)
} else if value.is::<bool>() {
let value2 = value.clone();
(
Some(
if *value.downcast::<bool>().expect("value should be bool") {
Expr::True(pos)
} else {
Expr::False(pos)
},
),
Some(if value.cast::<bool>() {
Expr::True(pos)
} else {
Expr::False(pos)
}),
value2,
)
} else {
@@ -2715,13 +2695,7 @@ pub fn map_dynamic_to_expr(value: Dynamic, pos: Position) -> (Option<Expr>, Dyna
{
if value.is::<FLOAT>() {
let value2 = value.clone();
return (
Some(Expr::FloatConstant(
*value.downcast::<FLOAT>().expect("value should be FLOAT"),
pos,
)),
value2,
);
return (Some(Expr::FloatConstant(value.cast(), pos)), value2);
}
}