Add Dynamic::is_XXX API.

This commit is contained in:
Stephen Chung 2022-11-09 12:44:57 +08:00
parent ad018aaae3
commit ce046422f0
22 changed files with 327 additions and 91 deletions

View File

@ -25,6 +25,11 @@ Breaking changes
New features
------------
### `Dynamic` detection API
* New methods are added to `Dynamic` in the form of `is_XXX()` where `XXX` is a type (e.g. `is_int`, `is_unit`, `is_bool`, `is_array`).
* This new API is to make it easier to detect the data type, instead of having to call `is::<XXX>()`.
### Loop expressions
* Loops (such as `loop`, `do`, `while` and `for`) can now act as _expressions_, with the `break` statement returning an optional value.
@ -37,9 +42,9 @@ New features
* This is necessary when using Rhai across shared-library boundaries.
* A build script is used to extract the environment variable (`RHAI_AHASH_SEED`, if any) and splice it into the source code before compilation.
### No Timestamps
### `no_time` for no timestamps
* A new feature, `no_time`, is added to disable support timestamps.
* A new feature, `no_time`, is added to disable support for timestamps.
* This may be necessary when building for architectures without time support, such as raw WASM.
### Serializable `Scope`

View File

@ -99,6 +99,10 @@ impl Engine {
///
/// To define a pretty-print name, call [`with_name`][`TypeBuilder::with_name`],
/// to use [`Engine::register_type_with_name`] instead.
///
/// # WARNING - Volatile Type
///
/// This type is volatile and may change in the future.
#[deprecated = "This type is NOT deprecated, but it is considered volatile and may change in the future."]
pub struct TypeBuilder<'a, T: Variant + Clone> {
engine: &'a mut Engine,

View File

@ -89,6 +89,7 @@ impl Expression<'_> {
/// # WARNING - Low Level API
///
/// This function is _extremely_ low level. It evaluates an expression from an [`AST`][crate::AST].
#[deprecated = "This API is NOT deprecated, but it is considered volatile and may change in the future."]
#[inline(always)]
pub fn eval_with_context_raw(
&self,

View File

@ -166,7 +166,7 @@ pub fn format_map_as_json(map: &Map) -> String {
if let Some(val) = value.read_lock::<Map>() {
result.push_str(&format_map_as_json(&*val));
} else if value.is::<()>() {
} else if value.is_unit() {
result.push_str("null");
} else {
write!(result, "{:?}", value).unwrap();

View File

@ -559,7 +559,7 @@ fn main() {
// Evaluate
engine.eval_ast_with_scope::<Dynamic>(&mut scope, &main_ast)
}) {
Ok(result) if !result.is::<()>() => {
Ok(result) if !result.is_unit() => {
println!("=> {result:?}");
println!();
}

View File

@ -63,7 +63,7 @@ impl Engine {
#[cfg(not(feature = "no_index"))]
ChainType::Indexing => {
// Check for existence with the null conditional operator
if parent_options.contains(ASTFlags::NEGATED) && target.is::<()>() {
if parent_options.contains(ASTFlags::NEGATED) && target.is_unit() {
return Ok((Dynamic::UNIT, false));
}
@ -189,7 +189,7 @@ impl Engine {
#[cfg(not(feature = "no_object"))]
ChainType::Dotting => {
// Check for existence with the Elvis operator
if parent_options.contains(ASTFlags::NEGATED) && target.is::<()>() {
if parent_options.contains(ASTFlags::NEGATED) && target.is_unit() {
return Ok((Dynamic::UNIT, false));
}
@ -229,7 +229,7 @@ impl Engine {
unreachable!("function call in dot chain should not be namespace-qualified")
}
// {xxx:map}.id op= ???
Expr::Property(x, pos) if target.is::<crate::Map>() && new_val.is_some() => {
Expr::Property(x, pos) if target.is_map() && new_val.is_some() => {
#[cfg(feature = "debugging")]
self.run_debugger(global, caches, lib, scope, this_ptr, rhs)?;
@ -247,7 +247,7 @@ impl Engine {
Ok((Dynamic::UNIT, true))
}
// {xxx:map}.id
Expr::Property(x, pos) if target.is::<crate::Map>() => {
Expr::Property(x, pos) if target.is_map() => {
#[cfg(feature = "debugging")]
self.run_debugger(global, caches, lib, scope, this_ptr, rhs)?;
@ -351,7 +351,7 @@ impl Engine {
}
// {xxx:map}.sub_lhs[expr] | {xxx:map}.sub_lhs.expr
Expr::Index(x, options, x_pos) | Expr::Dot(x, options, x_pos)
if target.is::<crate::Map>() =>
if target.is_map() =>
{
let _node = &x.lhs;

View File

@ -287,7 +287,7 @@ impl Engine {
// `... ${...} ...`
Expr::InterpolatedString(x, _) => {
let mut concat = self.get_interned_string("").into();
let mut concat = self.const_empty_string().into();
let target = &mut concat;
let mut op_info = OpAssignment::new_op_assignment(OP_CONCAT, Position::NONE);
@ -296,7 +296,9 @@ impl Engine {
let result = x
.iter()
.try_for_each(|expr| {
let item = self.eval_expr(global, caches, lib, scope, this_ptr, expr)?;
let item = self
.eval_expr(global, caches, lib, scope, this_ptr, expr)?
.flatten();
op_info.pos = expr.start_position();
@ -394,7 +396,7 @@ impl Engine {
Expr::Coalesce(x, ..) => {
let value = self.eval_expr(global, caches, lib, scope, this_ptr, &x.lhs)?;
if value.is::<()>() {
if value.is_unit() {
self.eval_expr(global, caches, lib, scope, this_ptr, &x.rhs)
} else {
Ok(value)

View File

@ -8,7 +8,10 @@ use crate::ast::{
use crate::func::{get_builtin_op_assignment_fn, get_hasher};
use crate::types::dynamic::{AccessMode, Union};
use crate::types::RestoreOnDrop;
use crate::{Dynamic, Engine, Position, RhaiResult, RhaiResultOf, Scope, SharedModule, ERR, INT};
use crate::{
Dynamic, Engine, ImmutableString, Position, RhaiResult, RhaiResultOf, Scope, SharedModule, ERR,
INT,
};
use std::hash::{Hash, Hasher};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
@ -174,8 +177,7 @@ impl Engine {
global, caches, lib, op, token, *hash_op, args, true, *op_pos,
)
.map_err(|err| err.fill_position(op_info.pos))?
.0
.flatten();
.0;
}
Err(err) => return Err(err),
}
@ -183,6 +185,13 @@ impl Engine {
self.check_data_size(args[0], root.1)?;
} else {
// Normal assignment
// If value is a string, intern it
if new_val.is_string() {
let value = new_val.into_immutable_string().expect("`ImmutableString`");
new_val = self.get_interned_string(value).into();
}
*target.write_lock::<Dynamic>().unwrap() = new_val;
}
@ -257,34 +266,27 @@ impl Engine {
let root = (var_name, pos);
let lhs_ptr = &mut lhs_ptr;
return self
.eval_op_assignment(global, caches, lib, op_info, lhs_ptr, root, rhs_val)
.map(|_| Dynamic::UNIT);
self.eval_op_assignment(global, caches, lib, op_info, lhs_ptr, root, rhs_val)?;
return Ok(Dynamic::UNIT);
}
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
{
let rhs_val = self.eval_expr(global, caches, lib, scope, this_ptr, rhs)?;
let mut rhs_val = self
.eval_expr(global, caches, lib, scope, this_ptr, rhs)?
.flatten();
// Check if the result is a string. If so, intern it.
#[cfg(not(feature = "no_closure"))]
let is_string = !rhs_val.is_shared() && rhs_val.is::<crate::ImmutableString>();
#[cfg(feature = "no_closure")]
let is_string = rhs_val.is::<crate::ImmutableString>();
let rhs_val = if is_string {
self.get_interned_string(
rhs_val.into_immutable_string().expect("`ImmutableString`"),
)
.into()
} else {
rhs_val.flatten()
};
// If value is a string, intern it
if rhs_val.is_string() {
let value = rhs_val.into_immutable_string().expect("`ImmutableString`");
rhs_val = self.get_interned_string(value).into();
}
let _new_val = &mut Some((rhs_val, op_info));
// Must be either `var[index] op= val` or `var.prop op= val`
return match lhs {
match lhs {
// name op= rhs (handled above)
Expr::Variable(..) => {
unreachable!("Expr::Variable case is already handled")
@ -298,8 +300,9 @@ impl Engine {
Expr::Dot(..) => self
.eval_dot_index_chain(global, caches, lib, scope, this_ptr, lhs, _new_val),
_ => unreachable!("cannot assign to expression: {:?}", lhs),
}
.map(|_| Dynamic::UNIT);
}?;
return Ok(Dynamic::UNIT);
}
}
@ -381,7 +384,7 @@ impl Engine {
break;
}
}
} else if value.is::<INT>() && !ranges.is_empty() {
} else if value.is_int() && !ranges.is_empty() {
// Then check integer ranges
let value = value.as_int().expect("`INT`");
@ -803,8 +806,8 @@ impl Engine {
let v = self.eval_expr(global, caches, lib, scope, this_ptr, expr)?;
let typ = v.type_name();
let path = v.try_cast::<crate::ImmutableString>().ok_or_else(|| {
self.make_type_mismatch_err::<crate::ImmutableString>(typ, expr.position())
let path = v.try_cast::<ImmutableString>().ok_or_else(|| {
self.make_type_mismatch_err::<ImmutableString>(typ, expr.position())
})?;
use crate::ModuleResolver;
@ -832,7 +835,7 @@ impl Engine {
let (export, must_be_indexed) = if !export.is_empty() {
(export.name.clone(), true)
} else {
(self.get_interned_string(""), false)
(self.const_empty_string(), false)
};
if !must_be_indexed || module.is_indexed() {

View File

@ -145,7 +145,7 @@ impl Engine {
format!(
"{fn_name} ({})",
args.iter()
.map(|a| if a.is::<ImmutableString>() {
.map(|a| if a.is_string() {
"&str | ImmutableString | String"
} else {
self.map_type_name(a.type_name())
@ -574,7 +574,7 @@ impl Engine {
// Handle is_def_fn()
#[cfg(not(feature = "no_function"))]
crate::engine::KEYWORD_IS_DEF_FN
if args.len() == 2 && args[0].is::<FnPtr>() && args[1].is::<crate::INT>() =>
if args.len() == 2 && args[0].is_fnptr() && args[1].is_int() =>
{
let fn_name = args[0].read_lock::<ImmutableString>().expect("`FnPtr`");
let num_params = args[1].as_int().expect("`INT`");
@ -731,7 +731,7 @@ impl Engine {
let is_ref_mut = target.is_ref();
let (result, updated) = match fn_name {
KEYWORD_FN_PTR_CALL if target.is::<FnPtr>() => {
KEYWORD_FN_PTR_CALL if target.is_fnptr() => {
// FnPtr call
let fn_ptr = target.read_lock::<FnPtr>().expect("`FnPtr`");
@ -775,7 +775,7 @@ impl Engine {
if call_args.is_empty() {
let typ = self.map_type_name(target.type_name());
return Err(self.make_type_mismatch_err::<FnPtr>(typ, fn_call_pos));
} else if !call_args[0].is::<FnPtr>() {
} else if !call_args[0].is_fnptr() {
let typ = self.map_type_name(call_args[0].type_name());
return Err(self.make_type_mismatch_err::<FnPtr>(typ, first_arg_pos));
}
@ -827,7 +827,7 @@ impl Engine {
)
}
KEYWORD_FN_PTR_CURRY => {
if !target.is::<FnPtr>() {
if !target.is_fnptr() {
let typ = self.map_type_name(target.type_name());
return Err(self.make_type_mismatch_err::<FnPtr>(typ, fn_call_pos));
}
@ -969,7 +969,7 @@ impl Engine {
let (arg_value, arg_pos) =
self.get_arg_value(global, caches, lib, scope, this_ptr, arg)?;
if !arg_value.is::<FnPtr>() {
if !arg_value.is_fnptr() {
let typ = self.map_type_name(arg_value.type_name());
return Err(self.make_type_mismatch_err::<FnPtr>(typ, arg_pos));
}
@ -1025,7 +1025,7 @@ impl Engine {
let (arg_value, arg_pos) =
self.get_arg_value(global, caches, lib, scope, this_ptr, first)?;
if !arg_value.is::<FnPtr>() {
if !arg_value.is_fnptr() {
let typ = self.map_type_name(arg_value.type_name());
return Err(self.make_type_mismatch_err::<FnPtr>(typ, arg_pos));
}

View File

@ -23,6 +23,7 @@ pub use callable_function::CallableFunction;
pub use func::Func;
pub use hashing::{calc_fn_hash, calc_fn_hash_full, calc_var_hash, get_hasher, StraightHashMap};
#[cfg(feature = "internals")]
#[allow(deprecated)]
pub use native::NativeCallContextStore;
pub use native::{
locked_read, locked_write, shared_get_mut, shared_make_mut, shared_take, shared_take_or_clone,

View File

@ -81,6 +81,11 @@ pub struct NativeCallContext<'a> {
/// _(internals)_ Context of a native Rust function call.
/// Exported under the `internals` feature only.
///
/// # WARNING - Volatile Type
///
/// This type is volatile and may change in the future.
#[deprecated = "This type is NOT deprecated, but it is considered volatile and may change in the future."]
#[cfg(feature = "internals")]
#[derive(Debug, Clone)]
pub struct NativeCallContextStore {
@ -97,8 +102,14 @@ pub struct NativeCallContextStore {
}
#[cfg(feature = "internals")]
#[allow(deprecated)]
impl NativeCallContextStore {
/// Create a [`NativeCallContext`] from a [`NativeCallContextClone`].
///
/// # WARNING - Unstable API
///
/// This API is volatile and may change in the future.
#[deprecated = "This API is NOT deprecated, but it is considered volatile and may change in the future."]
#[inline(always)]
#[must_use]
pub fn create_context<'a>(&'a self, engine: &'a Engine) -> NativeCallContext<'a> {
@ -167,9 +178,15 @@ impl<'a> NativeCallContext<'a> {
/// _(internals)_ Create a [`NativeCallContext`] from a [`NativeCallContextClone`].
/// Exported under the `internals` feature only.
///
/// # WARNING - Unstable API
///
/// This API is volatile and may change in the future.
#[deprecated = "This API is NOT deprecated, but it is considered volatile and may change in the future."]
#[cfg(feature = "internals")]
#[inline]
#[must_use]
#[allow(deprecated)]
pub fn from_stored_data(engine: &'a Engine, context: &'a NativeCallContextStore) -> Self {
Self {
engine,
@ -182,9 +199,15 @@ impl<'a> NativeCallContext<'a> {
}
/// _(internals)_ Store this [`NativeCallContext`] into a [`NativeCallContextClone`].
/// Exported under the `internals` feature only.
///
/// # WARNING - Unstable API
///
/// This API is volatile and may change in the future.
#[deprecated = "This API is NOT deprecated, but it is considered volatile and may change in the future."]
#[cfg(feature = "internals")]
#[inline]
#[must_use]
#[allow(deprecated)]
pub fn store_data(&self) -> NativeCallContextStore {
NativeCallContextStore {
fn_name: self.fn_name.to_string(),

View File

@ -111,10 +111,10 @@ impl Engine {
#[cfg(not(feature = "no_module"))]
let (lib, constants) = if let Some(ref environ) = fn_def.environ {
let crate::ast::EncapsulatedEnviron {
lib: fn_lib,
imports,
constants,
} = environ.as_ref();
lib: ref fn_lib,
ref imports,
ref constants,
} = **environ;
imports
.iter()

View File

@ -304,17 +304,15 @@ pub use types::dynamic::{AccessMode, DynamicReadLock, DynamicWriteLock, Variant}
pub use types::FloatWrapper;
#[cfg(feature = "internals")]
pub use tokenizer::{get_next_token, parse_string_literal};
pub use types::StringsInterner;
#[cfg(feature = "internals")]
pub use tokenizer::{
is_valid_function_name, is_valid_identifier, InputStream, MultiInputsStream, Span, Token,
TokenIterator, TokenizeState, TokenizerControl, TokenizerControlBlock,
get_next_token, is_valid_function_name, is_valid_identifier, parse_string_literal, InputStream,
MultiInputsStream, Span, Token, TokenIterator, TokenizeState, TokenizerControl,
TokenizerControlBlock,
};
#[cfg(feature = "internals")]
pub use types::StringsInterner;
#[cfg(feature = "internals")]
pub use parser::ParseState;
@ -340,6 +338,10 @@ pub use ast::EncapsulatedEnviron;
#[cfg(feature = "internals")]
pub use eval::{Caches, FnResolutionCache, FnResolutionCacheEntry, GlobalRuntimeState};
#[cfg(feature = "internals")]
#[allow(deprecated)]
pub use func::NativeCallContextStore;
#[cfg(feature = "internals")]
#[cfg(feature = "metadata")]
pub use api::definitions::Definitions;

View File

@ -12,7 +12,7 @@ use crate::tokenizer::Token;
use crate::types::dynamic::AccessMode;
use crate::{
calc_fn_hash, calc_fn_hash_full, Dynamic, Engine, FnPtr, Identifier, ImmutableString, Position,
Scope, StaticVec, AST, INT,
Scope, StaticVec, AST,
};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
@ -592,7 +592,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
}
// Then check ranges
if value.is::<INT>() && !ranges.is_empty() {
if value.is_int() && !ranges.is_empty() {
let value = value.as_int().unwrap();
// Only one range or all ranges without conditions

View File

@ -39,7 +39,7 @@ pub fn print_with_func(
value: &mut Dynamic,
) -> crate::ImmutableString {
match ctx.call_native_fn_raw(fn_name, true, &mut [value]) {
Ok(result) if result.is::<crate::ImmutableString>() => {
Ok(result) if result.is_string() => {
result.into_immutable_string().expect("`ImmutableString`")
}
Ok(result) => ctx.engine().map_type_name(result.type_name()).into(),
@ -75,7 +75,7 @@ mod print_debug_functions {
/// Return the empty string.
#[rhai_fn(name = "print", name = "debug")]
pub fn print_empty_string(ctx: NativeCallContext) -> ImmutableString {
ctx.engine().get_interned_string("")
ctx.engine().const_empty_string()
}
/// Return the `string`.
@ -121,7 +121,7 @@ mod print_debug_functions {
#[rhai_fn(name = "print", name = "to_string")]
pub fn print_unit(ctx: NativeCallContext, unit: ()) -> ImmutableString {
let _ = unit;
ctx.engine().get_interned_string("")
ctx.engine().const_empty_string()
}
/// Convert the unit into a string in debug format.
#[rhai_fn(name = "debug", name = "to_debug")]

View File

@ -348,7 +348,7 @@ mod string_functions {
len: INT,
) -> ImmutableString {
if string.is_empty() || len <= 0 {
return ctx.engine().get_interned_string("");
return ctx.engine().const_empty_string();
}
let len = len.min(MAX_USIZE_INT) as usize;
@ -896,18 +896,18 @@ mod string_functions {
len: INT,
) -> ImmutableString {
if string.is_empty() {
return ctx.engine().get_interned_string("");
return ctx.engine().const_empty_string();
}
let mut chars = StaticVec::with_capacity(string.len());
let offset = if string.is_empty() || len <= 0 {
return ctx.engine().get_interned_string("");
return ctx.engine().const_empty_string();
} else if start < 0 {
let abs_start = start.unsigned_abs();
if abs_start as u64 > MAX_USIZE_INT as u64 {
return ctx.engine().get_interned_string("");
return ctx.engine().const_empty_string();
}
let abs_start = abs_start as usize;
@ -918,7 +918,7 @@ mod string_functions {
chars.len() - abs_start
}
} else if start > MAX_USIZE_INT || start as usize >= string.chars().count() {
return ctx.engine().get_interned_string("");
return ctx.engine().const_empty_string();
} else {
start as usize
};
@ -964,7 +964,7 @@ mod string_functions {
start: INT,
) -> ImmutableString {
if string.is_empty() {
ctx.engine().get_interned_string("")
ctx.engine().const_empty_string()
} else {
let len = string.len() as INT;
sub_string(ctx, string, start, len)
@ -1348,7 +1348,7 @@ mod string_functions {
if abs_index as u64 > MAX_USIZE_INT as u64 {
return vec![
ctx.engine().get_interned_string("").into(),
ctx.engine().const_empty_string().into(),
string.as_str().into(),
];
}
@ -1357,7 +1357,7 @@ mod string_functions {
let num_chars = string.chars().count();
if abs_index > num_chars {
vec![
ctx.engine().get_interned_string("").into(),
ctx.engine().const_empty_string().into(),
string.as_str().into(),
]
} else {
@ -1368,7 +1368,7 @@ mod string_functions {
} else if index > MAX_USIZE_INT {
vec![
string.as_str().into(),
ctx.engine().get_interned_string("").into(),
ctx.engine().const_empty_string().into(),
]
} else {
let prefix: String = string.chars().take(index as usize).collect();

View File

@ -374,7 +374,7 @@ impl Expr {
fn ensure_bool_expr(self) -> ParseResult<Expr> {
let type_name = match self {
Expr::Unit(..) => "()",
Expr::DynamicConstant(ref v, ..) if !v.is::<bool>() => v.type_name(),
Expr::DynamicConstant(ref v, ..) if !v.is_bool() => v.type_name(),
Expr::IntegerConstant(..) => "a number",
#[cfg(not(feature = "no_float"))]
Expr::FloatConstant(..) => "a floating-point number",
@ -1248,7 +1248,7 @@ impl Engine {
continue;
}
if value.is::<INT>() && !ranges.is_empty() {
if value.is_int() && !ranges.is_empty() {
return Err(PERR::WrongSwitchIntegerCase.into_err(expr.start_position()));
}

View File

@ -370,7 +370,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
}
fn deserialize_option<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if self.0.is::<()>() {
if self.0.is_unit() {
visitor.visit_none()
} else {
visitor.visit_some(self)

View File

@ -1626,6 +1626,194 @@ impl Dynamic {
_ => None,
}
}
/// Return `true` if the [`Dynamic`] holds a `()`.
#[inline]
#[must_use]
pub fn is_unit(&self) -> bool {
match self.0 {
Union::Unit(..) => true,
#[cfg(not(feature = "no_closure"))]
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 {
Union::Unit(..) => true,
_ => false,
},
_ => false,
}
}
/// Return `true` if the [`Dynamic`] holds the system integer type [`INT`].
#[inline]
#[must_use]
pub fn is_int(&self) -> bool {
match self.0 {
Union::Int(..) => true,
#[cfg(not(feature = "no_closure"))]
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 {
Union::Int(..) => true,
_ => false,
},
_ => false,
}
}
/// Return `true` if the [`Dynamic`] holds the system floating-point type [`FLOAT`][crate::FLOAT].
///
/// Not available under `no_float`.
#[cfg(not(feature = "no_float"))]
#[inline]
#[must_use]
pub fn is_float(&self) -> bool {
match self.0 {
Union::Float(..) => true,
#[cfg(not(feature = "no_closure"))]
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 {
Union::Float(..) => true,
_ => false,
},
_ => false,
}
}
/// _(decimal)_ Return `true` if the [`Dynamic`] holds a [`Decimal`][rust_decimal::Decimal].
///
/// Exported under the `decimal` feature only.
#[cfg(feature = "decimal")]
#[inline]
#[must_use]
pub fn is_decimal(&self) -> bool {
match self.0 {
Union::Decimal(..) => true,
#[cfg(not(feature = "no_closure"))]
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 {
Union::Decimal(..) => true,
_ => false,
},
_ => false,
}
}
/// Return `true` if the [`Dynamic`] holds a [`bool`].
#[inline]
#[must_use]
pub fn is_bool(&self) -> bool {
match self.0 {
Union::Bool(..) => true,
#[cfg(not(feature = "no_closure"))]
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 {
Union::Bool(..) => true,
_ => false,
},
_ => false,
}
}
/// Return `true` if the [`Dynamic`] holds a [`char`].
#[inline]
#[must_use]
pub fn is_char(&self) -> bool {
match self.0 {
Union::Char(..) => true,
#[cfg(not(feature = "no_closure"))]
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 {
Union::Char(..) => true,
_ => false,
},
_ => false,
}
}
/// Return `true` if the [`Dynamic`] holds an [`ImmutableString`].
#[inline]
#[must_use]
pub fn is_string(&self) -> bool {
match self.0 {
Union::Str(..) => true,
#[cfg(not(feature = "no_closure"))]
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 {
Union::Str(..) => true,
_ => false,
},
_ => false,
}
}
/// Return `true` if the [`Dynamic`] holds an [`Array`][crate::Array].
///
/// Not available under `no_index`.
#[cfg(not(feature = "no_index"))]
#[inline]
#[must_use]
pub fn is_array(&self) -> bool {
match self.0 {
Union::Array(..) => true,
#[cfg(not(feature = "no_closure"))]
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 {
Union::Array(..) => true,
_ => false,
},
_ => false,
}
}
/// Return `true` if the [`Dynamic`] holds a [`Blob`][crate::Blob].
///
/// Not available under `no_index`.
#[cfg(not(feature = "no_index"))]
#[inline]
#[must_use]
pub fn is_blob(&self) -> bool {
match self.0 {
Union::Blob(..) => true,
#[cfg(not(feature = "no_closure"))]
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 {
Union::Blob(..) => true,
_ => false,
},
_ => false,
}
}
/// Return `true` if the [`Dynamic`] holds a [`Map`][crate::Map].
///
/// Not available under `no_object`.
#[cfg(not(feature = "no_object"))]
#[inline]
#[must_use]
pub fn is_map(&self) -> bool {
match self.0 {
Union::Map(..) => true,
#[cfg(not(feature = "no_closure"))]
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 {
Union::Map(..) => true,
_ => false,
},
_ => false,
}
}
/// Return `true` if the [`Dynamic`] holds a [`FnPtr`].
#[inline]
#[must_use]
pub(crate) fn is_fnptr(&self) -> bool {
match self.0 {
Union::FnPtr(..) => true,
#[cfg(not(feature = "no_closure"))]
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 {
Union::FnPtr(..) => true,
_ => false,
},
_ => false,
}
}
/// Return `true` if the [`Dynamic`] holds a [timestamp][Instant].
///
/// Not available under `no_time`.
#[cfg(not(feature = "no_time"))]
#[inline]
#[must_use]
pub fn is_timestamp(&self) -> bool {
match self.0 {
Union::TimeStamp(..) => true,
#[cfg(not(feature = "no_closure"))]
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 {
Union::TimeStamp(..) => true,
_ => false,
},
_ => false,
}
}
/// Cast the [`Dynamic`] as a unit `()`.
/// Returns the name of the actual type if the cast fails.
#[inline]
@ -1698,6 +1886,7 @@ impl Dynamic {
_ => Err(self.type_name()),
}
}
/// Cast the [`Dynamic`] as a string slice.
/// Returns the name of the actual type if the cast fails.
///
@ -1741,6 +1930,8 @@ impl Dynamic {
}
/// Convert the [`Dynamic`] into an [`Array`][crate::Array].
/// Returns the name of the actual type if the cast fails.
///
/// Not available under `no_index`.
#[cfg(not(feature = "no_index"))]
#[inline(always)]
pub fn into_array(self) -> Result<crate::Array, &'static str> {
@ -1760,6 +1951,8 @@ impl Dynamic {
}
/// Convert the [`Dynamic`] into a [`Vec`].
/// Returns the name of the actual type if any cast fails.
///
/// Not available under `no_index`.
#[cfg(not(feature = "no_index"))]
#[inline(always)]
pub fn into_typed_array<T: Variant + Clone>(self) -> Result<Vec<T>, &'static str> {
@ -1814,6 +2007,8 @@ impl Dynamic {
}
/// Convert the [`Dynamic`] into a [`Blob`][crate::Blob].
/// Returns the name of the actual type if the cast fails.
///
/// Not available under `no_index`.
#[cfg(not(feature = "no_index"))]
#[inline(always)]
pub fn into_blob(self) -> Result<crate::Blob, &'static str> {

View File

@ -174,7 +174,7 @@ impl fmt::Display for EvalAltResult {
Self::ErrorStackOverflow(..) => f.write_str("Stack overflow")?,
Self::ErrorTerminated(..) => f.write_str("Script terminated")?,
Self::ErrorRuntime(d, ..) if d.is::<()>() => f.write_str("Runtime error")?,
Self::ErrorRuntime(d, ..) if d.is_unit() => f.write_str("Runtime error")?,
Self::ErrorRuntime(d, ..)
if d.read_lock::<ImmutableString>()
.map_or(false, |v| v.is_empty()) =>

View File

@ -19,11 +19,11 @@ use rust_decimal::Decimal;
#[test]
fn test_serde_ser_primary_types() -> Result<(), Box<EvalAltResult>> {
assert!(to_dynamic(42_u64)?.is::<INT>());
assert!(to_dynamic(42_u64)?.is_int());
assert!(to_dynamic(u64::MAX)?.is::<u64>());
assert!(to_dynamic(42 as INT)?.is::<INT>());
assert!(to_dynamic(true)?.is::<bool>());
assert!(to_dynamic(())?.is::<()>());
assert!(to_dynamic(42 as INT)?.is_int());
assert!(to_dynamic(true)?.is_bool());
assert!(to_dynamic(())?.is_unit());
#[cfg(not(feature = "no_float"))]
{
@ -45,14 +45,14 @@ fn test_serde_ser_primary_types() -> Result<(), Box<EvalAltResult>> {
#[test]
fn test_serde_ser_integer_types() -> Result<(), Box<EvalAltResult>> {
assert!(to_dynamic(42_i8)?.is::<INT>());
assert!(to_dynamic(42_i16)?.is::<INT>());
assert!(to_dynamic(42_i32)?.is::<INT>());
assert!(to_dynamic(42_i64)?.is::<INT>());
assert!(to_dynamic(42_u8)?.is::<INT>());
assert!(to_dynamic(42_u16)?.is::<INT>());
assert!(to_dynamic(42_u32)?.is::<INT>());
assert!(to_dynamic(42_u64)?.is::<INT>());
assert!(to_dynamic(42_i8)?.is_int());
assert!(to_dynamic(42_i16)?.is_int());
assert!(to_dynamic(42_i32)?.is_int());
assert!(to_dynamic(42_i64)?.is_int());
assert!(to_dynamic(42_u8)?.is_int());
assert!(to_dynamic(42_u16)?.is_int());
assert!(to_dynamic(42_u32)?.is_int());
assert!(to_dynamic(42_u64)?.is_int());
Ok(())
}
@ -63,7 +63,7 @@ fn test_serde_ser_array() -> Result<(), Box<EvalAltResult>> {
let arr: Vec<INT> = vec![123, 456, 42, 999];
let d = to_dynamic(arr)?;
assert!(d.is::<Array>());
assert!(d.is_array());
assert_eq!(4, d.cast::<Array>().len());
Ok(())
@ -94,7 +94,7 @@ fn test_serde_ser_struct() -> Result<(), Box<EvalAltResult>> {
let d = to_dynamic(x)?;
assert!(d.is::<Map>());
assert!(d.is_map());
let mut map = d.cast::<Map>();
let obj = map.remove("obj").unwrap().cast::<Map>();
@ -734,7 +734,7 @@ fn test_serde_json() -> serde_json::Result<()> {
let d2: Dynamic = serde_json::from_str(&json)?;
assert!(d2.is::<Map>());
assert!(d2.is_map());
let mut m = d2.cast::<Map>();

View File

@ -11,7 +11,7 @@ fn test_throw() {
assert!(matches!(
*engine.run(r#"throw"#).expect_err("expects error"),
EvalAltResult::ErrorRuntime(s, ..) if s.is::<()>()
EvalAltResult::ErrorRuntime(s, ..) if s.is_unit()
));
}