hotg_rune_core/
value.rs

1use core::{
2    str::FromStr,
3    convert::TryFrom,
4    fmt::{Display, self, Formatter},
5};
6
7/// A dynamically typed value that may be passed back and forth across the
8/// runtime.
9#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
10#[non_exhaustive]
11pub enum Value {
12    Byte(u8),
13    Short(i16),
14    Integer(i32),
15    Float(f32),
16    SignedByte(i8),
17}
18
19impl Value {
20    /// Get a buffer big enough to be used with [`Value::to_le_bytes()`].
21    pub const fn buffer() -> [u8; core::mem::size_of::<Value>()] {
22        [0; core::mem::size_of::<Value>()]
23    }
24
25    pub fn from_le_bytes(ty: Type, bytes: &[u8]) -> Option<Self> {
26        match ty {
27            Type::Byte => bytes.get(0).copied().map(Value::Byte),
28            Type::SignedByte => {
29                if let [byte, ..] = bytes {
30                    Some(Value::SignedByte(i8::from_le_bytes([*byte])))
31                } else {
32                    None
33                }
34            },
35            Type::Short => {
36                const LEN: usize = core::mem::size_of::<i16>();
37
38                bytes.get(..LEN).map(|bytes| {
39                    let mut buffer = [0; LEN];
40                    buffer.copy_from_slice(bytes);
41                    Value::Short(i16::from_le_bytes(buffer))
42                })
43            },
44            Type::Integer => {
45                const LEN: usize = core::mem::size_of::<i32>();
46
47                bytes.get(..LEN).map(|bytes| {
48                    let mut buffer = [0; LEN];
49                    buffer.copy_from_slice(bytes);
50                    Value::Integer(i32::from_le_bytes(buffer))
51                })
52            },
53            Type::Float => {
54                const LEN: usize = core::mem::size_of::<f32>();
55
56                bytes.get(..LEN).map(|bytes| {
57                    let mut buffer = [0; LEN];
58                    buffer.copy_from_slice(bytes);
59                    Value::Float(f32::from_le_bytes(buffer))
60                })
61            },
62        }
63    }
64
65    /// Write this [`Value`]'s underlying value to the start of the provided
66    /// buffer, returning the number of bytes written.
67    ///
68    /// The buffer should have at least `core::mem::size_of::<Value>()` bytes.
69    /// You can use the [`Value::buffer()`] helper for creating an adequately
70    /// sized buffer.
71    pub fn to_le_bytes(self, buffer: &mut [u8]) -> usize {
72        match self {
73            Value::Byte(b) => {
74                buffer[0] = b;
75                1
76            },
77            Value::SignedByte(b) => {
78                buffer[..1].copy_from_slice(&b.to_le_bytes());
79                1
80            },
81            Value::Short(short) => {
82                let bytes = short.to_le_bytes();
83                buffer[..bytes.len()].copy_from_slice(&bytes);
84                bytes.len()
85            },
86            Value::Integer(int) => {
87                let bytes = int.to_le_bytes();
88                buffer[..bytes.len()].copy_from_slice(&bytes);
89                bytes.len()
90            },
91            Value::Float(float) => {
92                let bytes = float.to_le_bytes();
93                buffer[..bytes.len()].copy_from_slice(&bytes);
94                bytes.len()
95            },
96        }
97    }
98
99    pub fn ty(&self) -> Type {
100        match self {
101            Value::Byte(_) => Type::Byte,
102            Value::SignedByte(_) => Type::SignedByte,
103            Value::Short(_) => Type::Short,
104            Value::Integer(_) => Type::Integer,
105            Value::Float(_) => Type::Float,
106        }
107    }
108}
109
110impl Display for Value {
111    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
112        match self {
113            Value::Byte(b) => write!(f, "{}_u8", b),
114            Value::SignedByte(b) => write!(f, "{}_i8", b),
115            Value::Short(s) => write!(f, "{}_i16", s),
116            Value::Integer(i) => write!(f, "{}_i32", i),
117            Value::Float(float) => write!(f, "{:.1}", float),
118        }
119    }
120}
121
122impl FromStr for Value {
123    type Err = core::num::ParseFloatError;
124
125    fn from_str(s: &str) -> Result<Self, Self::Err> {
126        if let Ok(integer) = s.parse() {
127            Ok(Value::Integer(integer))
128        } else {
129            s.parse().map(Value::Float)
130        }
131    }
132}
133
134#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
135#[repr(u32)]
136#[non_exhaustive]
137pub enum Type {
138    /// A 32-bit signed integer.
139    Integer = 1,
140    /// A 32-bit floating point number.
141    Float = 2,
142    /// An 8-bit unsigned integer.
143    Byte = 5,
144    /// A 16-bit signed integer.
145    Short = 6,
146    // Note: Enum discriminant are important here. We want to stay
147    // compatible with PARAM_TYPE so the mobile runtime isn't broken.
148    //
149    // https://github.com/hotg-ai/runic_mobile/blob/94f9e72d6de8bd57c004952dc3ba31adc7603381/ios/Runner/hmr/hmr.hpp#L23-L29
150    //
151    // Don't forget to update TryFrom if you add new variants!
152    //
153    // We *could* use #[derive(FromPrimitive)] to automate things, but I'd
154    // prefer not to add a proc-macro dependency to the crate that every
155    // single rune or proc block will depend on.
156    SignedByte = 7,
157}
158
159impl From<Type> for u32 {
160    fn from(t: Type) -> Self { t as u32 }
161}
162
163impl TryFrom<u32> for Type {
164    type Error = ();
165
166    fn try_from(value: u32) -> Result<Self, Self::Error> {
167        match value {
168            1 => Ok(Type::Integer),
169            2 => Ok(Type::Float),
170            5 => Ok(Type::Byte),
171            6 => Ok(Type::Short),
172            _ => Err(()),
173        }
174    }
175}
176
177/// A Rust primitive which has a corresponding [`Type`] and can be converted to
178/// or from a [`Value`].
179pub trait AsType
180where
181    Self: Into<Value>,
182    Self: TryFrom<Value>,
183{
184    /// The corresponding [`Type`] variant.
185    const TYPE: Type;
186}
187
188macro_rules! impl_as_type {
189    ($($type:ty => $variant:ident),* $(,)?) => {
190        $(
191            impl AsType for $type {
192                const TYPE: Type = Type::$variant;
193            }
194
195            impl From<$type> for Value {
196                fn from(other: $type) -> Value {
197                    Value::$variant(other)
198                }
199            }
200
201            impl TryFrom<Value> for $type {
202                type Error = InvalidConversionError;
203
204                fn try_from(value: Value) -> Result<Self, Self::Error> {
205                    match value {
206                        Value::$variant(v) => Ok(v),
207                        _ => Err(InvalidConversionError {
208                            value,
209                            target_type: Type::$variant,
210                        }),
211                    }
212                }
213            }
214        )*
215    }
216}
217
218impl_as_type!(u8 => Byte, i16 => Short, i32 => Integer, f32 => Float, i8 => SignedByte);
219
220#[derive(Debug, Copy, Clone, PartialEq)]
221pub struct InvalidConversionError {
222    pub value: Value,
223    pub target_type: Type,
224}
225
226impl Display for InvalidConversionError {
227    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
228        write!(
229            f,
230            "Unable to convert {} ({:?}) to a {:?}",
231            self.value,
232            self.value.ty(),
233            self.target_type
234        )
235    }
236}
237
238#[cfg(feature = "std")]
239impl std::error::Error for InvalidConversionError {}