use crate::error::InteropError;
use bevy_mod_scripting_derive::DebugWithTypeInfo;
use bevy_mod_scripting_display::{
DisplayWithTypeInfo, GetTypeInfo, ReflectDisplayWithTypeInfo, WithTypeInfo,
};
use bevy_platform::collections::HashMap;
use bevy_reflect::Reflect;
use std::borrow::Cow;
use super::{
ReflectReference,
function::script_function::{DynamicScriptFunction, DynamicScriptFunctionMut},
};
#[derive(Clone, Reflect, Default, DebugWithTypeInfo)]
#[reflect(opaque, DisplayWithTypeInfo)]
#[debug_with_type_info(bms_display_path = "bevy_mod_scripting_display")]
pub enum ScriptValue {
#[default]
Unit,
Bool(bool),
Integer(i64),
Float(f64),
String(Cow<'static, str>),
List(Vec<ScriptValue>),
Map(HashMap<String, ScriptValue>),
Reference(ReflectReference),
FunctionMut(DynamicScriptFunctionMut),
Function(DynamicScriptFunction),
Error(InteropError),
}
impl DisplayWithTypeInfo for ScriptValue {
fn display_with_type_info(
&self,
f: &mut std::fmt::Formatter<'_>,
type_info_provider: Option<&dyn GetTypeInfo>,
) -> std::fmt::Result {
match self {
ScriptValue::Unit => f.write_str("()"),
ScriptValue::Bool(v) => write!(f, "{v}"),
ScriptValue::Integer(v) => write!(f, "{v}"),
ScriptValue::Float(v) => write!(f, "{v}"),
ScriptValue::String(v) => write!(f, "{v}"),
ScriptValue::List(v) => {
f.write_str("[")?;
let mut first = true;
for item in v {
if !first {
f.write_str(", ")?;
}
first = false;
WithTypeInfo::new_with_opt_info(item, type_info_provider)
.display_with_type_info(f, type_info_provider)?;
}
f.write_str("]")
}
ScriptValue::Map(v) => {
f.write_str("{")?;
let mut first = true;
for (key, value) in v {
if !first {
f.write_str(", ")?;
}
first = false;
write!(f, "{key}: ")?;
WithTypeInfo::new_with_opt_info(value, type_info_provider)
.display_with_type_info(f, type_info_provider)?;
}
f.write_str("}")
}
ScriptValue::Reference(v) => v.display_with_type_info(f, type_info_provider),
ScriptValue::FunctionMut(v) => v.display_with_type_info(f, type_info_provider),
ScriptValue::Function(v) => v.display_with_type_info(f, type_info_provider),
ScriptValue::Error(e) => e.display_with_type_info(f, type_info_provider),
}
}
}
impl PartialEq for ScriptValue {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Unit, Self::Unit) => true,
(Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
(Self::Integer(l0), Self::Integer(r0)) => l0 == r0,
(Self::Float(l0), Self::Float(r0)) => l0 == r0,
(Self::String(l0), Self::String(r0)) => l0 == r0,
(Self::List(l0), Self::List(r0)) => l0 == r0,
(Self::Map(l0), Self::Map(r0)) => l0 == r0,
(Self::Reference(l0), Self::Reference(r0)) => l0 == r0,
_ => false,
}
}
}
#[profiling::all_functions]
impl ScriptValue {
pub fn as_string(self) -> Result<Cow<'static, str>, Self> {
match self {
ScriptValue::String(s) => Ok(s),
other => Err(other),
}
}
pub fn type_name(&self) -> String {
match self {
ScriptValue::Unit => "Unit".to_owned(),
ScriptValue::Bool(_) => "Bool".to_owned(),
ScriptValue::Integer(_) => "Integer".to_owned(),
ScriptValue::Float(_) => "Float".to_owned(),
ScriptValue::String(_) => "String".to_owned(),
ScriptValue::List(_) => "List".to_owned(),
ScriptValue::Reference(_) => "Reference".to_owned(),
ScriptValue::FunctionMut(_) => "FunctionMut".to_owned(),
ScriptValue::Function(_) => "Function".to_owned(),
ScriptValue::Error(_) => "Error".to_owned(),
ScriptValue::Map(_) => "Map".to_owned(),
}
}
}
#[profiling::all_functions]
impl From<()> for ScriptValue {
fn from(_: ()) -> Self {
ScriptValue::Unit
}
}
#[profiling::all_functions]
impl From<bool> for ScriptValue {
fn from(value: bool) -> Self {
ScriptValue::Bool(value)
}
}
#[profiling::all_functions]
impl From<i64> for ScriptValue {
fn from(value: i64) -> Self {
ScriptValue::Integer(value)
}
}
#[profiling::all_functions]
impl From<f64> for ScriptValue {
fn from(value: f64) -> Self {
ScriptValue::Float(value)
}
}
#[profiling::all_functions]
impl From<&'static str> for ScriptValue {
fn from(value: &'static str) -> Self {
ScriptValue::String(value.into())
}
}
#[profiling::all_functions]
impl From<String> for ScriptValue {
fn from(value: String) -> Self {
ScriptValue::String(value.into())
}
}
#[profiling::all_functions]
impl From<Cow<'static, str>> for ScriptValue {
fn from(value: Cow<'static, str>) -> Self {
ScriptValue::String(value)
}
}
#[profiling::all_functions]
impl From<Vec<ScriptValue>> for ScriptValue {
fn from(value: Vec<ScriptValue>) -> Self {
ScriptValue::List(value)
}
}
#[profiling::all_functions]
impl From<ReflectReference> for ScriptValue {
fn from(value: ReflectReference) -> Self {
ScriptValue::Reference(value)
}
}
#[profiling::all_functions]
impl From<InteropError> for ScriptValue {
fn from(value: InteropError) -> Self {
ScriptValue::Error(value)
}
}
#[profiling::all_functions]
impl<T: Into<ScriptValue>> From<Option<T>> for ScriptValue {
fn from(value: Option<T>) -> Self {
match value {
Some(v) => v.into(),
None => ScriptValue::Unit,
}
}
}
#[profiling::all_functions]
impl<T: Into<ScriptValue>, E: Into<InteropError>> From<Result<T, E>> for ScriptValue {
fn from(value: Result<T, E>) -> Self {
match value {
Ok(v) => v.into(),
Err(e) => ScriptValue::Error(e.into()),
}
}
}
impl From<HashMap<String, ScriptValue>> for ScriptValue {
fn from(value: HashMap<String, ScriptValue>) -> Self {
ScriptValue::Map(value)
}
}