mini_v8/
value.rs

1use crate::*;
2use std::iter::FromIterator;
3use std::ops::{Deref, DerefMut};
4use std::{fmt, slice, vec};
5
6/// A JavaScript value.
7///
8/// `Value`s can either hold direct values (undefined, null, booleans, numbers, dates) or references
9/// (strings, arrays, functions, other objects). Cloning values (via Rust's `Clone`) of the direct
10/// types defers to Rust's `Copy`, while cloning values of the referential types results in a simple
11/// reference clone similar to JavaScript's own "by-reference" semantics.
12#[derive(Clone)]
13pub enum Value {
14    /// The JavaScript value `undefined`.
15    Undefined,
16    /// The JavaScript value `null`.
17    Null,
18    /// The JavaScript value `true` or `false`.
19    Boolean(bool),
20    /// A JavaScript floating point number.
21    Number(f64),
22    /// Elapsed milliseconds since Unix epoch.
23    Date(f64),
24    /// An immutable JavaScript string, managed by V8.
25    String(String),
26    /// Reference to a JavaScript arrray.
27    Array(Array),
28    /// Reference to a JavaScript function.
29    Function(Function),
30    /// Reference to a JavaScript object. If a value is a function or an array in JavaScript, it
31    /// will be converted to `Value::Array` or `Value::Function` instead of `Value::Object`.
32    Object(Object),
33}
34
35impl Value {
36    /// Returns `true` if this is a `Value::Undefined`, `false` otherwise.
37    pub fn is_undefined(&self) -> bool {
38        if let Value::Undefined = *self { true } else { false }
39    }
40
41    /// Returns `true` if this is a `Value::Null`, `false` otherwise.
42    pub fn is_null(&self) -> bool {
43        if let Value::Null = *self { true } else { false }
44    }
45
46    /// Returns `true` if this is a `Value::Boolean`, `false` otherwise.
47    pub fn is_boolean(&self) -> bool {
48        if let Value::Boolean(_) = *self { true } else { false }
49    }
50
51    /// Returns `true` if this is a `Value::Number`, `false` otherwise.
52    pub fn is_number(&self) -> bool {
53        if let Value::Number(_) = *self { true } else { false }
54    }
55
56    /// Returns `true` if this is a `Value::Date`, `false` otherwise.
57    pub fn is_date(&self) -> bool {
58        if let Value::Date(_) = *self { true } else { false }
59    }
60
61    /// Returns `true` if this is a `Value::String`, `false` otherwise.
62    pub fn is_string(&self) -> bool {
63        if let Value::String(_) = *self { true } else { false }
64    }
65
66    /// Returns `true` if this is a `Value::Array`, `false` otherwise.
67    pub fn is_array(&self) -> bool {
68        if let Value::Array(_) = *self { true } else { false }
69    }
70
71    /// Returns `true` if this is a `Value::Function`, `false` otherwise.
72    pub fn is_function(&self) -> bool {
73        if let Value::Function(_) = *self { true } else { false }
74    }
75
76    /// Returns `true` if this is a `Value::Object`, `false` otherwise.
77    pub fn is_object(&self) -> bool {
78        if let Value::Object(_) = *self { true } else { false }
79    }
80
81    /// Returns `Some(())` if this is a `Value::Undefined`, `None` otherwise.
82    pub fn as_undefined(&self) -> Option<()> {
83        if let Value::Undefined = *self { Some(()) } else { None }
84    }
85
86    /// Returns `Some(())` if this is a `Value::Null`, `None` otherwise.
87    pub fn as_null(&self) -> Option<()> {
88        if let Value::Undefined = *self { Some(()) } else { None }
89    }
90
91    /// Returns `Some` if this is a `Value::Boolean`, `None` otherwise.
92    pub fn as_boolean(&self) -> Option<bool> {
93        if let Value::Boolean(value) = *self { Some(value) } else { None }
94    }
95
96    /// Returns `Some` if this is a `Value::Number`, `None` otherwise.
97    pub fn as_number(&self) -> Option<f64> {
98        if let Value::Number(value) = *self { Some(value) } else { None }
99    }
100
101    /// Returns `Some` if this is a `Value::Date`, `None` otherwise.
102    pub fn as_date(&self) -> Option<f64> {
103        if let Value::Date(value) = *self { Some(value) } else { None }
104    }
105
106    /// Returns `Some` if this is a `Value::String`, `None` otherwise.
107    pub fn as_string(&self) -> Option<&String> {
108        if let Value::String(ref value) = *self { Some(value) } else { None }
109    }
110
111    /// Returns `Some` if this is a `Value::Array`, `None` otherwise.
112    pub fn as_array(&self) -> Option<&Array> {
113        if let Value::Array(ref value) = *self { Some(value) } else { None }
114    }
115
116    /// Returns `Some` if this is a `Value::Function`, `None` otherwise.
117    pub fn as_function(&self) -> Option<&Function> {
118        if let Value::Function(ref value) = *self { Some(value) } else { None }
119    }
120
121    /// Returns `Some` if this is a `Value::Object`, `None` otherwise.
122    pub fn as_object(&self) -> Option<&Object> {
123        if let Value::Object(ref value) = *self { Some(value) } else { None }
124    }
125
126    /// A wrapper around `FromValue::from_value`.
127    pub fn into<T: FromValue>(self, mv8: &MiniV8) -> Result< T> {
128        T::from_value(self, mv8)
129    }
130
131    /// Coerces a value to a boolean. Returns `true` if the value is "truthy", `false` otherwise.
132    pub fn coerce_boolean(&self, mv8: &MiniV8) -> bool {
133        match self {
134            &Value::Boolean(b) => b,
135            value => mv8.scope(|scope| value.to_v8_value(scope).boolean_value(scope)),
136        }
137    }
138
139    /// Coerces a value to a number. Nearly all JavaScript values are coercible to numbers, but this
140    /// may fail with a runtime error under extraordinary circumstances (e.g. if the ECMAScript
141    /// `ToNumber` implementation throws an error).
142    ///
143    /// This will return `std::f64::NAN` if the value has no numerical equivalent.
144    pub fn coerce_number(&self, mv8: &MiniV8) -> Result<f64> {
145        match self {
146            &Value::Number(n) => Ok(n),
147            value => mv8.try_catch(|scope| {
148                let maybe = value.to_v8_value(scope).to_number(scope);
149                mv8.exception(scope).map(|_| maybe.unwrap().value())
150            }),
151        }
152    }
153
154    /// Coerces a value to a string. Nearly all JavaScript values are coercible to strings, but this
155    /// may fail with a runtime error if `toString()` fails or under otherwise extraordinary
156    /// circumstances (e.g. if the ECMAScript `ToString` implementation throws an error).
157    pub fn coerce_string(&self, mv8: &MiniV8) -> Result<String> {
158        match self {
159            &Value::String(ref s) => Ok(s.clone()),
160            value => mv8.try_catch(|scope| {
161                let maybe = value.to_v8_value(scope).to_string(scope);
162                mv8.exception(scope).map(|_| String {
163                    mv8: mv8.clone(),
164                    handle: v8::Global::new(scope, maybe.unwrap()),
165                })
166            }),
167        }
168    }
169
170    pub(crate) fn type_name(&self) -> &'static str {
171        match *self {
172            Value::Undefined => "undefined",
173            Value::Null => "null",
174            Value::Boolean(_) => "boolean",
175            Value::Number(_) => "number",
176            Value::Date(_) => "date",
177            Value::Function(_) => "function",
178            Value::Array(_) => "array",
179            Value::Object(_) => "object",
180            Value::String(_) => "string",
181        }
182    }
183
184    pub(crate) fn from_v8_value(
185        mv8: &MiniV8,
186        scope: &mut v8::HandleScope,
187        value: v8::Local<v8::Value>,
188    ) -> Value {
189        if value.is_undefined() {
190            Value::Undefined
191        } else if value.is_null() {
192            Value::Null
193        } else if value.is_boolean() {
194            Value::Boolean(value.boolean_value(scope))
195        } else if value.is_int32() {
196            Value::Number(value.int32_value(scope).unwrap() as f64)
197        } else if value.is_number() {
198            Value::Number(value.number_value(scope).unwrap())
199        } else if value.is_date() {
200            let value: v8::Local<v8::Date> = value.try_into().unwrap();
201            Value::Date(value.value_of())
202        } else if value.is_string() {
203            let value: v8::Local<v8::String> = value.try_into().unwrap();
204            let handle = v8::Global::new(scope, value);
205            Value::String(String { mv8: mv8.clone(), handle })
206        } else if value.is_array() {
207            let value: v8::Local<v8::Array> = value.try_into().unwrap();
208            let handle = v8::Global::new(scope, value);
209            Value::Array(Array { mv8: mv8.clone(), handle })
210        } else if value.is_function() {
211            let value: v8::Local<v8::Function> = value.try_into().unwrap();
212            let handle = v8::Global::new(scope, value);
213            Value::Function(Function { mv8: mv8.clone(), handle })
214        } else if value.is_object() {
215            let value: v8::Local<v8::Object> = value.try_into().unwrap();
216            let handle = v8::Global::new(scope, value);
217            Value::Object(Object { mv8: mv8.clone(), handle })
218        } else {
219            Value::Undefined
220        }
221    }
222
223    pub(crate) fn to_v8_value<'s>(&self, scope: &mut v8::HandleScope<'s>)
224        -> v8::Local<'s, v8::Value>
225    {
226        match self {
227            Value::Undefined => v8::undefined(scope).into(),
228            Value::Null => v8::null(scope).into(),
229            Value::Boolean(v) => v8::Boolean::new(scope, *v).into(),
230            Value::Number(v) => v8::Number::new(scope, *v).into(),
231            Value::Date(v) => v8::Date::new(scope, *v).unwrap().into(),
232            Value::Function(v) => v8::Local::new(scope, v.handle.clone()).into(),
233            Value::Array(v) => v8::Local::new(scope, v.handle.clone()).into(),
234            Value::Object(v) => v8::Local::new(scope, v.handle.clone()).into(),
235            Value::String(v) => v8::Local::new(scope, v.handle.clone()).into(),
236        }
237    }
238}
239
240impl fmt::Debug for Value {
241    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
242        match self {
243            Value::Undefined => write!(f, "undefined"),
244            Value::Null => write!(f, "null"),
245            Value::Boolean(b) => write!(f, "{:?}", b),
246            Value::Number(n) => write!(f, "{}", n),
247            Value::Date(d) => write!(f, "date:{}", d),
248            Value::String(s) => write!(f, "{:?}", s),
249            Value::Array(a) => write!(f, "{:?}", a),
250            Value::Function(u) => write!(f, "{:?}", u),
251            Value::Object(o) => write!(f, "{:?}", o),
252        }
253    }
254}
255
256/// Trait for types convertible to `Value`.
257pub trait ToValue {
258    /// Performs the conversion.
259    fn to_value(self, mv8: &MiniV8) -> Result<Value>;
260}
261
262/// Trait for types convertible from `Value`.
263pub trait FromValue: Sized {
264    /// Performs the conversion.
265    fn from_value(value: Value, mv8: &MiniV8) -> Result<Self>;
266}
267
268/// A collection of multiple JavaScript values used for interacting with function arguments.
269#[derive(Clone)]
270pub struct Values(Vec<Value>);
271
272impl Values {
273    /// Creates an empty `Values`.
274    pub fn new() -> Values {
275        Values(Vec::new())
276    }
277
278    pub fn from_vec(vec: Vec<Value>) -> Values {
279        Values(vec)
280    }
281
282    pub fn into_vec(self) -> Vec<Value> {
283        self.0
284    }
285
286    pub fn get(&self, index: usize) -> Value {
287        self.0.get(index).map(Clone::clone).unwrap_or(Value::Undefined)
288    }
289
290    pub fn from<T: FromValue>(&self, mv8: &MiniV8, index: usize) -> Result<T> {
291        T::from_value(self.0.get(index).map(Clone::clone).unwrap_or(Value::Undefined), mv8)
292    }
293
294    pub fn into<T: FromValues>(self, mv8: &MiniV8) -> Result<T> {
295        T::from_values(self, mv8)
296    }
297
298    pub fn len(&self) -> usize {
299        self.0.len()
300    }
301
302    pub fn iter<'a>(&'a self) -> impl Iterator<Item = &'a Value> {
303        self.0.iter()
304    }
305}
306
307impl FromIterator<Value> for Values {
308    fn from_iter<I: IntoIterator<Item = Value>>(iter: I) -> Self {
309        Values::from_vec(Vec::from_iter(iter))
310    }
311}
312
313impl IntoIterator for Values {
314    type Item = Value;
315    type IntoIter = vec::IntoIter<Value>;
316
317    fn into_iter(self) -> Self::IntoIter {
318        self.0.into_iter()
319    }
320}
321
322impl<'a> IntoIterator for &'a Values {
323    type Item = &'a Value;
324    type IntoIter = slice::Iter<'a, Value>;
325
326    fn into_iter(self) -> Self::IntoIter {
327        (&self.0).into_iter()
328    }
329}
330
331/// Trait for types convertible to any number of JavaScript values.
332///
333/// This is a generalization of `ToValue`, allowing any number of resulting JavaScript values
334/// instead of just one. Any type that implements `ToValue` will automatically implement this trait.
335pub trait ToValues {
336    /// Performs the conversion.
337    fn to_values(self, mv8: &MiniV8) -> Result<Values>;
338}
339
340/// Trait for types that can be created from an arbitrary number of JavaScript values.
341///
342/// This is a generalization of `FromValue`, allowing an arbitrary number of JavaScript values to
343/// participate in the conversion. Any type that implements `FromValue` will automatically implement
344/// this trait.
345pub trait FromValues: Sized {
346    /// Performs the conversion.
347    ///
348    /// In case `values` contains more values than needed to perform the conversion, the excess
349    /// values should be ignored. Similarly, if not enough values are given, conversions should
350    /// assume that any missing values are undefined.
351    fn from_values(values: Values, mv8: &MiniV8) -> Result<Self>;
352}
353
354/// Wraps a variable number of `T`s.
355///
356/// Can be used to work with variadic functions more easily. Using this type as the last argument of
357/// a Rust callback will accept any number of arguments from JavaScript and convert them to the type
358/// `T` using [`FromValue`]. `Variadic<T>` can also be returned from a callback, returning a
359/// variable number of values to JavaScript.
360#[derive(Clone)]
361pub struct Variadic<T>(pub(crate) Vec<T>);
362
363impl<T> Variadic<T> {
364    /// Creates an empty `Variadic` wrapper containing no values.
365    pub fn new() -> Variadic<T> {
366        Variadic(Vec::new())
367    }
368
369    pub fn from_vec(vec: Vec<T>) -> Variadic<T> {
370        Variadic(vec)
371    }
372
373    pub fn into_vec(self) -> Vec<T> {
374        self.0
375    }
376}
377
378impl<T> FromIterator<T> for Variadic<T> {
379    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
380        Variadic(Vec::from_iter(iter))
381    }
382}
383
384impl<T> IntoIterator for Variadic<T> {
385    type Item = T;
386    type IntoIter = <Vec<T> as IntoIterator>::IntoIter;
387
388    fn into_iter(self) -> Self::IntoIter {
389        self.0.into_iter()
390    }
391}
392
393impl<T> Deref for Variadic<T> {
394    type Target = Vec<T>;
395
396    fn deref(&self) -> &Self::Target {
397        &self.0
398    }
399}
400
401impl<T> DerefMut for Variadic<T> {
402    fn deref_mut(&mut self) -> &mut Self::Target {
403        &mut self.0
404    }
405}