1use std::fmt;
2
3use crate::{
4 ink_list::InkList,
5 object::{Object, RTObject},
6 path::Path,
7 story_error::StoryError,
8 value_type::{StringValue, ValueType, VariablePointerValue},
9};
10
11const CAST_BOOL: u8 = 0;
12const CAST_INT: u8 = 1;
13const CAST_FLOAT: u8 = 2;
14const CAST_LIST: u8 = 3;
15const CAST_STRING: u8 = 4;
16const CAST_DIVERT_TARGET: u8 = 5;
17const CAST_VARIABLE_POINTER: u8 = 6;
18
19pub struct Value {
20 obj: Object,
21 pub value: ValueType,
22}
23
24impl RTObject for Value {
25 fn get_object(&self) -> &Object {
26 &self.obj
27 }
28}
29
30impl fmt::Display for Value {
31 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32 match &self.value {
33 ValueType::Bool(v) => write!(f, "{}", v),
34 ValueType::Int(v) => write!(f, "{}", v),
35 ValueType::Float(v) => write!(f, "{}", v),
36 ValueType::String(v) => write!(f, "{}", v.string),
37 ValueType::DivertTarget(p) => write!(f, "DivertTargetValue({})", p),
38 ValueType::VariablePointer(v) => write!(f, "VariablePointerValue({})", v.variable_name),
39 ValueType::List(l) => write!(f, "{}", l),
40 }
41 }
42}
43
44impl<T: Into<ValueType>> From<T> for Value {
45 fn from(value: T) -> Self {
46 Self::new_value_type(value.into())
47 }
48}
49
50impl<'val> TryFrom<&'val dyn RTObject> for &'val StringValue {
51 type Error = ();
52 fn try_from(o: &dyn RTObject) -> Result<&StringValue, Self::Error> {
53 match o.as_any().downcast_ref::<Value>() {
54 Some(v) => match &v.value {
55 ValueType::String(v) => Ok(v),
56 _ => Err(()),
57 },
58 None => Err(()),
59 }
60 }
61}
62impl<'val> TryFrom<&'val dyn RTObject> for &'val VariablePointerValue {
63 type Error = ();
64 fn try_from(o: &dyn RTObject) -> Result<&VariablePointerValue, Self::Error> {
65 match o.as_any().downcast_ref::<Value>() {
66 Some(v) => match &v.value {
67 ValueType::VariablePointer(v) => Ok(v),
68 _ => Err(()),
69 },
70 None => Err(()),
71 }
72 }
73}
74impl<'val> TryFrom<&'val dyn RTObject> for &'val Path {
75 type Error = ();
76 fn try_from(o: &dyn RTObject) -> Result<&Path, Self::Error> {
77 match o.as_any().downcast_ref::<Value>() {
78 Some(v) => match &v.value {
79 ValueType::DivertTarget(p) => Ok(p),
80 _ => Err(()),
81 },
82 None => Err(()),
83 }
84 }
85}
86impl TryFrom<&dyn RTObject> for i32 {
87 type Error = ();
88 fn try_from(o: &dyn RTObject) -> Result<i32, Self::Error> {
89 match o.as_any().downcast_ref::<Value>() {
90 Some(v) => match &v.value {
91 ValueType::Int(v) => Ok(*v),
92 _ => Err(()),
93 },
94 None => Err(()),
95 }
96 }
97}
98impl TryFrom<&dyn RTObject> for f32 {
99 type Error = ();
100 fn try_from(o: &dyn RTObject) -> Result<f32, Self::Error> {
101 match o.as_any().downcast_ref::<Value>() {
102 Some(v) => match &v.value {
103 ValueType::Float(v) => Ok(*v),
104 _ => Err(()),
105 },
106 None => Err(()),
107 }
108 }
109}
110impl<'val> TryFrom<&'val mut dyn RTObject> for &'val mut InkList {
111 type Error = ();
112 fn try_from(o: &mut dyn RTObject) -> Result<&mut InkList, Self::Error> {
113 match o.as_any_mut().downcast_mut::<Value>() {
114 Some(v) => match &mut v.value {
115 ValueType::List(v) => Ok(v),
116 _ => Err(()),
117 },
118 None => Err(()),
119 }
120 }
121}
122impl<'val> TryFrom<&'val dyn RTObject> for &'val InkList {
123 type Error = ();
124 fn try_from(o: &dyn RTObject) -> Result<&InkList, Self::Error> {
125 match o.as_any().downcast_ref::<Value>() {
126 Some(v) => match &v.value {
127 ValueType::List(v) => Ok(v),
128 _ => Err(()),
129 },
130 None => Err(()),
131 }
132 }
133}
134
135impl Value {
136 pub fn new_value_type(valuetype: ValueType) -> Self {
137 Self {
138 obj: Object::new(),
139 value: valuetype,
140 }
141 }
142
143 pub fn new<T: Into<Value>>(v: T) -> Self {
144 v.into()
145 }
146
147 pub fn new_variable_pointer(variable_name: &str, context_index: i32) -> Self {
148 Self {
149 obj: Object::new(),
150 value: ValueType::VariablePointer(VariablePointerValue {
151 variable_name: variable_name.to_string(),
152 context_index,
153 }),
154 }
155 }
156
157 pub fn get_value<'val, T>(o: &'val dyn RTObject) -> Option<T>
158 where
159 &'val dyn RTObject: TryInto<T>,
160 {
161 o.try_into().ok()
162 }
163
164 pub(crate) fn get_bool_value(o: &dyn RTObject) -> Option<bool> {
165 match o.as_any().downcast_ref::<Value>() {
166 Some(v) => match &v.value {
167 ValueType::Bool(v) => Some(*v),
168 _ => None,
169 },
170 None => None,
171 }
172 }
173
174 pub fn is_truthy(&self) -> Result<bool, StoryError> {
175 match &self.value {
176 ValueType::Bool(v) => Ok(*v),
177 ValueType::Int(v) => Ok(*v != 0),
178 ValueType::Float(v) => Ok(*v != 0.0),
179 ValueType::String(v) => Ok(!v.string.is_empty()),
180 ValueType::DivertTarget(_) => Err(StoryError::InvalidStoryState(
181 "Shouldn't be checking the truthiness of a divert target".to_owned(),
182 )),
183 ValueType::VariablePointer(_) => Err(StoryError::InvalidStoryState(
184 "Shouldn't be checking the truthiness of a variable pointer".to_owned(),
185 )),
186 ValueType::List(l) => Ok(!l.items.is_empty()),
187 }
188 }
189
190 pub fn retain_list_origins_for_assignment(old_value: &dyn RTObject, new_value: &dyn RTObject) {
191 if let Some(old_list) = Self::get_value::<&InkList>(old_value) {
192 if let Some(new_list) = Self::get_value::<&InkList>(new_value) {
193 if new_list.items.is_empty() {
194 new_list.set_initial_origin_names(old_list.get_origin_names());
195 }
196 }
197 }
198 }
199
200 pub fn get_cast_ordinal(&self) -> u8 {
201 let v = &self.value;
202
203 let ptr_to_option = (v as *const ValueType) as *const u8;
208 unsafe { *ptr_to_option }
209 }
210
211 pub fn cast(&self, cast_dest_type: u8) -> Result<Option<Value>, StoryError> {
213 match &self.value {
214 ValueType::Bool(v) => match cast_dest_type {
215 CAST_BOOL => Ok(None),
216 CAST_INT => {
217 if *v {
218 Ok(Some(Self::new::<i32>(1)))
219 } else {
220 Ok(Some(Self::new::<i32>(0)))
221 }
222 }
223 CAST_FLOAT => {
224 if *v {
225 Ok(Some(Self::new::<f32>(1.0)))
226 } else {
227 Ok(Some(Self::new::<f32>(0.0)))
228 }
229 }
230 CAST_STRING => {
231 if *v {
232 Ok(Some(Self::new::<&str>("true")))
233 } else {
234 Ok(Some(Self::new::<&str>("false")))
235 }
236 }
237 _ => Err(StoryError::InvalidStoryState(
238 "Cast not allowed for bool".to_owned(),
239 )),
240 },
241 ValueType::Int(v) => match cast_dest_type {
242 CAST_BOOL => {
243 if *v == 0 {
244 Ok(Some(Self::new::<bool>(false)))
245 } else {
246 Ok(Some(Self::new::<bool>(true)))
247 }
248 }
249 CAST_INT => Ok(None),
250 CAST_FLOAT => Ok(Some(Self::new::<f32>(*v as f32))),
251 CAST_STRING => Ok(Some(Self::new::<&str>(&v.to_string()))),
252 _ => Err(StoryError::InvalidStoryState(
253 "Cast not allowed for int".to_owned(),
254 )),
255 },
256 ValueType::Float(v) => match cast_dest_type {
257 CAST_BOOL => {
258 if *v == 0.0 {
259 Ok(Some(Self::new::<bool>(false)))
260 } else {
261 Ok(Some(Self::new::<bool>(true)))
262 }
263 }
264 CAST_INT => Ok(Some(Self::new::<i32>(*v as i32))),
265 CAST_FLOAT => Ok(None),
266 CAST_STRING => Ok(Some(Self::new::<&str>(&v.to_string()))),
267 _ => Err(StoryError::InvalidStoryState(
268 "Cast not allowed for float".to_owned(),
269 )),
270 },
271 ValueType::String(v) => match cast_dest_type {
272 CAST_INT => Ok(Some(Self::new::<i32>(v.string.parse::<i32>().unwrap()))),
273 CAST_FLOAT => Ok(Some(Self::new::<f32>(v.string.parse::<f32>().unwrap()))),
274 CAST_STRING => Ok(None),
275 _ => Err(StoryError::InvalidStoryState(
276 "Cast not allowed for string".to_owned(),
277 )),
278 },
279 ValueType::List(l) => match cast_dest_type {
280 CAST_INT => {
281 let max = l.get_max_item();
282 match max {
283 Some(i) => Ok(Some(Self::new::<i32>(i.1))),
284 None => Ok(Some(Self::new::<i32>(0))),
285 }
286 }
287 CAST_FLOAT => {
288 let max = l.get_max_item();
289 match max {
290 Some(i) => Ok(Some(Self::new::<f32>(i.1 as f32))),
291 None => Ok(Some(Self::new::<f32>(0.0))),
292 }
293 }
294 CAST_LIST => Ok(None),
295 CAST_STRING => {
296 let max = l.get_max_item();
297 match max {
298 Some(i) => Ok(Some(Self::new::<&str>(&i.0.to_string()))),
299 None => Ok(Some(Self::new::<&str>(""))),
300 }
301 }
302 _ => Err(StoryError::InvalidStoryState(
303 "Cast not allowed for list".to_owned(),
304 )),
305 },
306 ValueType::DivertTarget(_) => match cast_dest_type {
307 CAST_DIVERT_TARGET => Ok(None),
308 _ => Err(StoryError::InvalidStoryState(
309 "Cast not allowed for divert".to_owned(),
310 )),
311 },
312 ValueType::VariablePointer(_) => match cast_dest_type {
313 CAST_VARIABLE_POINTER => Ok(None),
314 _ => Err(StoryError::InvalidStoryState(
315 "Cast not allowed for variable pointer".to_owned(),
316 )),
317 },
318 }
319 }
320}