diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f785f8c..dbd2fae6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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::()`. + ### 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` diff --git a/src/api/build_type.rs b/src/api/build_type.rs index 9ce59ba4..2ebab673 100644 --- a/src/api/build_type.rs +++ b/src/api/build_type.rs @@ -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, diff --git a/src/api/custom_syntax.rs b/src/api/custom_syntax.rs index a12ea593..b884cc24 100644 --- a/src/api/custom_syntax.rs +++ b/src/api/custom_syntax.rs @@ -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, diff --git a/src/api/json.rs b/src/api/json.rs index c22d682c..4365a4a8 100644 --- a/src/api/json.rs +++ b/src/api/json.rs @@ -166,7 +166,7 @@ pub fn format_map_as_json(map: &Map) -> String { if let Some(val) = value.read_lock::() { 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(); diff --git a/src/bin/rhai-repl.rs b/src/bin/rhai-repl.rs index 71801d82..260f7de8 100644 --- a/src/bin/rhai-repl.rs +++ b/src/bin/rhai-repl.rs @@ -559,7 +559,7 @@ fn main() { // Evaluate engine.eval_ast_with_scope::(&mut scope, &main_ast) }) { - Ok(result) if !result.is::<()>() => { + Ok(result) if !result.is_unit() => { println!("=> {result:?}"); println!(); } diff --git a/src/eval/chaining.rs b/src/eval/chaining.rs index ca813d86..231e915a 100644 --- a/src/eval/chaining.rs +++ b/src/eval/chaining.rs @@ -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::() && 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::() => { + 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::() => + if target.is_map() => { let _node = &x.lhs; diff --git a/src/eval/expr.rs b/src/eval/expr.rs index 0c18772a..b691eef1 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -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) diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 9a086ed9..f2d0088a 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -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::().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::(); - #[cfg(feature = "no_closure")] - let is_string = rhs_val.is::(); - - 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::() && !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::().ok_or_else(|| { - self.make_type_mismatch_err::(typ, expr.position()) + let path = v.try_cast::().ok_or_else(|| { + self.make_type_mismatch_err::(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() { diff --git a/src/func/call.rs b/src/func/call.rs index 38020404..5f209345 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -145,7 +145,7 @@ impl Engine { format!( "{fn_name} ({})", args.iter() - .map(|a| if a.is::() { + .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::() && args[1].is::() => + if args.len() == 2 && args[0].is_fnptr() && args[1].is_int() => { let fn_name = args[0].read_lock::().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::() => { + KEYWORD_FN_PTR_CALL if target.is_fnptr() => { // FnPtr call let fn_ptr = target.read_lock::().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::(typ, fn_call_pos)); - } else if !call_args[0].is::() { + } 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::(typ, first_arg_pos)); } @@ -827,7 +827,7 @@ impl Engine { ) } KEYWORD_FN_PTR_CURRY => { - if !target.is::() { + if !target.is_fnptr() { let typ = self.map_type_name(target.type_name()); return Err(self.make_type_mismatch_err::(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::() { + if !arg_value.is_fnptr() { let typ = self.map_type_name(arg_value.type_name()); return Err(self.make_type_mismatch_err::(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::() { + if !arg_value.is_fnptr() { let typ = self.map_type_name(arg_value.type_name()); return Err(self.make_type_mismatch_err::(typ, arg_pos)); } diff --git a/src/func/mod.rs b/src/func/mod.rs index d5659854..0b31f72b 100644 --- a/src/func/mod.rs +++ b/src/func/mod.rs @@ -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, diff --git a/src/func/native.rs b/src/func/native.rs index de74f6c6..a6501803 100644 --- a/src/func/native.rs +++ b/src/func/native.rs @@ -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(), diff --git a/src/func/script.rs b/src/func/script.rs index db3fa542..734acace 100644 --- a/src/func/script.rs +++ b/src/func/script.rs @@ -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() diff --git a/src/lib.rs b/src/lib.rs index b4c33fc6..7af804f2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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; diff --git a/src/optimizer.rs b/src/optimizer.rs index 9bf57005..8812db59 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -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::() && !ranges.is_empty() { + if value.is_int() && !ranges.is_empty() { let value = value.as_int().unwrap(); // Only one range or all ranges without conditions diff --git a/src/packages/string_basic.rs b/src/packages/string_basic.rs index d9b0d6c8..63218d2b 100644 --- a/src/packages/string_basic.rs +++ b/src/packages/string_basic.rs @@ -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::() => { + 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")] diff --git a/src/packages/string_more.rs b/src/packages/string_more.rs index 72c4a6ca..bc39f8cb 100644 --- a/src/packages/string_more.rs +++ b/src/packages/string_more.rs @@ -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(); diff --git a/src/parser.rs b/src/parser.rs index fed55ac3..b57da9c3 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -374,7 +374,7 @@ impl Expr { fn ensure_bool_expr(self) -> ParseResult { let type_name = match self { Expr::Unit(..) => "()", - Expr::DynamicConstant(ref v, ..) if !v.is::() => 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::() && !ranges.is_empty() { + if value.is_int() && !ranges.is_empty() { return Err(PERR::WrongSwitchIntegerCase.into_err(expr.start_position())); } diff --git a/src/serde/de.rs b/src/serde/de.rs index 42d4739f..1e3f3664 100644 --- a/src/serde/de.rs +++ b/src/serde/de.rs @@ -370,7 +370,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> { } fn deserialize_option>(self, visitor: V) -> RhaiResultOf { - if self.0.is::<()>() { + if self.0.is_unit() { visitor.visit_none() } else { visitor.visit_some(self) diff --git a/src/types/dynamic.rs b/src/types/dynamic.rs index 8e4ad09a..a3139dde 100644 --- a/src/types/dynamic.rs +++ b/src/types/dynamic.rs @@ -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 { @@ -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(self) -> Result, &'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 { diff --git a/src/types/error.rs b/src/types/error.rs index 906f32c9..1a8035e5 100644 --- a/src/types/error.rs +++ b/src/types/error.rs @@ -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::() .map_or(false, |v| v.is_empty()) => diff --git a/tests/serde.rs b/tests/serde.rs index 4a08bb81..28f3e1bb 100644 --- a/tests/serde.rs +++ b/tests/serde.rs @@ -19,11 +19,11 @@ use rust_decimal::Decimal; #[test] fn test_serde_ser_primary_types() -> Result<(), Box> { - assert!(to_dynamic(42_u64)?.is::()); + assert!(to_dynamic(42_u64)?.is_int()); assert!(to_dynamic(u64::MAX)?.is::()); - assert!(to_dynamic(42 as INT)?.is::()); - assert!(to_dynamic(true)?.is::()); - 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> { #[test] fn test_serde_ser_integer_types() -> Result<(), Box> { - assert!(to_dynamic(42_i8)?.is::()); - assert!(to_dynamic(42_i16)?.is::()); - assert!(to_dynamic(42_i32)?.is::()); - assert!(to_dynamic(42_i64)?.is::()); - assert!(to_dynamic(42_u8)?.is::()); - assert!(to_dynamic(42_u16)?.is::()); - assert!(to_dynamic(42_u32)?.is::()); - assert!(to_dynamic(42_u64)?.is::()); + 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> { let arr: Vec = vec![123, 456, 42, 999]; let d = to_dynamic(arr)?; - assert!(d.is::()); + assert!(d.is_array()); assert_eq!(4, d.cast::().len()); Ok(()) @@ -94,7 +94,7 @@ fn test_serde_ser_struct() -> Result<(), Box> { let d = to_dynamic(x)?; - assert!(d.is::()); + assert!(d.is_map()); let mut map = d.cast::(); let obj = map.remove("obj").unwrap().cast::(); @@ -734,7 +734,7 @@ fn test_serde_json() -> serde_json::Result<()> { let d2: Dynamic = serde_json::from_str(&json)?; - assert!(d2.is::()); + assert!(d2.is_map()); let mut m = d2.cast::(); diff --git a/tests/throw.rs b/tests/throw.rs index caf2fed9..b83dbce2 100644 --- a/tests/throw.rs +++ b/tests/throw.rs @@ -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() )); }