From 1bbe14ef1aa796020d76cacd849137878c3264fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tin=20=C5=A0vagelj?= Date: Tue, 5 Nov 2024 16:22:21 +0100 Subject: [PATCH 1/4] Cleanup features MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Enabled default features specified in the spec. - Removed patch versions from crates with stable minors. - Added better support for number casting. - Cleaned up imports. Signed-off-by: Tin Švagelj --- example/Cargo.toml | 42 ++++++++++++++++++----------- interpreter/Cargo.toml | 17 +++++++----- interpreter/src/functions.rs | 9 +++---- interpreter/src/lib.rs | 25 +++++++++++++---- interpreter/src/macros.rs | 40 ++++++++++++++++++++++----- interpreter/src/magic.rs | 13 ++++----- interpreter/src/objects.rs | 52 +++++++++++++++++++++--------------- interpreter/src/testing.rs | 9 ------- 8 files changed, 132 insertions(+), 75 deletions(-) delete mode 100644 interpreter/src/testing.rs diff --git a/example/Cargo.toml b/example/Cargo.toml index a0720ef..19704c4 100644 --- a/example/Cargo.toml +++ b/example/Cargo.toml @@ -3,48 +3,60 @@ name = "example" version = "0.1.0" edition = "2021" +[features] +axum = ["dep:axum", "dep:tokio", "dep:thiserror"] +serde = ["dep:serde", "cel-interpreter/serde"] +json = ["dep:serde_json", "cel-interpreter/json"] +chrono = ["dep:chrono", "cel-interpreter/chrono"] + [dependencies] +cel-interpreter = { path = "../interpreter", default-features = false } + +chrono = { version = "0.4", optional = true } + +serde = { version = "1.0", features = ["derive"], optional = true } +serde_json = { version = "1.0", optional = true } + axum = { version = "0.7.5", default-features = false, features = [ "http1", "json", "tokio", -] } -cel-interpreter = { path = "../interpreter", features = ["json", "chrono", "regex"] } -chrono = "0.4.26" -serde = { version = "1.0.196", features = ["derive"] } -serde_json = "1.0.124" -thiserror = { version = "1.0.61", default-features = false } +], optional = true } tokio = { version = "1.38.0", default-features = false, features = [ "macros", "net", "rt-multi-thread", -] } +], optional = true } +thiserror = { version = "1.0", optional = true } [[bin]] -name = "simple" +name = "example-simple" path = "src/simple.rs" [[bin]] -name = "variables" +name = "example-variables" path = "src/variables.rs" [[bin]] -name = "functions" +name = "example-functions" path = "src/functions.rs" +required-features = ["chrono"] [[bin]] -name = "threads" +name = "example-threads" path = "src/threads.rs" [[bin]] -name = "serde" +name = "example-serde" path = "src/serde.rs" +required-features = ["serde"] [[bin]] -name = "axum" +name = "example-axum" path = "src/axum.rs" +required-features = ["axum"] [[bin]] -name = "json" +name = "example-json" path = "src/json.rs" - +required-features = ["json"] diff --git a/interpreter/Cargo.toml b/interpreter/Cargo.toml index fa70cce..581ff8c 100644 --- a/interpreter/Cargo.toml +++ b/interpreter/Cargo.toml @@ -10,15 +10,18 @@ categories = ["compilers"] [dependencies] cel-parser = { path = "../parser", version = "0.8.0" } -thiserror = "1.0.40" -chrono = { version = "0.4.26", default-features = false, features = ["alloc"], optional = true } + nom = "7.1.3" -paste = "1.0.14" -serde = "1.0.196" + +chrono = { version = "0.4", default-features = false, features = ["alloc"], optional = true } regex = { version = "1.10.5", optional = true } -serde_json = { version = "1.0.124", optional = true } +serde = { version = "1.0", optional = true } +serde_json = { version = "1.0", optional = true } base64 = { version = "0.22.1", optional = true } +thiserror = "1.0" +paste = "1.0" + [dev-dependencies] criterion = { version = "0.5.1", features = ["html_reports"] } serde_bytes = "0.11.14" @@ -28,6 +31,8 @@ name = "runtime" harness = false [features] -json = ["dep:base64", "dep:serde_json"] +default = ["regex", "chrono"] +json = ["serde", "dep:serde_json", "dep:base64"] +serde = ["dep:serde"] regex = ["dep:regex"] chrono = ["dep:chrono"] diff --git a/interpreter/src/functions.rs b/interpreter/src/functions.rs index a63cb38..457010f 100644 --- a/interpreter/src/functions.rs +++ b/interpreter/src/functions.rs @@ -645,10 +645,7 @@ pub fn max(Arguments(args): Arguments) -> Result { #[cfg(test)] mod tests { use crate::context::Context; - use crate::testing::test_script; - #[cfg(feature = "regex")] - use crate::ExecutionError::FunctionError; - use std::collections::HashMap; + use crate::tests::test_script; fn assert_script(input: &(&str, &str)) { assert_eq!(test_script(input.1, None), Ok(true.into()), "{}", input.0); @@ -679,7 +676,7 @@ mod tests { for (name, script) in tests { let mut ctx = Context::default(); - ctx.add_variable_from_value("foo", HashMap::from([("bar", 1)])); + ctx.add_variable_from_value("foo", std::collections::HashMap::from([("bar", 1)])); assert_eq!(test_script(script, Some(ctx)), Ok(true.into()), "{}", name); } } @@ -943,7 +940,7 @@ mod tests { test_script( "'foobar'.matches('(foo') == true", None), Err( - FunctionError { + crate::ExecutionError::FunctionError { function: "matches".to_string(), message: "'(foo' not a valid regex:\nregex parse error:\n (foo\n ^\nerror: unclosed group".to_string() } diff --git a/interpreter/src/lib.rs b/interpreter/src/lib.rs index e307f55..0410d9a 100644 --- a/interpreter/src/lib.rs +++ b/interpreter/src/lib.rs @@ -13,19 +13,21 @@ pub use cel_parser::Expression; pub use context::Context; pub use functions::FunctionContext; pub use objects::{ResolveResult, Value}; -#[cfg(feature = "chrono")] -mod duration; pub mod functions; mod magic; pub mod objects; mod resolvers; + +#[cfg(feature = "chrono")] +mod duration; + +#[cfg(feature = "serde")] mod ser; +#[cfg(feature = "serde")] pub use ser::to_value; #[cfg(feature = "json")] mod json; -#[cfg(test)] -mod testing; use magic::FromContext; @@ -45,6 +47,14 @@ pub enum ExecutionError { /// but the type of the value was not supported as a key. #[error("Unable to use value '{0:?}' as a key")] UnsupportedKeyType(Value), + #[error( + "Casting number '{value:.2}' ({source_ty}) to {target_ty} type would cause an overflow" + )] + CastOverflow { + value: f64, + source_ty: &'static str, + target_ty: &'static str, + }, #[error("Unexpected type: got '{got}', want '{want}'")] UnexpectedType { got: String, want: String }, /// Indicates that the script attempted to reference a key on a type that @@ -173,11 +183,16 @@ impl TryFrom<&str> for Program { mod tests { use crate::context::Context; use crate::objects::{ResolveResult, Value}; - use crate::testing::test_script; use crate::{ExecutionError, Program}; use std::collections::HashMap; use std::convert::TryInto; + /// Tests the provided script and returns the result. An optional context can be provided. + pub(crate) fn test_script(script: &str, ctx: Option) -> ResolveResult { + let program = Program::compile(script).unwrap(); + program.execute(&ctx.unwrap_or_default()) + } + #[test] fn parse() { Program::compile("1 + 1").unwrap(); diff --git a/interpreter/src/macros.rs b/interpreter/src/macros.rs index 08776ee..39cd565 100644 --- a/interpreter/src/macros.rs +++ b/interpreter/src/macros.rs @@ -1,12 +1,25 @@ #[macro_export] macro_rules! impl_conversions { // Capture pairs separated by commas, where each pair is separated by => - ($($target_type:ty => $value_variant:path),* $(,)?) => { + ($($target_type:ty $(as $cast:ty)? => $value_variant:path),* $(,)?) => { $( impl FromValue for $target_type { fn from_value(expr: &Value) -> Result { if let $value_variant(v) = expr { - Ok(v.clone()) + $(if <$target_type>::MAX as $cast < *v { + return Err(ExecutionError::CastOverflow { + value: *v as f64, + source_ty: std::any::type_name::<$cast>(), + target_ty: std::any::type_name::<$target_type>(), + }) + } else if <$target_type>::MIN as $cast > *v { + return Err(ExecutionError::CastOverflow { + value: *v as f64, + source_ty: std::any::type_name::<$cast>(), + target_ty: std::any::type_name::<$target_type>(), + }) + })? + Ok(v.clone() $(as $cast as $target_type)?) } else { Err(ExecutionError::UnexpectedType { got: format!("{:?}", expr), @@ -20,7 +33,22 @@ macro_rules! impl_conversions { fn from_value(expr: &Value) -> Result { match expr { Value::Null => Ok(None), - $value_variant(v) => Ok(Some(v.clone())), + $value_variant(v) => { + $(if <$target_type>::MAX as $cast < *v { + return Err(ExecutionError::CastOverflow { + value: *v as f64, + source_ty: std::any::type_name::<$cast>(), + target_ty: std::any::type_name::<$target_type>(), + }) + } else if <$target_type>::MIN as $cast > *v { + return Err(ExecutionError::CastOverflow { + value: *v as f64, + source_ty: std::any::type_name::<$cast>(), + target_ty: std::any::type_name::<$target_type>(), + }) + })? + Ok(Some(v.clone() $(as $cast as $target_type)?)) + }, _ => Err(ExecutionError::UnexpectedType { got: format!("{:?}", expr), want: stringify!($target_type).to_string(), @@ -31,19 +59,19 @@ macro_rules! impl_conversions { impl From<$target_type> for Value { fn from(value: $target_type) -> Self { - $value_variant(value) + $value_variant(value $(as $cast)?) } } impl $crate::magic::IntoResolveResult for $target_type { fn into_resolve_result(self) -> ResolveResult { - Ok($value_variant(self)) + Ok($value_variant(self $(as $cast)?)) } } impl $crate::magic::IntoResolveResult for Result<$target_type, ExecutionError> { fn into_resolve_result(self) -> ResolveResult { - self.map($value_variant) + self.map(|it| $value_variant(it $(as $cast)?)) } } diff --git a/interpreter/src/magic.rs b/interpreter/src/magic.rs index 0b8a373..31bb436 100644 --- a/interpreter/src/magic.rs +++ b/interpreter/src/magic.rs @@ -7,8 +7,15 @@ use std::marker::PhantomData; use std::sync::Arc; impl_conversions!( + i8 as i64 => Value::Int, + i16 as i64 => Value::Int, + i32 as i64 => Value::Int, i64 => Value::Int, + u8 as u64 => Value::UInt, + u16 as u64 => Value::UInt, + u32 as u64 => Value::UInt, u64 => Value::UInt, + f32 as f64 => Value::Float, f64 => Value::Float, Arc => Value::String, Arc> => Value::Bytes, @@ -22,12 +29,6 @@ impl_conversions!( chrono::DateTime => Value::Timestamp, ); -impl From for Value { - fn from(value: i32) -> Self { - Value::Int(value as i64) - } -} - /// Describes any type that can be converted from a [`Value`] into itself. /// This is commonly used to convert from [`Value`] into primitive types, /// e.g. from `Value::Bool(true) -> true`. This trait is auto-implemented diff --git a/interpreter/src/objects.rs b/interpreter/src/objects.rs index 50708d8..96e2906 100644 --- a/interpreter/src/objects.rs +++ b/interpreter/src/objects.rs @@ -1,15 +1,12 @@ use crate::context::Context; use crate::functions::FunctionContext; -use crate::ser::SerializationError; -use crate::ExecutionError::NoSuchKey; -use crate::{to_value, ExecutionError}; -use cel_parser::{ArithmeticOp, Atom, Expression, Member, RelationOp, UnaryOp}; -use core::ops; -use serde::{Serialize, Serializer}; +use crate::ExecutionError; +use cel_parser::ast::*; use std::cmp::Ordering; use std::collections::HashMap; use std::convert::{Infallible, TryFrom, TryInto}; use std::fmt::{Display, Formatter}; +use std::ops; use std::sync::Arc; #[derive(Debug, PartialEq, Clone)] @@ -84,10 +81,11 @@ impl From for Key { } } -impl Serialize for Key { +#[cfg(feature = "serde")] +impl serde::Serialize for Key { fn serialize(&self, serializer: S) -> Result where - S: Serializer, + S: serde::Serializer, { match self { Key::Int(v) => v.serialize(serializer), @@ -143,19 +141,28 @@ pub trait TryIntoValue { fn try_into_value(self) -> Result; } -impl TryIntoValue for T { - type Error = SerializationError; +#[cfg(feature = "serde")] +impl TryIntoValue for T { + type Error = crate::ser::SerializationError; fn try_into_value(self) -> Result { - to_value(self) + crate::ser::to_value(self) } } - +#[cfg(feature = "serde")] impl TryIntoValue for Value { type Error = Infallible; fn try_into_value(self) -> Result { Ok(self) } } +#[cfg(not(feature = "serde"))] +impl> TryIntoValue for T { + type Error = Infallible; + + fn try_into_value(self) -> Result { + Ok(self.into()) + } +} #[derive(Debug, Clone)] pub enum Value { @@ -351,16 +358,17 @@ impl From<&Key> for Key { } // Convert Vec to Value -impl> From> for Value { +impl + 'static> From> for Value { fn from(v: Vec) -> Self { - Value::List(v.into_iter().map(|v| v.into()).collect::>().into()) - } -} - -// Convert Vec to Value -impl From> for Value { - fn from(v: Vec) -> Self { - Value::Bytes(v.into()) + if std::any::TypeId::of::>() == std::any::TypeId::of::>() { + Value::Bytes(Arc::new(unsafe { + // SAFETY: Checked Vec is Vec, so they're the same type, + // just the compiler can't tell without specialization. + std::mem::transmute::, Vec>(v) + })) + } else { + Value::List(v.into_iter().map(|v| v.into()).collect::>().into()) + } } } @@ -629,7 +637,7 @@ impl<'a> Value { // give priority to the property. Maybe we can implement lookahead // to see if the next token is a function call? match (child, ctx.has_function(&***name)) { - (None, false) => NoSuchKey(name.clone()).into(), + (None, false) => ExecutionError::NoSuchKey(name.clone()).into(), (Some(child), _) => child.into(), (None, true) => Value::Function(name.clone(), Some(self.into())).into(), } diff --git a/interpreter/src/testing.rs b/interpreter/src/testing.rs deleted file mode 100644 index f814652..0000000 --- a/interpreter/src/testing.rs +++ /dev/null @@ -1,9 +0,0 @@ -use crate::context::Context; -use crate::objects::ResolveResult; -use crate::Program; - -/// Tests the provided script and returns the result. An optional context can be provided. -pub(crate) fn test_script(script: &str, ctx: Option) -> ResolveResult { - let program = Program::compile(script).unwrap(); - program.execute(&ctx.unwrap_or_default()) -} From 038468f10dfce081509265b34b00fee7c6d51179 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tin=20=C5=A0vagelj?= Date: Tue, 5 Nov 2024 17:30:07 +0100 Subject: [PATCH 2/4] Enable serde by default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tin Švagelj --- interpreter/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interpreter/Cargo.toml b/interpreter/Cargo.toml index 581ff8c..9943ad8 100644 --- a/interpreter/Cargo.toml +++ b/interpreter/Cargo.toml @@ -31,7 +31,7 @@ name = "runtime" harness = false [features] -default = ["regex", "chrono"] +default = ["regex", "chrono", "serde"] json = ["serde", "dep:serde_json", "dep:base64"] serde = ["dep:serde"] regex = ["dep:regex"] From 17b56fe9ebe314702eab08bf1034568d07a900f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tin=20=C5=A0vagelj?= Date: Tue, 5 Nov 2024 18:51:43 +0100 Subject: [PATCH 3/4] Add type constraint for Value casting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tin Švagelj --- interpreter/src/objects.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/interpreter/src/objects.rs b/interpreter/src/objects.rs index 96e2906..67fdbd2 100644 --- a/interpreter/src/objects.rs +++ b/interpreter/src/objects.rs @@ -968,4 +968,20 @@ mod tests { let result = program.execute(&context); assert_eq!(result.unwrap(), Value::Null); } + + #[test] + fn reference_to_value() { + let test = "example".to_string(); + let direct: Value = test.as_str().into(); + assert_eq!(direct, Value::String(Arc::new(String::from("example")))); + + let vec = vec![test.as_str()]; + let indirect: Value = vec.into(); + assert_eq!( + indirect, + Value::List(Arc::new(vec![Value::String(Arc::new(String::from( + "example" + )))])) + ); + } } From 30a545a7ad02d703adb03c399b5228c16833bd4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tin=20=C5=A0vagelj?= Date: Tue, 5 Nov 2024 19:23:18 +0100 Subject: [PATCH 4/4] Revert serde separation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tin Švagelj --- example/Cargo.toml | 4 +--- interpreter/Cargo.toml | 7 +++---- interpreter/src/lib.rs | 10 ---------- interpreter/src/macros.rs | 40 ++++++-------------------------------- interpreter/src/magic.rs | 13 ++++++------- interpreter/src/objects.rs | 30 +++++++++------------------- 6 files changed, 25 insertions(+), 79 deletions(-) diff --git a/example/Cargo.toml b/example/Cargo.toml index 19704c4..5751eee 100644 --- a/example/Cargo.toml +++ b/example/Cargo.toml @@ -5,7 +5,6 @@ edition = "2021" [features] axum = ["dep:axum", "dep:tokio", "dep:thiserror"] -serde = ["dep:serde", "cel-interpreter/serde"] json = ["dep:serde_json", "cel-interpreter/json"] chrono = ["dep:chrono", "cel-interpreter/chrono"] @@ -14,7 +13,7 @@ cel-interpreter = { path = "../interpreter", default-features = false } chrono = { version = "0.4", optional = true } -serde = { version = "1.0", features = ["derive"], optional = true } +serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", optional = true } axum = { version = "0.7.5", default-features = false, features = [ @@ -49,7 +48,6 @@ path = "src/threads.rs" [[bin]] name = "example-serde" path = "src/serde.rs" -required-features = ["serde"] [[bin]] name = "example-axum" diff --git a/interpreter/Cargo.toml b/interpreter/Cargo.toml index 9943ad8..45a1379 100644 --- a/interpreter/Cargo.toml +++ b/interpreter/Cargo.toml @@ -15,7 +15,7 @@ nom = "7.1.3" chrono = { version = "0.4", default-features = false, features = ["alloc"], optional = true } regex = { version = "1.10.5", optional = true } -serde = { version = "1.0", optional = true } +serde = "1.0" serde_json = { version = "1.0", optional = true } base64 = { version = "0.22.1", optional = true } @@ -31,8 +31,7 @@ name = "runtime" harness = false [features] -default = ["regex", "chrono", "serde"] -json = ["serde", "dep:serde_json", "dep:base64"] -serde = ["dep:serde"] +default = ["regex", "chrono"] +json = ["dep:serde_json", "dep:base64"] regex = ["dep:regex"] chrono = ["dep:chrono"] diff --git a/interpreter/src/lib.rs b/interpreter/src/lib.rs index 0410d9a..b11ff47 100644 --- a/interpreter/src/lib.rs +++ b/interpreter/src/lib.rs @@ -21,9 +21,7 @@ mod resolvers; #[cfg(feature = "chrono")] mod duration; -#[cfg(feature = "serde")] mod ser; -#[cfg(feature = "serde")] pub use ser::to_value; #[cfg(feature = "json")] @@ -47,14 +45,6 @@ pub enum ExecutionError { /// but the type of the value was not supported as a key. #[error("Unable to use value '{0:?}' as a key")] UnsupportedKeyType(Value), - #[error( - "Casting number '{value:.2}' ({source_ty}) to {target_ty} type would cause an overflow" - )] - CastOverflow { - value: f64, - source_ty: &'static str, - target_ty: &'static str, - }, #[error("Unexpected type: got '{got}', want '{want}'")] UnexpectedType { got: String, want: String }, /// Indicates that the script attempted to reference a key on a type that diff --git a/interpreter/src/macros.rs b/interpreter/src/macros.rs index 39cd565..08776ee 100644 --- a/interpreter/src/macros.rs +++ b/interpreter/src/macros.rs @@ -1,25 +1,12 @@ #[macro_export] macro_rules! impl_conversions { // Capture pairs separated by commas, where each pair is separated by => - ($($target_type:ty $(as $cast:ty)? => $value_variant:path),* $(,)?) => { + ($($target_type:ty => $value_variant:path),* $(,)?) => { $( impl FromValue for $target_type { fn from_value(expr: &Value) -> Result { if let $value_variant(v) = expr { - $(if <$target_type>::MAX as $cast < *v { - return Err(ExecutionError::CastOverflow { - value: *v as f64, - source_ty: std::any::type_name::<$cast>(), - target_ty: std::any::type_name::<$target_type>(), - }) - } else if <$target_type>::MIN as $cast > *v { - return Err(ExecutionError::CastOverflow { - value: *v as f64, - source_ty: std::any::type_name::<$cast>(), - target_ty: std::any::type_name::<$target_type>(), - }) - })? - Ok(v.clone() $(as $cast as $target_type)?) + Ok(v.clone()) } else { Err(ExecutionError::UnexpectedType { got: format!("{:?}", expr), @@ -33,22 +20,7 @@ macro_rules! impl_conversions { fn from_value(expr: &Value) -> Result { match expr { Value::Null => Ok(None), - $value_variant(v) => { - $(if <$target_type>::MAX as $cast < *v { - return Err(ExecutionError::CastOverflow { - value: *v as f64, - source_ty: std::any::type_name::<$cast>(), - target_ty: std::any::type_name::<$target_type>(), - }) - } else if <$target_type>::MIN as $cast > *v { - return Err(ExecutionError::CastOverflow { - value: *v as f64, - source_ty: std::any::type_name::<$cast>(), - target_ty: std::any::type_name::<$target_type>(), - }) - })? - Ok(Some(v.clone() $(as $cast as $target_type)?)) - }, + $value_variant(v) => Ok(Some(v.clone())), _ => Err(ExecutionError::UnexpectedType { got: format!("{:?}", expr), want: stringify!($target_type).to_string(), @@ -59,19 +31,19 @@ macro_rules! impl_conversions { impl From<$target_type> for Value { fn from(value: $target_type) -> Self { - $value_variant(value $(as $cast)?) + $value_variant(value) } } impl $crate::magic::IntoResolveResult for $target_type { fn into_resolve_result(self) -> ResolveResult { - Ok($value_variant(self $(as $cast)?)) + Ok($value_variant(self)) } } impl $crate::magic::IntoResolveResult for Result<$target_type, ExecutionError> { fn into_resolve_result(self) -> ResolveResult { - self.map(|it| $value_variant(it $(as $cast)?)) + self.map($value_variant) } } diff --git a/interpreter/src/magic.rs b/interpreter/src/magic.rs index 31bb436..0b8a373 100644 --- a/interpreter/src/magic.rs +++ b/interpreter/src/magic.rs @@ -7,15 +7,8 @@ use std::marker::PhantomData; use std::sync::Arc; impl_conversions!( - i8 as i64 => Value::Int, - i16 as i64 => Value::Int, - i32 as i64 => Value::Int, i64 => Value::Int, - u8 as u64 => Value::UInt, - u16 as u64 => Value::UInt, - u32 as u64 => Value::UInt, u64 => Value::UInt, - f32 as f64 => Value::Float, f64 => Value::Float, Arc => Value::String, Arc> => Value::Bytes, @@ -29,6 +22,12 @@ impl_conversions!( chrono::DateTime => Value::Timestamp, ); +impl From for Value { + fn from(value: i32) -> Self { + Value::Int(value as i64) + } +} + /// Describes any type that can be converted from a [`Value`] into itself. /// This is commonly used to convert from [`Value`] into primitive types, /// e.g. from `Value::Bool(true) -> true`. This trait is auto-implemented diff --git a/interpreter/src/objects.rs b/interpreter/src/objects.rs index 67fdbd2..6c8ccc2 100644 --- a/interpreter/src/objects.rs +++ b/interpreter/src/objects.rs @@ -81,7 +81,6 @@ impl From for Key { } } -#[cfg(feature = "serde")] impl serde::Serialize for Key { fn serialize(&self, serializer: S) -> Result where @@ -141,28 +140,18 @@ pub trait TryIntoValue { fn try_into_value(self) -> Result; } -#[cfg(feature = "serde")] impl TryIntoValue for T { type Error = crate::ser::SerializationError; fn try_into_value(self) -> Result { crate::ser::to_value(self) } } -#[cfg(feature = "serde")] impl TryIntoValue for Value { type Error = Infallible; fn try_into_value(self) -> Result { Ok(self) } } -#[cfg(not(feature = "serde"))] -impl> TryIntoValue for T { - type Error = Infallible; - - fn try_into_value(self) -> Result { - Ok(self.into()) - } -} #[derive(Debug, Clone)] pub enum Value { @@ -358,17 +347,16 @@ impl From<&Key> for Key { } // Convert Vec to Value -impl + 'static> From> for Value { +impl> From> for Value { fn from(v: Vec) -> Self { - if std::any::TypeId::of::>() == std::any::TypeId::of::>() { - Value::Bytes(Arc::new(unsafe { - // SAFETY: Checked Vec is Vec, so they're the same type, - // just the compiler can't tell without specialization. - std::mem::transmute::, Vec>(v) - })) - } else { - Value::List(v.into_iter().map(|v| v.into()).collect::>().into()) - } + Value::List(v.into_iter().map(|v| v.into()).collect::>().into()) + } +} + +// Convert Vec to Value +impl From> for Value { + fn from(v: Vec) -> Self { + Value::Bytes(v.into()) } }