Skip to main content

bladeink/
value.rs

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        // SAFETY: `ValueType` is `repr(u8)` so every variant has the layout
204        // of a struct with its first field being the `u8` discriminant,
205        // ensuring the `u8` can be read from a pointer to the enum.
206        // See e.g. https://doc.rust-lang.org/std/mem/fn.discriminant.html#accessing-the-numeric-value-of-the-discriminant
207        let ptr_to_option = (v as *const ValueType) as *const u8;
208        unsafe { *ptr_to_option }
209    }
210
211    // If None is returned means that casting is not needed
212    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}