From 840f1784b71cbf76b1207f813679c3d212c1c80b Mon Sep 17 00:00:00 2001 From: IFcoltransG <47414286+IFcoltransG@users.noreply.github.com> Date: Thu, 5 Dec 2024 15:11:26 +1300 Subject: [PATCH 1/3] Make Value and ValueTypes conversions into generic functions/traits --- lib/src/json/json_read.rs | 14 +-- lib/src/json/json_read_stream.rs | 14 +-- lib/src/list_definitions_origin.rs | 2 +- lib/src/native_function_call.rs | 156 ++++++++++++++-------------- lib/src/story/control_logic.rs | 28 ++--- lib/src/story/external_functions.rs | 2 +- lib/src/story_state.rs | 23 ++-- lib/src/value.rs | 101 ++++++------------ lib/src/value_type.rs | 117 +++++++++++++++------ lib/src/variables_state.rs | 2 +- lib/tests/function_test.rs | 2 +- lib/tests/runtime_test.rs | 14 +-- 12 files changed, 243 insertions(+), 232 deletions(-) diff --git a/lib/src/json/json_read.rs b/lib/src/json/json_read.rs index 4bc831b..72f72a2 100644 --- a/lib/src/json/json_read.rs +++ b/lib/src/json/json_read.rs @@ -94,14 +94,14 @@ pub fn jtoken_to_runtime_object( "Failed to convert token to runtime RTObject: {}", token ))), - serde_json::Value::Bool(value) => Ok(Rc::new(Value::new_bool(value.to_owned()))), + serde_json::Value::Bool(value) => Ok(Rc::new(Value::new::(value.to_owned()))), serde_json::Value::Number(_) => { if token.is_i64() { let val: i32 = token.as_i64().unwrap().try_into().unwrap(); - Ok(Rc::new(Value::new_int(val))) + Ok(Rc::new(Value::new::(val))) } else { let val: f32 = token.as_f64().unwrap() as f32; - Ok(Rc::new(Value::new_float(val))) + Ok(Rc::new(Value::new::(val))) } } @@ -111,9 +111,9 @@ pub fn jtoken_to_runtime_object( // String value let first_char = str.chars().next().unwrap(); if first_char == '^' { - return Ok(Rc::new(Value::new_string(&str[1..]))); + return Ok(Rc::new(Value::new::<&str>(&str[1..]))); } else if first_char == '\n' && str.len() == 1 { - return Ok(Rc::new(Value::new_string("\n"))); + return Ok(Rc::new(Value::new::<&str>("\n"))); } // Glue @@ -153,7 +153,7 @@ pub fn jtoken_to_runtime_object( let prop_value = obj.get("^->"); if let Some(prop_value) = prop_value { - return Ok(Rc::new(Value::new_divert_target( + return Ok(Rc::new(Value::new::( Path::new_with_components_string(prop_value.as_str()), ))); } @@ -336,7 +336,7 @@ pub fn jtoken_to_runtime_object( raw_list.items.insert(item, v.as_i64().unwrap() as i32); } - return Ok(Rc::new(Value::new_list(raw_list))); + return Ok(Rc::new(Value::new::(raw_list))); } // Used when serialising save state only diff --git a/lib/src/json/json_read_stream.rs b/lib/src/json/json_read_stream.rs index 01f52d5..b6aeafd 100644 --- a/lib/src/json/json_read_stream.rs +++ b/lib/src/json/json_read_stream.rs @@ -123,14 +123,14 @@ fn jtoken_to_runtime_object( ) -> Result { match value { JsonValue::Null => Ok(ArrayElement::NullElement), - JsonValue::Boolean(value) => Ok(ArrayElement::RTObject(Rc::new(Value::new_bool(value)))), + JsonValue::Boolean(value) => Ok(ArrayElement::RTObject(Rc::new(Value::new::(value)))), JsonValue::Number(value) => { if value.is_integer() { let val: i32 = value.as_integer().unwrap(); - Ok(ArrayElement::RTObject(Rc::new(Value::new_int(val)))) + Ok(ArrayElement::RTObject(Rc::new(Value::new::(val)))) } else { let val: f32 = value.as_float().unwrap(); - Ok(ArrayElement::RTObject(Rc::new(Value::new_float(val)))) + Ok(ArrayElement::RTObject(Rc::new(Value::new::(val)))) } } JsonValue::String(value) => { @@ -139,11 +139,11 @@ fn jtoken_to_runtime_object( // String value let first_char = str.chars().next().unwrap(); if first_char == '^' { - return Ok(ArrayElement::RTObject(Rc::new(Value::new_string( + return Ok(ArrayElement::RTObject(Rc::new(Value::new::<&str>( &str[1..], )))); } else if first_char == '\n' && str.len() == 1 { - return Ok(ArrayElement::RTObject(Rc::new(Value::new_string("\n")))); + return Ok(ArrayElement::RTObject(Rc::new(Value::new::<&str>("\n")))); } // Glue @@ -185,7 +185,7 @@ fn jtoken_to_runtime_object( // Divert target value to path if prop == "^->" { tok.expect('}')?; - return Ok(ArrayElement::RTObject(Rc::new(Value::new_divert_target( + return Ok(ArrayElement::RTObject(Rc::new(Value::new::( Path::new_with_components_string(prop_value.as_str()), )))); } @@ -375,7 +375,7 @@ fn jtoken_to_runtime_object( } tok.expect('}')?; - return Ok(ArrayElement::RTObject(Rc::new(Value::new_list(raw_list)))); + return Ok(ArrayElement::RTObject(Rc::new(Value::new::(raw_list)))); } // Used when serialising save state only diff --git a/lib/src/list_definitions_origin.rs b/lib/src/list_definitions_origin.rs index d29062d..53e4c0d 100644 --- a/lib/src/list_definitions_origin.rs +++ b/lib/src/list_definitions_origin.rs @@ -24,7 +24,7 @@ impl ListDefinitionsOrigin { let mut l = InkList::new(); l.items.insert(key.clone(), *val); - let list_value = Rc::new(Value::new_list(l)); + let list_value = Rc::new(Value::new::(l)); list_definitions_origin .all_unambiguous_list_value_cache diff --git a/lib/src/native_function_call.rs b/lib/src/native_function_call.rs index 33f3863..d88ec9d 100644 --- a/lib/src/native_function_call.rs +++ b/lib/src/native_function_call.rs @@ -265,7 +265,7 @@ impl NativeFunctionCall { } }; - return Ok(Rc::new(Value::new_bool(result))); + return Ok(Rc::new(Value::new::(result))); } // Normal (list • list) operation @@ -315,7 +315,7 @@ impl NativeFunctionCall { } } - Rc::new(Value::new_list(result_raw_list)) + Rc::new(Value::new::(result_raw_list)) } fn call_type(&self, coerced_params: Vec>) -> Result, StoryError> { @@ -397,25 +397,25 @@ impl NativeFunctionCall { fn and_op(&self, params: &[Rc]) -> Result, StoryError> { match ¶ms[0].value { ValueType::Bool(op1) => match params[1].value { - ValueType::Bool(op2) => Ok(Rc::new(Value::new_bool(*op1 && op2))), + ValueType::Bool(op2) => Ok(Rc::new(Value::new::(*op1 && op2))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), }, ValueType::Int(op1) => match params[1].value { - ValueType::Int(op2) => Ok(Rc::new(Value::new_bool(*op1 != 0 && op2 != 0))), + ValueType::Int(op2) => Ok(Rc::new(Value::new::(*op1 != 0 && op2 != 0))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), }, ValueType::Float(op1) => match params[1].value { - ValueType::Float(op2) => Ok(Rc::new(Value::new_bool(*op1 != 0.0 && op2 != 0.0))), + ValueType::Float(op2) => Ok(Rc::new(Value::new::(*op1 != 0.0 && op2 != 0.0))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), }, ValueType::List(op1) => match ¶ms[1].value { - ValueType::List(op2) => Ok(Rc::new(Value::new_bool( + ValueType::List(op2) => Ok(Rc::new(Value::new::( !op1.items.is_empty() && !op2.items.is_empty(), ))), _ => Err(StoryError::InvalidStoryState( @@ -431,19 +431,19 @@ impl NativeFunctionCall { fn greater_op(&self, params: &[Rc]) -> Result, StoryError> { match ¶ms[0].value { ValueType::Int(op1) => match params[1].value { - ValueType::Int(op2) => Ok(Rc::new(Value::new_bool(*op1 > op2))), + ValueType::Int(op2) => Ok(Rc::new(Value::new::(*op1 > op2))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), }, ValueType::Float(op1) => match params[1].value { - ValueType::Float(op2) => Ok(Rc::new(Value::new_bool(*op1 > op2))), + ValueType::Float(op2) => Ok(Rc::new(Value::new::(*op1 > op2))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), }, ValueType::List(op1) => match ¶ms[1].value { - ValueType::List(op2) => Ok(Rc::new(Value::new_bool(op1.greater_than(op2)))), + ValueType::List(op2) => Ok(Rc::new(Value::new::(op1.greater_than(op2)))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), @@ -457,19 +457,19 @@ impl NativeFunctionCall { fn less_op(&self, params: &[Rc]) -> Result, StoryError> { match ¶ms[0].value { ValueType::Int(op1) => match params[1].value { - ValueType::Int(op2) => Ok(Rc::new(Value::new_bool(*op1 < op2))), + ValueType::Int(op2) => Ok(Rc::new(Value::new::(*op1 < op2))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), }, ValueType::Float(op1) => match params[1].value { - ValueType::Float(op2) => Ok(Rc::new(Value::new_bool(*op1 < op2))), + ValueType::Float(op2) => Ok(Rc::new(Value::new::(*op1 < op2))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), }, ValueType::List(op1) => match ¶ms[1].value { - ValueType::List(op2) => Ok(Rc::new(Value::new_bool(op1.less_than(op2)))), + ValueType::List(op2) => Ok(Rc::new(Value::new::(op1.less_than(op2)))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), @@ -486,20 +486,20 @@ impl NativeFunctionCall { ) -> Result, StoryError> { match ¶ms[0].value { ValueType::Int(op1) => match params[1].value { - ValueType::Int(op2) => Ok(Rc::new(Value::new_bool(*op1 >= op2))), + ValueType::Int(op2) => Ok(Rc::new(Value::new::(*op1 >= op2))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), }, ValueType::Float(op1) => match params[1].value { - ValueType::Float(op2) => Ok(Rc::new(Value::new_bool(*op1 >= op2))), + ValueType::Float(op2) => Ok(Rc::new(Value::new::(*op1 >= op2))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), }, ValueType::List(op1) => match ¶ms[1].value { ValueType::List(op2) => { - Ok(Rc::new(Value::new_bool(op1.greater_than_or_equals(op2)))) + Ok(Rc::new(Value::new::(op1.greater_than_or_equals(op2)))) } _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), @@ -514,19 +514,19 @@ impl NativeFunctionCall { fn less_than_or_equals_op(&self, params: &[Rc]) -> Result, StoryError> { match ¶ms[0].value { ValueType::Int(op1) => match params[1].value { - ValueType::Int(op2) => Ok(Rc::new(Value::new_bool(*op1 <= op2))), + ValueType::Int(op2) => Ok(Rc::new(Value::new::(*op1 <= op2))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), }, ValueType::Float(op1) => match params[1].value { - ValueType::Float(op2) => Ok(Rc::new(Value::new_bool(*op1 <= op2))), + ValueType::Float(op2) => Ok(Rc::new(Value::new::(*op1 <= op2))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), }, ValueType::List(op1) => match ¶ms[1].value { - ValueType::List(op2) => Ok(Rc::new(Value::new_bool(op1.less_than_or_equals(op2)))), + ValueType::List(op2) => Ok(Rc::new(Value::new::(op1.less_than_or_equals(op2)))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), @@ -540,19 +540,19 @@ impl NativeFunctionCall { fn subtract_op(&self, params: &[Rc]) -> Result, StoryError> { match ¶ms[0].value { ValueType::Int(op1) => match params[1].value { - ValueType::Int(op2) => Ok(Rc::new(Value::new_int(*op1 - op2))), + ValueType::Int(op2) => Ok(Rc::new(Value::new::(*op1 - op2))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), }, ValueType::Float(op1) => match params[1].value { - ValueType::Float(op2) => Ok(Rc::new(Value::new_float(*op1 - op2))), + ValueType::Float(op2) => Ok(Rc::new(Value::new::(*op1 - op2))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), }, ValueType::List(op1) => match ¶ms[1].value { - ValueType::List(op2) => Ok(Rc::new(Value::new_list(op1.without(op2)))), + ValueType::List(op2) => Ok(Rc::new(Value::new::(op1.without(op2)))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), @@ -566,13 +566,13 @@ impl NativeFunctionCall { fn add_op(&self, params: &[Rc]) -> Result, StoryError> { match ¶ms[0].value { ValueType::Int(op1) => match params[1].value { - ValueType::Int(op2) => Ok(Rc::new(Value::new_int(op1 + op2))), + ValueType::Int(op2) => Ok(Rc::new(Value::new::(op1 + op2))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), }, ValueType::Float(op1) => match params[1].value { - ValueType::Float(op2) => Ok(Rc::new(Value::new_float(op1 + op2))), + ValueType::Float(op2) => Ok(Rc::new(Value::new::(op1 + op2))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), @@ -582,14 +582,14 @@ impl NativeFunctionCall { let mut sb = String::new(); sb.push_str(&op1.string); sb.push_str(&op2.string); - Ok(Rc::new(Value::new_string(&sb))) + Ok(Rc::new(Value::new::<&str>(&sb))) } _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), }, ValueType::List(op1) => match ¶ms[1].value { - ValueType::List(op2) => Ok(Rc::new(Value::new_list(op1.union(op2)))), + ValueType::List(op2) => Ok(Rc::new(Value::new::(op1.union(op2)))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), @@ -603,13 +603,13 @@ impl NativeFunctionCall { fn divide_op(&self, params: &[Rc]) -> Result, StoryError> { match params[0].value { ValueType::Int(op1) => match params[1].value { - ValueType::Int(op2) => Ok(Rc::new(Value::new_int(op1 / op2))), + ValueType::Int(op2) => Ok(Rc::new(Value::new::(op1 / op2))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), }, ValueType::Float(op1) => match params[1].value { - ValueType::Float(op2) => Ok(Rc::new(Value::new_float(op1 / op2))), + ValueType::Float(op2) => Ok(Rc::new(Value::new::(op1 / op2))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), @@ -623,13 +623,13 @@ impl NativeFunctionCall { fn pow_op(&self, params: &[Rc]) -> Result, StoryError> { match params[0].value { ValueType::Int(op1) => match params[1].value { - ValueType::Int(op2) => Ok(Rc::new(Value::new_float((op1 as f32).powf(op2 as f32)))), + ValueType::Int(op2) => Ok(Rc::new(Value::new::((op1 as f32).powf(op2 as f32)))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), }, ValueType::Float(op1) => match params[1].value { - ValueType::Float(op2) => Ok(Rc::new(Value::new_float(op1.powf(op2)))), + ValueType::Float(op2) => Ok(Rc::new(Value::new::(op1.powf(op2)))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), @@ -643,13 +643,13 @@ impl NativeFunctionCall { fn multiply_op(&self, params: &[Rc]) -> Result, StoryError> { match params[0].value { ValueType::Int(op1) => match params[1].value { - ValueType::Int(op2) => Ok(Rc::new(Value::new_int(op1 * op2))), + ValueType::Int(op2) => Ok(Rc::new(Value::new::(op1 * op2))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), }, ValueType::Float(op1) => match params[1].value { - ValueType::Float(op2) => Ok(Rc::new(Value::new_float(op1 * op2))), + ValueType::Float(op2) => Ok(Rc::new(Value::new::(op1 * op2))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), @@ -663,25 +663,25 @@ impl NativeFunctionCall { fn or_op(&self, params: &[Rc]) -> Result, StoryError> { match ¶ms[0].value { ValueType::Bool(op1) => match params[1].value { - ValueType::Bool(op2) => Ok(Rc::new(Value::new_bool(*op1 || op2))), + ValueType::Bool(op2) => Ok(Rc::new(Value::new::(*op1 || op2))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), }, ValueType::Int(op1) => match params[1].value { - ValueType::Int(op2) => Ok(Rc::new(Value::new_bool(*op1 != 0 || op2 != 0))), + ValueType::Int(op2) => Ok(Rc::new(Value::new::(*op1 != 0 || op2 != 0))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), }, ValueType::Float(op1) => match params[1].value { - ValueType::Float(op2) => Ok(Rc::new(Value::new_bool(*op1 != 0.0 || op2 != 0.0))), + ValueType::Float(op2) => Ok(Rc::new(Value::new::(*op1 != 0.0 || op2 != 0.0))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), }, ValueType::List(op1) => match ¶ms[1].value { - ValueType::List(op2) => Ok(Rc::new(Value::new_bool( + ValueType::List(op2) => Ok(Rc::new(Value::new::( !op1.items.is_empty() || !op2.items.is_empty(), ))), _ => Err(StoryError::InvalidStoryState( @@ -696,9 +696,9 @@ impl NativeFunctionCall { fn not_op(&self, params: &[Rc]) -> Result, StoryError> { match ¶ms[0].value { - ValueType::Int(op1) => Ok(Rc::new(Value::new_bool(*op1 == 0))), - ValueType::Float(op1) => Ok(Rc::new(Value::new_bool(*op1 == 0.0))), - ValueType::List(op1) => Ok(Rc::new(Value::new_int(match op1.items.is_empty() { + ValueType::Int(op1) => Ok(Rc::new(Value::new::(*op1 == 0))), + ValueType::Float(op1) => Ok(Rc::new(Value::new::(*op1 == 0.0))), + ValueType::List(op1) => Ok(Rc::new(Value::new::(match op1.items.is_empty() { true => 1, false => 0, }))), @@ -711,13 +711,13 @@ impl NativeFunctionCall { fn min_op(&self, params: &[Rc]) -> Result, StoryError> { match ¶ms[0].value { ValueType::Int(op1) => match params[1].value { - ValueType::Int(op2) => Ok(Rc::new(Value::new_int(i32::min(*op1, op2)))), + ValueType::Int(op2) => Ok(Rc::new(Value::new::(i32::min(*op1, op2)))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), }, ValueType::Float(op1) => match params[1].value { - ValueType::Float(op2) => Ok(Rc::new(Value::new_float(f32::min(*op1, op2)))), + ValueType::Float(op2) => Ok(Rc::new(Value::new::(f32::min(*op1, op2)))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), @@ -731,13 +731,13 @@ impl NativeFunctionCall { fn max_op(&self, params: &[Rc]) -> Result, StoryError> { match ¶ms[0].value { ValueType::Int(op1) => match params[1].value { - ValueType::Int(op2) => Ok(Rc::new(Value::new_int(i32::max(*op1, op2)))), + ValueType::Int(op2) => Ok(Rc::new(Value::new::(i32::max(*op1, op2)))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), }, ValueType::Float(op1) => match params[1].value { - ValueType::Float(op2) => Ok(Rc::new(Value::new_float(f32::max(*op1, op2)))), + ValueType::Float(op2) => Ok(Rc::new(Value::new::(f32::max(*op1, op2)))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), @@ -751,37 +751,37 @@ impl NativeFunctionCall { fn equal_op(&self, params: &[Rc]) -> Result, StoryError> { match ¶ms[0].value { ValueType::Bool(op1) => match params[1].value { - ValueType::Bool(op2) => Ok(Rc::new(Value::new_bool(*op1 == op2))), + ValueType::Bool(op2) => Ok(Rc::new(Value::new::(*op1 == op2))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), }, ValueType::Int(op1) => match params[1].value { - ValueType::Int(op2) => Ok(Rc::new(Value::new_bool(*op1 == op2))), + ValueType::Int(op2) => Ok(Rc::new(Value::new::(*op1 == op2))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), }, ValueType::Float(op1) => match params[1].value { - ValueType::Float(op2) => Ok(Rc::new(Value::new_bool(*op1 == op2))), + ValueType::Float(op2) => Ok(Rc::new(Value::new::(*op1 == op2))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), }, ValueType::String(op1) => match ¶ms[1].value { - ValueType::String(op2) => Ok(Rc::new(Value::new_bool(op1.string.eq(&op2.string)))), + ValueType::String(op2) => Ok(Rc::new(Value::new::(op1.string.eq(&op2.string)))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), }, ValueType::List(op1) => match ¶ms[1].value { - ValueType::List(op2) => Ok(Rc::new(Value::new_bool(op1.eq(op2)))), + ValueType::List(op2) => Ok(Rc::new(Value::new::(op1.eq(op2)))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), }, ValueType::DivertTarget(op1) => match ¶ms[1].value { - ValueType::DivertTarget(op2) => Ok(Rc::new(Value::new_bool(op1.eq(op2)))), + ValueType::DivertTarget(op2) => Ok(Rc::new(Value::new::(op1.eq(op2)))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), @@ -795,37 +795,37 @@ impl NativeFunctionCall { fn not_equals_op(&self, params: &[Rc]) -> Result, StoryError> { match ¶ms[0].value { ValueType::Bool(op1) => match params[1].value { - ValueType::Bool(op2) => Ok(Rc::new(Value::new_bool(*op1 != op2))), + ValueType::Bool(op2) => Ok(Rc::new(Value::new::(*op1 != op2))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), }, ValueType::Int(op1) => match params[1].value { - ValueType::Int(op2) => Ok(Rc::new(Value::new_bool(*op1 != op2))), + ValueType::Int(op2) => Ok(Rc::new(Value::new::(*op1 != op2))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), }, ValueType::Float(op1) => match params[1].value { - ValueType::Float(op2) => Ok(Rc::new(Value::new_bool(*op1 != op2))), + ValueType::Float(op2) => Ok(Rc::new(Value::new::(*op1 != op2))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), }, ValueType::String(op1) => match ¶ms[1].value { - ValueType::String(op2) => Ok(Rc::new(Value::new_bool(!op1.string.eq(&op2.string)))), + ValueType::String(op2) => Ok(Rc::new(Value::new::(!op1.string.eq(&op2.string)))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), }, ValueType::List(op1) => match ¶ms[1].value { - ValueType::List(op2) => Ok(Rc::new(Value::new_bool(!op1.eq(op2)))), + ValueType::List(op2) => Ok(Rc::new(Value::new::(!op1.eq(op2)))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), }, ValueType::DivertTarget(op1) => match ¶ms[1].value { - ValueType::DivertTarget(op2) => Ok(Rc::new(Value::new_bool(!op1.eq(op2)))), + ValueType::DivertTarget(op2) => Ok(Rc::new(Value::new::(!op1.eq(op2)))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), @@ -839,13 +839,13 @@ impl NativeFunctionCall { fn mod_op(&self, params: &[Rc]) -> Result, StoryError> { match params[0].value { ValueType::Int(op1) => match params[1].value { - ValueType::Int(op2) => Ok(Rc::new(Value::new_int(op1 % op2))), + ValueType::Int(op2) => Ok(Rc::new(Value::new::(op1 % op2))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), }, ValueType::Float(op1) => match params[1].value { - ValueType::Float(op2) => Ok(Rc::new(Value::new_float(op1 % op2))), + ValueType::Float(op2) => Ok(Rc::new(Value::new::(op1 % op2))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), @@ -859,7 +859,7 @@ impl NativeFunctionCall { fn intersect_op(&self, params: &[Rc]) -> Result, StoryError> { match ¶ms[0].value { ValueType::List(op1) => match ¶ms[1].value { - ValueType::List(op2) => Ok(Rc::new(Value::new_list(op1.intersect(op2)))), + ValueType::List(op2) => Ok(Rc::new(Value::new::(op1.intersect(op2)))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), @@ -874,14 +874,14 @@ impl NativeFunctionCall { match ¶ms[0].value { ValueType::String(op1) => match ¶ms[1].value { ValueType::String(op2) => { - Ok(Rc::new(Value::new_bool(op1.string.contains(&op2.string)))) + Ok(Rc::new(Value::new::(op1.string.contains(&op2.string)))) } _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), }, ValueType::List(op1) => match ¶ms[1].value { - ValueType::List(op2) => Ok(Rc::new(Value::new_bool(op1.contains(op2)))), + ValueType::List(op2) => Ok(Rc::new(Value::new::(op1.contains(op2)))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), @@ -896,14 +896,14 @@ impl NativeFunctionCall { match ¶ms[0].value { ValueType::String(op1) => match ¶ms[1].value { ValueType::String(op2) => { - Ok(Rc::new(Value::new_bool(!op1.string.contains(&op2.string)))) + Ok(Rc::new(Value::new::(!op1.string.contains(&op2.string)))) } _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), }, ValueType::List(op1) => match ¶ms[1].value { - ValueType::List(op2) => Ok(Rc::new(Value::new_bool(!op1.contains(op2)))), + ValueType::List(op2) => Ok(Rc::new(Value::new::(!op1.contains(op2)))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), @@ -917,8 +917,8 @@ impl NativeFunctionCall { fn value_of_list_op(&self, params: &[Rc]) -> Result, StoryError> { match ¶ms[0].value { ValueType::List(op1) => match op1.get_max_item() { - Some(i) => Ok(Rc::new(Value::new_int(i.1))), - None => Ok(Rc::new(Value::new_int(0))), + Some(i) => Ok(Rc::new(Value::new::(i.1))), + None => Ok(Rc::new(Value::new::(0))), }, _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), @@ -928,7 +928,7 @@ impl NativeFunctionCall { fn all_op(&self, params: &[Rc]) -> Result, StoryError> { match ¶ms[0].value { - ValueType::List(op1) => Ok(Rc::new(Value::new_list(op1.get_all()))), + ValueType::List(op1) => Ok(Rc::new(Value::new::(op1.get_all()))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), @@ -937,7 +937,7 @@ impl NativeFunctionCall { fn inverse_op(&self, params: &[Rc]) -> Result, StoryError> { match ¶ms[0].value { - ValueType::List(op1) => Ok(Rc::new(Value::new_list(op1.inverse()))), + ValueType::List(op1) => Ok(Rc::new(Value::new::(op1.inverse()))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), @@ -946,7 +946,7 @@ impl NativeFunctionCall { fn count_op(&self, params: &[Rc]) -> Result, StoryError> { match ¶ms[0].value { - ValueType::List(op1) => Ok(Rc::new(Value::new_int(op1.items.len() as i32))), + ValueType::List(op1) => Ok(Rc::new(Value::new::(op1.items.len() as i32))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), @@ -955,7 +955,7 @@ impl NativeFunctionCall { fn list_max_op(&self, params: &[Rc]) -> Result, StoryError> { match ¶ms[0].value { - ValueType::List(op1) => Ok(Rc::new(Value::new_list(op1.max_as_list()))), + ValueType::List(op1) => Ok(Rc::new(Value::new::(op1.max_as_list()))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), @@ -964,7 +964,7 @@ impl NativeFunctionCall { fn list_min_op(&self, params: &[Rc]) -> Result, StoryError> { match ¶ms[0].value { - ValueType::List(op1) => Ok(Rc::new(Value::new_list(op1.min_as_list()))), + ValueType::List(op1) => Ok(Rc::new(Value::new::(op1.min_as_list()))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), @@ -973,8 +973,8 @@ impl NativeFunctionCall { fn negate_op(&self, params: &[Rc]) -> Result, StoryError> { match ¶ms[0].value { - ValueType::Int(op1) => Ok(Rc::new(Value::new_int(-op1))), - ValueType::Float(op1) => Ok(Rc::new(Value::new_float(-op1))), + ValueType::Int(op1) => Ok(Rc::new(Value::new::(-op1))), + ValueType::Float(op1) => Ok(Rc::new(Value::new::(-op1))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), @@ -983,8 +983,8 @@ impl NativeFunctionCall { fn floor_op(&self, params: &[Rc]) -> Result, StoryError> { match ¶ms[0].value { - ValueType::Int(op1) => Ok(Rc::new(Value::new_int(*op1))), - ValueType::Float(op1) => Ok(Rc::new(Value::new_float(op1.floor()))), + ValueType::Int(op1) => Ok(Rc::new(Value::new::(*op1))), + ValueType::Float(op1) => Ok(Rc::new(Value::new::(op1.floor()))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), @@ -993,8 +993,8 @@ impl NativeFunctionCall { fn ceiling_op(&self, params: &[Rc]) -> Result, StoryError> { match ¶ms[0].value { - ValueType::Int(op1) => Ok(Rc::new(Value::new_int(*op1))), - ValueType::Float(op1) => Ok(Rc::new(Value::new_float(op1.ceil()))), + ValueType::Int(op1) => Ok(Rc::new(Value::new::(*op1))), + ValueType::Float(op1) => Ok(Rc::new(Value::new::(op1.ceil()))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), @@ -1003,8 +1003,8 @@ impl NativeFunctionCall { fn int_op(&self, params: &[Rc]) -> Result, StoryError> { match ¶ms[0].value { - ValueType::Int(op1) => Ok(Rc::new(Value::new_int(*op1))), - ValueType::Float(op1) => Ok(Rc::new(Value::new_int(*op1 as i32))), + ValueType::Int(op1) => Ok(Rc::new(Value::new::(*op1))), + ValueType::Float(op1) => Ok(Rc::new(Value::new::(*op1 as i32))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), @@ -1013,8 +1013,8 @@ impl NativeFunctionCall { fn float_op(&self, params: &[Rc]) -> Result, StoryError> { match ¶ms[0].value { - ValueType::Int(op1) => Ok(Rc::new(Value::new_float(*op1 as f32))), - ValueType::Float(op1) => Ok(Rc::new(Value::new_float(*op1))), + ValueType::Int(op1) => Ok(Rc::new(Value::new::(*op1 as f32))), + ValueType::Float(op1) => Ok(Rc::new(Value::new::(*op1))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), diff --git a/lib/src/story/control_logic.rs b/lib/src/story/control_logic.rs index 855f120..241fb39 100644 --- a/lib/src/story/control_logic.rs +++ b/lib/src/story/control_logic.rs @@ -129,7 +129,7 @@ impl Story { // only problem is when exporting text for viewing, it // skips over numbers etc. let text: Rc = - Rc::new(Value::new_string(&output.to_string())); + Rc::new(Value::new::<&str>(&output.to_string())); self.get_state_mut().push_to_output_stream(text); } } @@ -284,18 +284,18 @@ impl Story { // Return to expression evaluation (from content mode) self.get_state().set_in_expression_evaluation(true); self.get_state_mut() - .push_evaluation_stack(Rc::new(Value::new_string(&sb))); + .push_evaluation_stack(Rc::new(Value::new::<&str>(&sb))); } CommandType::NoOp => {} CommandType::ChoiceCount => { let choice_count = self.get_state().get_generated_choices().len(); self.get_state_mut() - .push_evaluation_stack(Rc::new(Value::new_int(choice_count as i32))); + .push_evaluation_stack(Rc::new(Value::new::(choice_count as i32))); } CommandType::Turns => { let current_turn = self.get_state().current_turn_index; self.get_state_mut() - .push_evaluation_stack(Rc::new(Value::new_int(current_turn + 1))); + .push_evaluation_stack(Rc::new(Value::new::(current_turn + 1))); } CommandType::TurnsSince | CommandType::ReadCount => { let target = self.get_state_mut().pop_evaluation_stack(); @@ -347,7 +347,7 @@ impl Story { } self.get_state_mut() - .push_evaluation_stack(Rc::new(Value::new_int(either_count))); + .push_evaluation_stack(Rc::new(Value::new::(either_count))); } CommandType::Random => { let mut max_int = None; @@ -392,7 +392,7 @@ impl Story { let next_random = rng.gen::(); let chosen_value = (next_random % random_range as u32) as i32 + min_value; self.get_state_mut() - .push_evaluation_stack(Rc::new(Value::new_int(chosen_value))); + .push_evaluation_stack(Rc::new(Value::new::(chosen_value))); self.get_state_mut().previous_random = self.get_state().previous_random + 1; } CommandType::SeedRandom => { @@ -419,11 +419,11 @@ impl Story { let count = self.get_state_mut().visit_count_for_container(&cpc) - 1; // index // not count self.get_state_mut() - .push_evaluation_stack(Rc::new(Value::new_int(count))); + .push_evaluation_stack(Rc::new(Value::new::(count))); } CommandType::SequenceShuffleIndex => { let shuffle_index = self.next_sequence_shuffle_index()?; - let v = Rc::new(Value::new_int(shuffle_index)); + let v = Rc::new(Value::new::(shuffle_index)); self.get_state_mut().push_evaluation_stack(v); } CommandType::StartThread => { @@ -477,7 +477,7 @@ impl Story { found_item.clone(), int_val.unwrap(), )); - generated_list_value = Some(Value::new_list(l)); + generated_list_value = Some(Value::new::(l)); } } else { return Err(StoryError::InvalidStoryState(format!( @@ -487,7 +487,7 @@ impl Story { } if generated_list_value.is_none() { - generated_list_value = Some(Value::new_list(InkList::new())); + generated_list_value = Some(Value::new::(InkList::new())); } self.get_state_mut() @@ -510,7 +510,7 @@ impl Story { .unwrap() .list_with_sub_range(&min.unwrap().value, &max.unwrap().value); self.get_state_mut() - .push_evaluation_stack(Rc::new(Value::new_list(result))); + .push_evaluation_stack(Rc::new(Value::new::(result))); } CommandType::ListRandom => { let o = self.get_state_mut().pop_evaluation_stack(); @@ -549,7 +549,7 @@ impl Story { } }; self.get_state_mut() - .push_evaluation_stack(Rc::new(Value::new_list(new_list))); + .push_evaluation_stack(Rc::new(Value::new::(new_list))); } CommandType::BeginTag => self .get_state_mut() @@ -658,7 +658,7 @@ impl Story { let count = self .get_state_mut() .visit_count_for_container(container.as_ref().unwrap()); - found_value = Rc::new(Value::new_int(count)); + found_value = Rc::new(Value::new::(count)); } // Normal variable reference else { @@ -670,7 +670,7 @@ impl Story { Some(v) => found_value = v, None => { self.add_error(&format!("Variable not found: '{}'. Using default value of 0 (false). This can happen with temporary variables if the declaration hasn't yet been hit. Globals are always given a default value on load if a value doesn't exist in the save state.", var_ref.name), true); - found_value = Rc::new(Value::new_int(0)); + found_value = Rc::new(Value::new::(0)); } } } diff --git a/lib/src/story/external_functions.rs b/lib/src/story/external_functions.rs index 4361144..b238ea0 100644 --- a/lib/src/story/external_functions.rs +++ b/lib/src/story/external_functions.rs @@ -185,7 +185,7 @@ impl Story { // Convert return value (if any) to a type that the ink engine can use let return_obj: Rc = match func_result { - Some(func_result) => Rc::new(Value::new(func_result)), + Some(func_result) => Rc::new(Value::new_value_type(func_result)), None => Rc::new(Void::new()), }; diff --git a/lib/src/story_state.rs b/lib/src/story_state.rs index ef58952..8c62be4 100644 --- a/lib/src/story_state.rs +++ b/lib/src/story_state.rs @@ -7,6 +7,7 @@ use crate::{ control_command::{CommandType, ControlCommand}, flow::Flow, glue::Glue, + ink_list::InkList, json::{json_read, json_write}, list_definitions_origin::ListDefinitionsOrigin, object::{Object, RTObject}, @@ -496,10 +497,10 @@ impl StoryState { if head_first_newline_idx != -1 { if head_first_newline_idx > 0 { - let leading_spaces = Value::new_string(&text[0..head_first_newline_idx as usize]); + let leading_spaces = Value::new::<&str>(&text[0..head_first_newline_idx as usize]); list_texts.push(leading_spaces); } - list_texts.push(Value::new_string("\n")); + list_texts.push(Value::new::<&str>("\n")); inner_str_start = head_last_newline_idx + 1; } @@ -509,14 +510,14 @@ impl StoryState { if inner_str_end > inner_str_start as usize { let inner_str_text = &text[inner_str_start as usize..inner_str_end]; - list_texts.push(Value::new_string(inner_str_text)); + list_texts.push(Value::new::<&str>(inner_str_text)); } if tail_last_newline_idx != -1 && tail_first_newline_idx > head_last_newline_idx { - list_texts.push(Value::new_string("\n")); + list_texts.push(Value::new::<&str>("\n")); if tail_last_newline_idx < text.len() as i32 - 1 { let num_spaces = (text.len() as i32 - tail_last_newline_idx) - 1; - let trailing_spaces = Value::new_string( + let trailing_spaces = Value::new::<&str>( &text[(tail_last_newline_idx + 1) as usize ..(num_spaces + tail_last_newline_idx + 1) as usize], ); @@ -1019,11 +1020,11 @@ impl StoryState { if let Some(arguments) = arguments { for arg in arguments { let value = match arg { - ValueType::Bool(v) => Value::new_bool(*v), - ValueType::Int(v) => Value::new_int(*v), - ValueType::Float(v) => Value::new_float(*v), - ValueType::List(v) => Value::new_list(v.clone()), - ValueType::String(v) => Value::new_string(&v.string), + ValueType::Bool(v) => Value::new::(*v), + ValueType::Int(v) => Value::new::(*v), + ValueType::Float(v) => Value::new::(*v), + ValueType::List(v) => Value::new::(v.clone()), + ValueType::String(v) => Value::new::<&str>(&v.string), _ => { return Err(StoryError::InvalidStoryState("ink arguments when calling EvaluateFunction / ChoosePathStringWithParameters must be \ int, float, string, bool or InkList.".to_owned())); @@ -1088,7 +1089,7 @@ impl StoryState { // DivertTargets get returned as the string of components // (rather than a Path, which isn't public) if let ValueType::DivertTarget(p) = &return_val.value { - return Ok(Some(ValueType::new_string(&p.to_string()))); + return Ok(Some(ValueType::new::<&str>(&p.to_string()))); } // Other types can just have their exact object type: diff --git a/lib/src/value.rs b/lib/src/value.rs index aae33ca..e62335c 100644 --- a/lib/src/value.rs +++ b/lib/src/value.rs @@ -41,47 +41,22 @@ impl fmt::Display for Value { } } -impl Value { - pub fn new(value: ValueType) -> Self { - Self { - obj: Object::new(), - value, - } - } - - pub fn new_bool(v: bool) -> Self { - Self { - obj: Object::new(), - value: ValueType::Bool(v), - } - } - - pub fn new_int(v: i32) -> Self { - Self { - obj: Object::new(), - value: ValueType::Int(v), - } - } - - pub fn new_float(v: f32) -> Self { - Self { - obj: Object::new(), - value: ValueType::Float(v), - } +impl> From for Value { + fn from(value: T) -> Self { + Self::new_value_type(value.into()) } +} - pub fn new_string(v: &str) -> Self { +impl Value { + pub fn new_value_type(valuetype: ValueType) -> Self { Self { obj: Object::new(), - value: ValueType::new_string(v), + value:valuetype, } } - pub fn new_divert_target(p: Path) -> Self { - Self { - obj: Object::new(), - value: ValueType::DivertTarget(p), - } + pub fn new>(v: T) -> Self { + v.into() } pub fn new_variable_pointer(variable_name: &str, context_index: i32) -> Self { @@ -94,20 +69,6 @@ impl Value { } } - pub fn new_list(l: InkList) -> Self { - Self { - obj: Object::new(), - value: ValueType::List(l), - } - } - - pub fn from_value_type(value_type: ValueType) -> Self { - Self { - obj: Object::new(), - value: value_type, - } - } - pub fn is_truthy(&self) -> Result { match &self.value { ValueType::Bool(v) => Ok(*v), @@ -242,23 +203,23 @@ impl Value { CAST_BOOL => Ok(None), CAST_INT => { if *v { - Ok(Some(Self::new_int(1))) + Ok(Some(Self::new::(1))) } else { - Ok(Some(Self::new_int(0))) + Ok(Some(Self::new::(0))) } } CAST_FLOAT => { if *v { - Ok(Some(Self::new_float(1.0))) + Ok(Some(Self::new::(1.0))) } else { - Ok(Some(Self::new_float(0.0))) + Ok(Some(Self::new::(0.0))) } } CAST_STRING => { if *v { - Ok(Some(Self::new_string("true"))) + Ok(Some(Self::new::<&str>("true"))) } else { - Ok(Some(Self::new_string("false"))) + Ok(Some(Self::new::<&str>("false"))) } } _ => Err(StoryError::InvalidStoryState( @@ -268,14 +229,14 @@ impl Value { ValueType::Int(v) => match cast_dest_type { CAST_BOOL => { if *v == 0 { - Ok(Some(Self::new_bool(false))) + Ok(Some(Self::new::(false))) } else { - Ok(Some(Self::new_bool(true))) + Ok(Some(Self::new::(true))) } } CAST_INT => Ok(None), - CAST_FLOAT => Ok(Some(Self::new_float(*v as f32))), - CAST_STRING => Ok(Some(Self::new_string(&v.to_string()))), + CAST_FLOAT => Ok(Some(Self::new::(*v as f32))), + CAST_STRING => Ok(Some(Self::new::<&str>(&v.to_string()))), _ => Err(StoryError::InvalidStoryState( "Cast not allowed for int".to_owned(), )), @@ -283,21 +244,21 @@ impl Value { ValueType::Float(v) => match cast_dest_type { CAST_BOOL => { if *v == 0.0 { - Ok(Some(Self::new_bool(false))) + Ok(Some(Self::new::(false))) } else { - Ok(Some(Self::new_bool(true))) + Ok(Some(Self::new::(true))) } } - CAST_INT => Ok(Some(Self::new_int(*v as i32))), + CAST_INT => Ok(Some(Self::new::(*v as i32))), CAST_FLOAT => Ok(None), - CAST_STRING => Ok(Some(Self::new_string(&v.to_string()))), + CAST_STRING => Ok(Some(Self::new::<&str>(&v.to_string()))), _ => Err(StoryError::InvalidStoryState( "Cast not allowed for float".to_owned(), )), }, ValueType::String(v) => match cast_dest_type { - CAST_INT => Ok(Some(Self::new_int(v.string.parse::().unwrap()))), - CAST_FLOAT => Ok(Some(Self::new_float(v.string.parse::().unwrap()))), + CAST_INT => Ok(Some(Self::new::(v.string.parse::().unwrap()))), + CAST_FLOAT => Ok(Some(Self::new::(v.string.parse::().unwrap()))), CAST_STRING => Ok(None), _ => Err(StoryError::InvalidStoryState( "Cast not allowed for string".to_owned(), @@ -307,23 +268,23 @@ impl Value { CAST_INT => { let max = l.get_max_item(); match max { - Some(i) => Ok(Some(Self::new_int(i.1))), - None => Ok(Some(Self::new_int(0))), + Some(i) => Ok(Some(Self::new::(i.1))), + None => Ok(Some(Self::new::(0))), } } CAST_FLOAT => { let max = l.get_max_item(); match max { - Some(i) => Ok(Some(Self::new_float(i.1 as f32))), - None => Ok(Some(Self::new_float(0.0))), + Some(i) => Ok(Some(Self::new::(i.1 as f32))), + None => Ok(Some(Self::new::(0.0))), } } CAST_LIST => Ok(None), CAST_STRING => { let max = l.get_max_item(); match max { - Some(i) => Ok(Some(Self::new_string(&i.0.to_string()))), - None => Ok(Some(Self::new_string(""))), + Some(i) => Ok(Some(Self::new::<&str>(&i.0.to_string()))), + None => Ok(Some(Self::new::<&str>(""))), } } _ => Err(StoryError::InvalidStoryState( diff --git a/lib/src/value_type.rs b/lib/src/value_type.rs index 9ca60c6..fa5bf4b 100644 --- a/lib/src/value_type.rs +++ b/lib/src/value_type.rs @@ -10,7 +10,7 @@ pub enum ValueType { Float(f32), /// An Ink list value. List(InkList), - /// Ink string, constructed with [`new_string`](ValueType::new_string) + /// Ink string, constructed with [`new_string`](ValueType::new::<&str>) String(StringValue), /// Reference to an Ink divert. DivertTarget(Path), @@ -18,56 +18,105 @@ pub enum ValueType { VariablePointer(VariablePointerValue), } -impl ValueType { - /// Creates a new `ValueType` for a `String`. - pub fn new_string(str: &str) -> ValueType { - let mut inline_ws = true; - - for c in str.chars() { - if c != ' ' && c != '\t' { - inline_ws = false; - break; - } - } +impl From for ValueType { + fn from(value: bool) -> ValueType { + ValueType::Bool(value) + } +} + +impl From for ValueType { + fn from(value: i32) -> ValueType { + ValueType::Int(value) + } +} + +impl From for ValueType { + fn from(value: f32) -> ValueType { + ValueType::Float(value) + } +} + +impl From<&str> for ValueType { + fn from(value: &str) -> ValueType { + let inline_ws = value.chars().all(|c| c == ' ' || c == '\t'); ValueType::String(StringValue { - string: str.to_string(), + string: value.to_string(), is_inline_whitespace: inline_ws, - is_newline: str.eq("\n"), + is_newline: value.eq("\n"), }) } +} - /// Gets the internal boolean, value or `None` if the `ValueType` is not a [`ValueType::Bool`] - pub fn get_bool(&self) -> Option { - match self { - ValueType::Bool(v) => Some(*v), - _ => None, +impl From for ValueType { + fn from(value: InkList) -> ValueType { + ValueType::List(value) + } +} + +impl From for ValueType { + fn from(value: Path) -> ValueType { + ValueType::DivertTarget(value) + } +} + +impl From for ValueType { + fn from(value: VariablePointerValue) -> Self { + ValueType::VariablePointer(value) + } +} + +impl TryFrom<&ValueType> for bool { + type Error = (); + fn try_from(value: &ValueType) -> Result { + match value { + ValueType::Bool(v) => Ok(*v), + _ => Err(()), } } +} - /// Gets the internal `i32` value, or `None` if the `ValueType` is not a [`ValueType::Int`] - pub fn get_int(&self) -> Option { - match self { - ValueType::Int(v) => Some(*v), - _ => None, +impl TryFrom<&ValueType> for i32 { + type Error = (); + fn try_from(value: &ValueType) -> Result { + match value { + ValueType::Int(v) => Ok(*v), + _ => Err(()), } } +} - /// Gets the internal `f32` value, or `None` if the `ValueType` is not a [`ValueType::Float`] - pub fn get_float(&self) -> Option { - match self { - ValueType::Float(v) => Some(*v), - _ => None, +impl TryFrom<&ValueType> for f32 { + type Error = (); + fn try_from(value: &ValueType) -> Result { + match value { + ValueType::Float(v) => Ok(*v), + _ => Err(()), } } +} - /// Gets the internal string value, or `None` if the `ValueType` is not a [`ValueType::String`] - pub fn get_str(&self) -> Option<&str> { - match self { - ValueType::String(v) => Some(&v.string), - _ => None, +impl<'val> TryFrom<&'val ValueType> for &'val str { + type Error = (); + fn try_from(value: &'val ValueType) -> Result { + match value { + ValueType::String(v) => Ok(&v.string), + _ => Err(()), } } +} + +impl ValueType { + pub fn new>(v: T) -> Self { + v.into() + } + + pub fn get<'val, T>(&'val self) -> Option + where + &'val Self: TryInto, + { + self.try_into().ok() + } /// Tries to convert the internal value of this `ValueType` to `i32` pub fn coerce_to_int(&self) -> Result { diff --git a/lib/src/variables_state.rs b/lib/src/variables_state.rs index f768a5e..84ec874 100644 --- a/lib/src/variables_state.rs +++ b/lib/src/variables_state.rs @@ -198,7 +198,7 @@ impl VariablesState { ))); } - let val = Value::from_value_type(value_type); + let val = Value::new_value_type(value_type); let notify = self.set_global(variable_name, Rc::new(val)); diff --git a/lib/tests/function_test.rs b/lib/tests/function_test.rs index 1394f1d..909a1b6 100644 --- a/lib/tests/function_test.rs +++ b/lib/tests/function_test.rs @@ -123,7 +123,7 @@ fn evaluating_function_variable_state_bug_test() -> Result<(), StoryError> { let mut output = String::new(); let result = story.evaluate_function("function_to_evaluate", None, &mut output); - assert_eq!("RIGHT", result?.unwrap().get_str().unwrap()); + assert_eq!("RIGHT", result?.unwrap().get::<&str>().unwrap()); assert_eq!("End\n", story.cont()?); diff --git a/lib/tests/runtime_test.rs b/lib/tests/runtime_test.rs index 0c89965..3a30634 100644 --- a/lib/tests/runtime_test.rs +++ b/lib/tests/runtime_test.rs @@ -26,13 +26,13 @@ impl ExternalFunction for ExtFunc1 { impl ExternalFunction for ExtFunc2 { fn call(&mut self, _: &str, _: Vec) -> Option { - Some(ValueType::new_string("Hello world")) + Some(ValueType::new::<&str>("Hello world")) } } impl ExternalFunction for ExtFunc3 { fn call(&mut self, _: &str, args: Vec) -> Option { - Some(ValueType::Bool(args[0].get_int().unwrap() != 1)) + Some(ValueType::Bool(args[0].get::().unwrap() != 1)) } } @@ -159,11 +159,11 @@ fn set_and_get_variable_test() -> Result<(), Box> { let mut text: Vec = Vec::new(); common::next_all(&mut story, &mut text)?; - assert_eq!(10, story.get_variable("x").unwrap().get_int().unwrap()); + assert_eq!(10, story.get_variable("x").unwrap().get::().unwrap()); story.set_variable("x", &ValueType::Int(15))?; - assert_eq!(15, story.get_variable("x").unwrap().get_int().unwrap()); + assert_eq!(15, story.get_variable("x").unwrap().get::().unwrap()); story.choose_choice_index(0)?; @@ -184,14 +184,14 @@ fn set_non_existant_variable_test() -> Result<(), Box> { common::next_all(&mut story, &mut text)?; - let result = story.set_variable("y", &ValueType::new_string("earth")); + let result = story.set_variable("y", &ValueType::new::<&str>("earth")); assert!(result.is_err()); - assert_eq!(10, story.get_variable("x").unwrap().get_int().unwrap()); + assert_eq!(10, story.get_variable("x").unwrap().get::().unwrap()); story.set_variable("x", &ValueType::Int(15))?; - assert_eq!(15, story.get_variable("x").unwrap().get_int().unwrap()); + assert_eq!(15, story.get_variable("x").unwrap().get::().unwrap()); story.choose_choice_index(0)?; From de2dfc264a98df2a835618aecd171da33a2c0770 Mon Sep 17 00:00:00 2001 From: IFcoltransG <47414286+IFcoltransG@users.noreply.github.com> Date: Thu, 5 Dec 2024 15:35:37 +1300 Subject: [PATCH 2/3] Make Value accessors into a generic function with trait --- lib/src/json/json_write.rs | 35 ++++-- lib/src/native_function_call.rs | 46 ++++---- lib/src/story/choices.rs | 4 +- lib/src/story/control_logic.rs | 31 +++--- lib/src/story/mod.rs | 21 ++-- lib/src/story/progress.rs | 6 +- lib/src/story/tags.rs | 5 +- lib/src/story_state.rs | 20 ++-- lib/src/value.rs | 182 +++++++++++++++++--------------- lib/src/variables_state.rs | 8 +- 10 files changed, 200 insertions(+), 158 deletions(-) diff --git a/lib/src/json/json_write.rs b/lib/src/json/json_write.rs index dbed038..4a02990 100644 --- a/lib/src/json/json_write.rs +++ b/lib/src/json/json_write.rs @@ -3,11 +3,24 @@ use std::{collections::HashMap, rc::Rc}; use serde_json::{json, Map}; use crate::{ - choice::Choice, choice_point::ChoicePoint, container::Container, - control_command::ControlCommand, divert::Divert, glue::Glue, ink_list::InkList, - native_function_call::NativeFunctionCall, object::RTObject, push_pop::PushPopType, - story_error::StoryError, tag::Tag, value::Value, variable_assigment::VariableAssignment, - variable_reference::VariableReference, void::Void, + choice::Choice, + choice_point::ChoicePoint, + container::Container, + control_command::ControlCommand, + divert::Divert, + glue::Glue, + ink_list::InkList, + native_function_call::NativeFunctionCall, + object::RTObject, + path::Path, + push_pop::PushPopType, + story_error::StoryError, + tag::Tag, + value::Value, + value_type::{StringValue, VariablePointerValue}, + variable_assigment::VariableAssignment, + variable_reference::VariableReference, + void::Void, }; pub fn write_dictionary_values( @@ -79,15 +92,15 @@ pub fn write_rtobject(o: Rc) -> Result(o.as_ref()) { return Ok(json!(v)); } - if let Some(v) = Value::get_float_value(o.as_ref()) { + if let Some(v) = Value::get_value::(o.as_ref()) { return Ok(json!(v)); } - if let Some(v) = Value::get_string_value(o.as_ref()) { + if let Some(v) = Value::get_value::<&StringValue>(o.as_ref()) { let mut s = String::new(); if v.is_newline { @@ -100,17 +113,17 @@ pub fn write_rtobject(o: Rc) -> Result(o.as_ref()) { return Ok(write_ink_list(v)); } - if let Some(v) = Value::get_divert_target_value(o.as_ref()) { + if let Some(v) = Value::get_value::<&Path>(o.as_ref()) { let mut jobj: Map = Map::new(); jobj.insert("^->".to_owned(), json!(v.get_components_string())); return Ok(serde_json::Value::Object(jobj)); } - if let Some(v) = Value::get_variable_pointer_value(o.as_ref()) { + if let Some(v) = Value::get_value::<&VariablePointerValue>(o.as_ref()) { let mut jobj: Map = Map::new(); jobj.insert("^var".to_owned(), json!(v.variable_name)); jobj.insert("ci".to_owned(), json!(v.context_index)); diff --git a/lib/src/native_function_call.rs b/lib/src/native_function_call.rs index d88ec9d..c6980fa 100644 --- a/lib/src/native_function_call.rs +++ b/lib/src/native_function_call.rs @@ -221,7 +221,7 @@ impl NativeFunctionCall { return Err(StoryError::InvalidStoryState(format!("Attempting to perform {} on a void value. Did you forget to 'return' a value from a function you called here?", Self::get_name(self.op)))); } - if Value::get_list_value(p.as_ref()).is_some() { + if Value::get_value::<&InkList>(p.as_ref()).is_some() { has_list = true; } } @@ -243,8 +243,8 @@ impl NativeFunctionCall { ) -> Result, StoryError> { // List-Int addition/subtraction returns a List (e.g., "alpha" + 1 = "beta") if (self.op == Op::Add || self.op == Op::Subtract) - && Value::get_list_value(params[0].as_ref()).is_some() - && Value::get_int_value(params[1].as_ref()).is_some() + && Value::get_value::<&InkList>(params[0].as_ref()).is_some() + && Value::get_value::(params[1].as_ref()).is_some() { return Ok(self.call_list_increment_operation(params)); } @@ -254,8 +254,8 @@ impl NativeFunctionCall { // And/or with any other type requires coercion to bool if (self.op == Op::And || self.op == Op::Or) - && (Value::get_list_value(params[0].as_ref()).is_none() - || Value::get_list_value(params[1].as_ref()).is_none()) + && (Value::get_value::<&InkList>(params[0].as_ref()).is_none() + || Value::get_value::<&InkList>(params[1].as_ref()).is_none()) { let result = { if self.op == Op::And { @@ -269,8 +269,8 @@ impl NativeFunctionCall { } // Normal (list • list) operation - if Value::get_list_value(params[0].as_ref()).is_some() - && Value::get_list_value(params[1].as_ref()).is_some() + if Value::get_value::<&InkList>(params[0].as_ref()).is_some() + && Value::get_value::<&InkList>(params[1].as_ref()).is_some() { let p = vec![v1.clone(), v2.clone()]; @@ -286,8 +286,8 @@ impl NativeFunctionCall { } fn call_list_increment_operation(&self, list_int_params: &[Rc]) -> Rc { - let list_val = Value::get_list_value(list_int_params[0].as_ref()).unwrap(); - let int_val = Value::get_int_value(list_int_params[1].as_ref()).unwrap(); + let list_val = Value::get_value::<&InkList>(list_int_params[0].as_ref()).unwrap(); + let int_val = Value::get_value::(list_int_params[1].as_ref()).unwrap(); let mut result_raw_list = InkList::new(); @@ -526,7 +526,9 @@ impl NativeFunctionCall { )), }, ValueType::List(op1) => match ¶ms[1].value { - ValueType::List(op2) => Ok(Rc::new(Value::new::(op1.less_than_or_equals(op2)))), + ValueType::List(op2) => { + Ok(Rc::new(Value::new::(op1.less_than_or_equals(op2)))) + } _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), @@ -623,7 +625,9 @@ impl NativeFunctionCall { fn pow_op(&self, params: &[Rc]) -> Result, StoryError> { match params[0].value { ValueType::Int(op1) => match params[1].value { - ValueType::Int(op2) => Ok(Rc::new(Value::new::((op1 as f32).powf(op2 as f32)))), + ValueType::Int(op2) => { + Ok(Rc::new(Value::new::((op1 as f32).powf(op2 as f32)))) + } _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), @@ -769,7 +773,9 @@ impl NativeFunctionCall { )), }, ValueType::String(op1) => match ¶ms[1].value { - ValueType::String(op2) => Ok(Rc::new(Value::new::(op1.string.eq(&op2.string)))), + ValueType::String(op2) => { + Ok(Rc::new(Value::new::(op1.string.eq(&op2.string)))) + } _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), @@ -813,7 +819,9 @@ impl NativeFunctionCall { )), }, ValueType::String(op1) => match ¶ms[1].value { - ValueType::String(op2) => Ok(Rc::new(Value::new::(!op1.string.eq(&op2.string)))), + ValueType::String(op2) => { + Ok(Rc::new(Value::new::(!op1.string.eq(&op2.string)))) + } _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), @@ -873,9 +881,9 @@ impl NativeFunctionCall { fn has(&self, params: &[Rc]) -> Result, StoryError> { match ¶ms[0].value { ValueType::String(op1) => match ¶ms[1].value { - ValueType::String(op2) => { - Ok(Rc::new(Value::new::(op1.string.contains(&op2.string)))) - } + ValueType::String(op2) => Ok(Rc::new(Value::new::( + op1.string.contains(&op2.string), + ))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), @@ -895,9 +903,9 @@ impl NativeFunctionCall { fn hasnt(&self, params: &[Rc]) -> Result, StoryError> { match ¶ms[0].value { ValueType::String(op1) => match ¶ms[1].value { - ValueType::String(op2) => { - Ok(Rc::new(Value::new::(!op1.string.contains(&op2.string)))) - } + ValueType::String(op2) => Ok(Rc::new(Value::new::( + !op1.string.contains(&op2.string), + ))), _ => Err(StoryError::InvalidStoryState( "Operation not available for type.".to_owned(), )), diff --git a/lib/src/story/choices.rs b/lib/src/story/choices.rs index cc2d86c..d19ef50 100644 --- a/lib/src/story/choices.rs +++ b/lib/src/story/choices.rs @@ -1,6 +1,6 @@ use crate::{ choice::Choice, choice_point::ChoicePoint, object::Object, path::Path, story::Story, - story_error::StoryError, tag::Tag, value::Value, + story_error::StoryError, tag::Tag, value::Value, value_type::StringValue, }; use std::rc::Rc; /// # Choices @@ -156,7 +156,7 @@ impl Story { fn pop_choice_string_and_tags(&mut self, tags: &mut Vec) -> String { let obj = self.get_state_mut().pop_evaluation_stack(); - let choice_only_str_val = Value::get_string_value(obj.as_ref()).unwrap(); + let choice_only_str_val = Value::get_value::<&StringValue>(obj.as_ref()).unwrap(); while !self.get_state().evaluation_stack.is_empty() && self diff --git a/lib/src/story/control_logic.rs b/lib/src/story/control_logic.rs index 241fb39..467984f 100644 --- a/lib/src/story/control_logic.rs +++ b/lib/src/story/control_logic.rs @@ -6,6 +6,7 @@ use crate::{ ink_list_item::InkListItem, native_function_call::NativeFunctionCall, object::RTObject, + path::Path, pointer, push_pop::PushPopType, story::Story, @@ -13,7 +14,7 @@ use crate::{ story_state::StoryState, tag::Tag, value::Value, - value_type::ValueType, + value_type::{StringValue, ValueType}, variable_assigment::VariableAssignment, variable_reference::VariableReference, void::Void, @@ -50,7 +51,7 @@ impl Story { .variables_state .get_variable_with_name(var_name.as_ref().unwrap(), -1) { - if let Some(target) = Value::get_divert_target_value(var_contents.as_ref()) { + if let Some(target) = Value::get_value::<&Path>(var_contents.as_ref()) { let p = Self::pointer_at_path(&self.main_content_container, target)?; self.get_state_mut().set_diverted_pointer(p); } else { @@ -159,7 +160,7 @@ impl Story { let mut override_tunnel_return_target = None; if pop_type == PushPopType::Tunnel { let popped = self.get_state_mut().pop_evaluation_stack(); - if let Some(v) = Value::get_divert_target_value(popped.as_ref()) { + if let Some(v) = Value::get_value::<&Path>(popped.as_ref()) { override_tunnel_return_target = Some(v.clone()); } @@ -259,7 +260,7 @@ impl Story { content_to_retain.push_back(obj.clone()); } - if Value::get_string_value(obj.as_ref()).is_some() { + if Value::get_value::<&StringValue>(obj.as_ref()).is_some() { content_stack_for_string.push_back(obj.clone()); } } @@ -299,9 +300,9 @@ impl Story { } CommandType::TurnsSince | CommandType::ReadCount => { let target = self.get_state_mut().pop_evaluation_stack(); - if Value::get_divert_target_value(target.as_ref()).is_none() { + if Value::get_value::<&Path>(target.as_ref()).is_none() { let mut extra_note = "".to_owned(); - if Value::get_int_value(target.as_ref()).is_some() { + if Value::get_value::(target.as_ref()).is_some() { extra_note = format!(". Did you accidentally pass a read count ('knot_name') instead of a target {}", "('-> knot_name')?").to_owned(); } @@ -310,7 +311,7 @@ impl Story { , extra_note))); } - let target = Value::get_divert_target_value(target.as_ref()).unwrap(); + let target = Value::get_value::<&Path>(target.as_ref()).unwrap(); let otmp = self.content_at_path(target).correct_obj(); let container = match &otmp { Some(o) => o.clone().into_any().downcast::().ok(), @@ -352,13 +353,13 @@ impl Story { CommandType::Random => { let mut max_int = None; let o = self.get_state_mut().pop_evaluation_stack(); - if let Some(v) = Value::get_int_value(o.as_ref()) { + if let Some(v) = Value::get_value::(o.as_ref()) { max_int = Some(v); } let o = self.get_state_mut().pop_evaluation_stack(); let mut min_int = None; - if let Some(v) = Value::get_int_value(o.as_ref()) { + if let Some(v) = Value::get_value::(o.as_ref()) { min_int = Some(v); } @@ -398,7 +399,7 @@ impl Story { CommandType::SeedRandom => { let mut seed: Option = None; let o = self.get_state_mut().pop_evaluation_stack(); - if let Some(v) = Value::get_int_value(o.as_ref()) { + if let Some(v) = Value::get_value::(o.as_ref()) { seed = Some(v); } @@ -451,12 +452,12 @@ impl Story { let mut int_val: Option = None; let mut list_name_val: Option<&String> = None; let o = self.get_state_mut().pop_evaluation_stack(); - if let Some(v) = Value::get_int_value(o.as_ref()) { + if let Some(v) = Value::get_value::(o.as_ref()) { int_val = Some(v); } let o = self.get_state_mut().pop_evaluation_stack(); - if let Some(s) = Value::get_string_value(o.as_ref()) { + if let Some(s) = Value::get_value::<&StringValue>(o.as_ref()) { list_name_val = Some(&s.string); } @@ -499,7 +500,7 @@ impl Story { p = self.get_state_mut().pop_evaluation_stack(); let min = p.into_any().downcast::(); p = self.get_state_mut().pop_evaluation_stack(); - let target_list = Value::get_list_value(p.as_ref()); + let target_list = Value::get_value::<&InkList>(p.as_ref()); if target_list.is_none() || min.is_err() || max.is_err() { return Err(StoryError::InvalidStoryState( "Expected List, minimum and maximum for LIST_RANGE".to_owned(), @@ -514,7 +515,7 @@ impl Story { } CommandType::ListRandom => { let o = self.get_state_mut().pop_evaluation_stack(); - let list = Value::get_list_value(o.as_ref()); + let list = Value::get_value::<&InkList>(o.as_ref()); if list.is_none() { return Err(StoryError::InvalidStoryState( "Expected list for LIST_RANDOM".to_owned(), @@ -597,7 +598,7 @@ impl Story { } } - if let Some(sv) = Value::get_string_value(obj.as_ref()) { + if let Some(sv) = Value::get_value::<&StringValue>(obj.as_ref()) { content_stack_for_tag.push(sv.string.clone()); } } diff --git a/lib/src/story/mod.rs b/lib/src/story/mod.rs index 8d80490..cb23d72 100644 --- a/lib/src/story/mod.rs +++ b/lib/src/story/mod.rs @@ -46,6 +46,7 @@ mod misc { use crate::{ json::{json_read, json_read_stream}, object::{Object, RTObject}, + path::Path, story::{Story, INK_VERSION_CURRENT}, story_error::StoryError, story_state::StoryState, @@ -111,7 +112,7 @@ mod misc { let truthy = false; if let Some(val) = obj.as_ref().as_any().downcast_ref::() { - if let Some(target_path) = Value::get_divert_target_value(obj.as_ref()) { + if let Some(target_path) = Value::get_value::<&Path>(obj.as_ref()) { return Err(StoryError::InvalidStoryState(format!("Shouldn't use a divert target (to {}) as a conditional value. Did you intend a function call 'likeThis()' or a read count check 'likeThis'? (no arrows)", target_path))); } @@ -123,19 +124,19 @@ mod misc { pub(crate) fn next_sequence_shuffle_index(&mut self) -> Result { let pop_evaluation_stack = self.get_state_mut().pop_evaluation_stack(); - let num_elements = if let Some(v) = Value::get_int_value(pop_evaluation_stack.as_ref()) - { - v - } else { - return Err(StoryError::InvalidStoryState( - "Expected number of elements in sequence for shuffle index".to_owned(), - )); - }; + let num_elements = + if let Some(v) = Value::get_value::(pop_evaluation_stack.as_ref()) { + v + } else { + return Err(StoryError::InvalidStoryState( + "Expected number of elements in sequence for shuffle index".to_owned(), + )); + }; let seq_container = self.get_state().get_current_pointer().container.unwrap(); let seq_count = if let Some(v) = - Value::get_int_value(self.get_state_mut().pop_evaluation_stack().as_ref()) + Value::get_value::(self.get_state_mut().pop_evaluation_stack().as_ref()) { v } else { diff --git a/lib/src/story/progress.rs b/lib/src/story/progress.rs index b387de5..06df858 100644 --- a/lib/src/story/progress.rs +++ b/lib/src/story/progress.rs @@ -9,6 +9,7 @@ use crate::{ story::{errors::ErrorType, OutputStateChange, Story}, story_error::StoryError, value::Value, + value_type::VariablePointerValue, void::Void, }; use std::{self, rc::Rc}; @@ -443,8 +444,9 @@ impl Story { // to our current (possibly temporary) context index. And make a // copy of the pointer // so that we're not editing the original runtime Object. - let var_pointer = - Value::get_variable_pointer_value(current_content_obj.as_ref().unwrap().as_ref()); + let var_pointer = Value::get_value::<&VariablePointerValue>( + current_content_obj.as_ref().unwrap().as_ref(), + ); if let Some(var_pointer) = var_pointer { if var_pointer.context_index == -1 { diff --git a/lib/src/story/tags.rs b/lib/src/story/tags.rs index 52c30d1..4280922 100644 --- a/lib/src/story/tags.rs +++ b/lib/src/story/tags.rs @@ -5,6 +5,7 @@ use crate::{ story::Story, story_error::StoryError, value::Value, + value_type::StringValue, }; /// # Tags @@ -54,7 +55,9 @@ impl Story { }, _ => { if in_tag { - if let Some(string_value) = Value::get_string_value(content.as_ref()) { + if let Some(string_value) = + Value::get_value::<&StringValue>(content.as_ref()) + { tags.push(string_value.string.clone()); } else { return Err( diff --git a/lib/src/story_state.rs b/lib/src/story_state.rs index 8c62be4..50385a5 100644 --- a/lib/src/story_state.rs +++ b/lib/src/story_state.rs @@ -19,7 +19,7 @@ use crate::{ story_error::StoryError, tag::Tag, value::Value, - value_type::ValueType, + value_type::{StringValue, ValueType}, variables_state::VariablesState, void::Void, }; @@ -200,7 +200,7 @@ impl StoryState { let mut in_tag = false; for output_obj in self.get_output_stream() { - let text_content = Value::get_string_value(output_obj.as_ref()); + let text_content = Value::get_value::<&StringValue>(output_obj.as_ref()); if let (false, Some(text_content)) = (in_tag, text_content) { sb.push_str(&text_content.string); @@ -258,7 +258,9 @@ impl StoryState { _ => {} } } else if in_tag { - if let Some(string_value) = Value::get_string_value(output_obj.as_ref()) { + if let Some(string_value) = + Value::get_value::<&StringValue>(output_obj.as_ref()) + { sb.push_str(&string_value.string); } if let Some(tag) = output_obj.as_ref().as_any().downcast_ref::() { @@ -360,7 +362,7 @@ impl StoryState { } pub fn push_evaluation_stack(&mut self, obj: Rc) { - if let Some(list) = Value::get_list_value(obj.as_ref()) { + if let Some(list) = Value::get_value::<&InkList>(obj.as_ref()) { let origin_names = list.get_origin_names(); list.origins.borrow_mut().clear(); @@ -377,7 +379,7 @@ impl StoryState { } pub fn push_to_output_stream(&mut self, obj: Rc) { - let text = Value::get_string_value(obj.as_ref()); + let text = Value::get_value::<&StringValue>(obj.as_ref()); if let Some(s) = text { let list_text = StoryState::try_splitting_head_tail_whitespace(&s.string); @@ -530,7 +532,7 @@ impl StoryState { fn push_to_output_stream_individual(&mut self, obj: Rc) { let glue = obj.clone().into_any().downcast::(); - let text = Value::get_string_value(obj.as_ref()); + let text = Value::get_value::<&StringValue>(obj.as_ref()); let mut include_in_output = true; // New glue, so chomp away any whitespace from the end of the stream @@ -632,7 +634,7 @@ impl StoryState { if let Some(obj) = output_stream.get(i as usize) { if obj.as_ref().as_any().is::() { break; - } else if let Some(sv) = Value::get_string_value(obj.as_ref()) { + } else if let Some(sv) = Value::get_value::<&StringValue>(obj.as_ref()) { if sv.is_non_whitespace() { break; } else if sv.is_newline { @@ -647,7 +649,7 @@ impl StoryState { if remove_whitespace_from >= 0 { i = remove_whitespace_from; while i < output_stream.len() as i32 { - if Value::get_string_value(output_stream[i as usize].as_ref()).is_some() { + if Value::get_value::<&StringValue>(output_stream[i as usize].as_ref()).is_some() { output_stream.remove(i as usize); } else { i += 1; @@ -975,7 +977,7 @@ impl StoryState { break; } - if let Some(txt) = Value::get_string_value(obj.as_ref()) { + if let Some(txt) = Value::get_value::<&StringValue>(obj.as_ref()) { if txt.is_newline || txt.is_inline_whitespace { self.get_output_stream_mut().remove(i as usize); self.output_stream_dirty(); diff --git a/lib/src/value.rs b/lib/src/value.rs index e62335c..b091a76 100644 --- a/lib/src/value.rs +++ b/lib/src/value.rs @@ -47,137 +47,149 @@ impl> From for Value { } } -impl Value { - pub fn new_value_type(valuetype: ValueType) -> Self { - Self { - obj: Object::new(), - value:valuetype, - } - } - - pub fn new>(v: T) -> Self { - v.into() - } - - pub fn new_variable_pointer(variable_name: &str, context_index: i32) -> Self { - Self { - obj: Object::new(), - value: ValueType::VariablePointer(VariablePointerValue { - variable_name: variable_name.to_string(), - context_index, - }), - } - } - - pub fn is_truthy(&self) -> Result { - match &self.value { - ValueType::Bool(v) => Ok(*v), - ValueType::Int(v) => Ok(*v != 0), - ValueType::Float(v) => Ok(*v != 0.0), - ValueType::String(v) => Ok(!v.string.is_empty()), - ValueType::DivertTarget(_) => Err(StoryError::InvalidStoryState( - "Shouldn't be checking the truthiness of a divert target".to_owned(), - )), - ValueType::VariablePointer(_) => Err(StoryError::InvalidStoryState( - "Shouldn't be checking the truthiness of a variable pointer".to_owned(), - )), - ValueType::List(l) => Ok(!l.items.is_empty()), - } - } - - pub fn get_string_value(o: &dyn RTObject) -> Option<&StringValue> { +impl<'val> TryFrom<&'val dyn RTObject> for &'val StringValue { + type Error = (); + fn try_from(o: &dyn RTObject) -> Result<&StringValue, Self::Error> { match o.as_any().downcast_ref::() { Some(v) => match &v.value { - ValueType::String(v) => Some(v), - _ => None, + ValueType::String(v) => Ok(v), + _ => Err(()), }, - None => None, + None => Err(()), } } - - pub fn get_variable_pointer_value(o: &dyn RTObject) -> Option<&VariablePointerValue> { +} +impl<'val> TryFrom<&'val dyn RTObject> for &'val VariablePointerValue { + type Error = (); + fn try_from(o: &dyn RTObject) -> Result<&VariablePointerValue, Self::Error> { match o.as_any().downcast_ref::() { Some(v) => match &v.value { - ValueType::VariablePointer(v) => Some(v), - _ => None, + ValueType::VariablePointer(v) => Ok(v), + _ => Err(()), }, - None => None, + None => Err(()), } } - - pub fn get_divert_target_value(o: &dyn RTObject) -> Option<&Path> { +} +impl<'val> TryFrom<&'val dyn RTObject> for &'val Path { + type Error = (); + fn try_from(o: &dyn RTObject) -> Result<&Path, Self::Error> { match o.as_any().downcast_ref::() { Some(v) => match &v.value { - ValueType::DivertTarget(p) => Some(p), - _ => None, + ValueType::DivertTarget(p) => Ok(p), + _ => Err(()), }, - None => None, + None => Err(()), } } - - pub(crate) fn get_bool_value(o: &dyn RTObject) -> Option { +} +impl TryFrom<&dyn RTObject> for i32 { + type Error = (); + fn try_from(o: &dyn RTObject) -> Result { match o.as_any().downcast_ref::() { Some(v) => match &v.value { - ValueType::Bool(v) => Some(*v), - _ => None, + ValueType::Int(v) => Ok(*v), + _ => Err(()), }, - None => None, + None => Err(()), } } - - pub fn get_int_value(o: &dyn RTObject) -> Option { +} +impl TryFrom<&dyn RTObject> for f32 { + type Error = (); + fn try_from(o: &dyn RTObject) -> Result { match o.as_any().downcast_ref::() { Some(v) => match &v.value { - ValueType::Int(v) => Some(*v), - _ => None, + ValueType::Float(v) => Ok(*v), + _ => Err(()), }, - None => None, + None => Err(()), } } - - pub fn get_float_value(o: &dyn RTObject) -> Option { +} +impl<'val> TryFrom<&'val mut dyn RTObject> for &'val mut InkList { + type Error = (); + fn try_from(o: &mut dyn RTObject) -> Result<&mut InkList, Self::Error> { + match o.as_any_mut().downcast_mut::() { + Some(v) => match &mut v.value { + ValueType::List(v) => Ok(v), + _ => Err(()), + }, + None => Err(()), + } + } +} +impl<'val> TryFrom<&'val dyn RTObject> for &'val InkList { + type Error = (); + fn try_from(o: &dyn RTObject) -> Result<&InkList, Self::Error> { match o.as_any().downcast_ref::() { Some(v) => match &v.value { - ValueType::Float(v) => Some(*v), - _ => None, + ValueType::List(v) => Ok(v), + _ => Err(()), }, - None => None, + None => Err(()), } } +} - pub fn get_list_value_mut(o: &mut dyn RTObject) -> Option<&mut InkList> { - match o.as_any_mut().downcast_mut::() { - Some(v) => match &mut v.value { - ValueType::List(v) => Some(v), - _ => None, - }, - None => None, +impl Value { + pub fn new_value_type(valuetype: ValueType) -> Self { + Self { + obj: Object::new(), + value: valuetype, } } - pub fn get_list_value(o: &dyn RTObject) -> Option<&InkList> { - match o.as_any().downcast_ref::() { - Some(v) => match &v.value { - ValueType::List(v) => Some(v), - _ => None, - }, - None => None, + pub fn new>(v: T) -> Self { + v.into() + } + + pub fn new_variable_pointer(variable_name: &str, context_index: i32) -> Self { + Self { + obj: Object::new(), + value: ValueType::VariablePointer(VariablePointerValue { + variable_name: variable_name.to_string(), + context_index, + }), } } - pub fn get_divert_value(o: &dyn RTObject) -> Option<&Path> { + pub fn get_value<'val, T>(o: &'val dyn RTObject) -> Option + where + &'val dyn RTObject: TryInto, + { + o.try_into().ok() + } + + pub(crate) fn get_bool_value(o: &dyn RTObject) -> Option { match o.as_any().downcast_ref::() { Some(v) => match &v.value { - ValueType::DivertTarget(v) => Some(v), + ValueType::Bool(v) => Some(*v), _ => None, }, None => None, } } + pub fn is_truthy(&self) -> Result { + match &self.value { + ValueType::Bool(v) => Ok(*v), + ValueType::Int(v) => Ok(*v != 0), + ValueType::Float(v) => Ok(*v != 0.0), + ValueType::String(v) => Ok(!v.string.is_empty()), + ValueType::DivertTarget(_) => Err(StoryError::InvalidStoryState( + "Shouldn't be checking the truthiness of a divert target".to_owned(), + )), + ValueType::VariablePointer(_) => Err(StoryError::InvalidStoryState( + "Shouldn't be checking the truthiness of a variable pointer".to_owned(), + )), + ValueType::List(l) => Ok(!l.items.is_empty()), + } + } + pub fn retain_list_origins_for_assignment(old_value: &dyn RTObject, new_value: &dyn RTObject) { - if let Some(old_list) = Self::get_list_value(old_value) { - if let Some(new_list) = Self::get_list_value(new_value) { + if let Some(old_list) = Self::get_value::<&InkList>(old_value) { + if let Some(new_list) = Self::get_value::<&InkList>(new_value) { if new_list.items.is_empty() { new_list.set_initial_origin_names(old_list.get_origin_names()); } diff --git a/lib/src/variables_state.rs b/lib/src/variables_state.rs index 84ec874..2587876 100644 --- a/lib/src/variables_state.rs +++ b/lib/src/variables_state.rs @@ -115,7 +115,7 @@ impl VariablesState { let mut value = value; // Constructing new variable pointer reference if var_ass.is_new_declaration { - if let Some(var_pointer) = Value::get_variable_pointer_value(value.as_ref()) { + if let Some(var_pointer) = Value::get_value::<&VariablePointerValue>(value.as_ref()) { value = self.resolve_variable_pointer(var_pointer); } } else { @@ -127,7 +127,7 @@ impl VariablesState { match existing_pointer { Some(existing_pointer) => { - match Value::get_variable_pointer_value(existing_pointer.as_ref()) { + match Value::get_value::<&VariablePointerValue>(existing_pointer.as_ref()) { Some(pv) => { name = pv.variable_name.to_string(); context_index = pv.context_index; @@ -178,7 +178,7 @@ impl VariablesState { // create // a chain of indirection by just returning the final target. if let Some(value_of_variable_pointed_to) = value_of_variable_pointed_to { - if Value::get_variable_pointer_value(value_of_variable_pointed_to.as_ref()).is_some() { + if Value::get_value::<&VariablePointerValue>(value_of_variable_pointed_to.as_ref()).is_some() { return value_of_variable_pointed_to; } } @@ -320,7 +320,7 @@ impl VariablesState { let var_value = self.get_raw_variable_with_name(name, context_index); // Get value from pointer? if let Some(vv) = var_value.clone() { - if let Some(var_pointer) = Value::get_variable_pointer_value(vv.as_ref()) { + if let Some(var_pointer) = Value::get_value::<&VariablePointerValue>(vv.as_ref()) { return self.value_at_variable_pointer(var_pointer); } } From 0b6e46b0fdf3c2cf0102eb3961520714740c5efb Mon Sep 17 00:00:00 2001 From: IFcoltransG <47414286+IFcoltransG@users.noreply.github.com> Date: Thu, 5 Dec 2024 15:54:32 +1300 Subject: [PATCH 3/3] Rustfmt --- lib/src/json/json_read_stream.rs | 4 +++- lib/src/variables_state.rs | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/src/json/json_read_stream.rs b/lib/src/json/json_read_stream.rs index b6aeafd..139e3e2 100644 --- a/lib/src/json/json_read_stream.rs +++ b/lib/src/json/json_read_stream.rs @@ -375,7 +375,9 @@ fn jtoken_to_runtime_object( } tok.expect('}')?; - return Ok(ArrayElement::RTObject(Rc::new(Value::new::(raw_list)))); + return Ok(ArrayElement::RTObject(Rc::new(Value::new::( + raw_list, + )))); } // Used when serialising save state only diff --git a/lib/src/variables_state.rs b/lib/src/variables_state.rs index 2587876..d467912 100644 --- a/lib/src/variables_state.rs +++ b/lib/src/variables_state.rs @@ -178,7 +178,9 @@ impl VariablesState { // create // a chain of indirection by just returning the final target. if let Some(value_of_variable_pointed_to) = value_of_variable_pointed_to { - if Value::get_value::<&VariablePointerValue>(value_of_variable_pointed_to.as_ref()).is_some() { + if Value::get_value::<&VariablePointerValue>(value_of_variable_pointed_to.as_ref()) + .is_some() + { return value_of_variable_pointed_to; } }