yarnspinner_core/generated/
ext.rs

1//! Contains extensions to generated types that in the original implementation are sprinkled around the repo via partial classes
2
3use crate::prelude::*;
4use core::error::Error;
5use core::fmt::{Debug, Display};
6
7impl From<String> for Operand {
8    fn from(s: String) -> Self {
9        Self {
10            value: Some(OperandValue::StringValue(s)),
11        }
12    }
13}
14
15impl From<f32> for Operand {
16    fn from(f: f32) -> Self {
17        Self {
18            value: Some(OperandValue::FloatValue(f)),
19        }
20    }
21}
22
23impl From<usize> for Operand {
24    fn from(f: usize) -> Self {
25        Self::from(f as f32)
26    }
27}
28
29impl From<bool> for Operand {
30    fn from(b: bool) -> Self {
31        Self {
32            value: Some(OperandValue::BoolValue(b)),
33        }
34    }
35}
36
37impl TryFrom<Operand> for String {
38    type Error = ();
39
40    fn try_from(value: Operand) -> Result<Self, Self::Error> {
41        match value.value {
42            Some(OperandValue::StringValue(s)) => Ok(s),
43            _ => Err(()),
44        }
45    }
46}
47
48impl TryFrom<Operand> for f32 {
49    type Error = ();
50
51    fn try_from(value: Operand) -> Result<Self, Self::Error> {
52        match value.value {
53            Some(OperandValue::FloatValue(f)) => Ok(f),
54            _ => Err(()),
55        }
56    }
57}
58
59impl TryFrom<Operand> for usize {
60    type Error = ();
61
62    fn try_from(value: Operand) -> Result<Self, Self::Error> {
63        match value.value {
64            // [sic] TODO: we only have float operands, which is
65            // unpleasant. we should make 'int' operands a
66            // valid type, but doing that implies that the
67            // language differentiates between floats and
68            // ints, which it doesn't.
69            Some(OperandValue::FloatValue(f)) => Ok(f as usize),
70            _ => Err(()),
71        }
72    }
73}
74
75impl TryFrom<Operand> for bool {
76    type Error = ();
77
78    fn try_from(value: Operand) -> Result<Self, Self::Error> {
79        match value.value {
80            Some(OperandValue::BoolValue(b)) => Ok(b),
81            _ => Err(()),
82        }
83    }
84}
85
86impl From<Operand> for YarnValue {
87    fn from(value: Operand) -> Self {
88        let value = value.value.unwrap();
89        match value {
90            OperandValue::StringValue(s) => s.into(),
91            OperandValue::FloatValue(f) => f.into(),
92            OperandValue::BoolValue(b) => b.into(),
93        }
94    }
95}
96
97#[derive(Debug, Clone, Copy, PartialEq, Eq)]
98#[cfg_attr(feature = "bevy", derive(Reflect))]
99#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
100#[cfg_attr(feature = "bevy", reflect(Debug, PartialEq))]
101#[cfg_attr(
102    all(feature = "bevy", feature = "serde"),
103    reflect(Serialize, Deserialize)
104)]
105pub struct InvalidOpCodeError(pub i32);
106
107impl Error for InvalidOpCodeError {}
108
109impl Display for InvalidOpCodeError {
110    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
111        write!(f, "{:?} is not a valid OpCode", self.0)
112    }
113}
114
115impl Program {
116    /// Creates a new Program by merging multiple Programs together.
117    ///
118    /// The new program will contain every node from every input program.
119    /// Returns [`None`] if the input is empty.
120    pub fn combine(programs: Vec<Program>) -> Option<Self> {
121        if programs.is_empty() {
122            return None;
123        }
124        let mut output = Program::default();
125        for program in programs {
126            for (node_name, node) in program.nodes {
127                assert!(
128                    !output.nodes.contains_key(&node_name),
129                    "This program already contains a node named {node_name}",
130                );
131                output.nodes.insert(node_name, node);
132            }
133            output.initial_values.extend(program.initial_values);
134        }
135        Some(output)
136    }
137}
138
139impl Instruction {
140    pub fn read_operand<T>(&self, index: usize) -> T
141    where
142        T: TryFrom<Operand>,
143        <T as TryFrom<Operand>>::Error: Debug,
144    {
145        self.operands[index]
146            .clone()
147            .try_into()
148            .unwrap_or_else(|e| panic!("Failed to convert operand {index}: {e:?}",))
149    }
150}