mini_v8/
object.rs

1use crate::*;
2use std::fmt;
3use std::marker::PhantomData;
4
5#[derive(Clone)]
6pub struct Object {
7    pub(crate) mv8: MiniV8,
8    pub(crate) handle: v8::Global<v8::Object>,
9}
10
11impl Object {
12    /// Get an object property value using the given key. Returns `Value::Undefined` if no property
13    /// with the key exists.
14    ///
15    /// Returns an error if `ToValue::to_value` fails for the key or if the key value could not be
16    /// cast to a property key string.
17    pub fn get<K: ToValue, V: FromValue>(&self, key: K) -> Result<V> {
18        let key = key.to_value(&self.mv8)?;
19        self.mv8.try_catch(|scope| {
20            let object = v8::Local::new(scope, self.handle.clone());
21            let key = key.to_v8_value(scope);
22            let result = object.get(scope, key);
23            self.mv8.exception(scope)?;
24            Ok(Value::from_v8_value(&self.mv8, scope, result.unwrap()))
25        }).and_then(|v| v.into(&self.mv8))
26    }
27
28    /// Sets an object property using the given key and value.
29    ///
30    /// Returns an error if `ToValue::to_value` fails for either the key or the value or if the key
31    /// value could not be cast to a property key string.
32    pub fn set<K: ToValue, V: ToValue>(&self, key: K, value: V) -> Result<()> {
33        let key = key.to_value(&self.mv8)?;
34        let value = value.to_value(&self.mv8)?;
35        self.mv8.try_catch(|scope| {
36            let object = v8::Local::new(scope, self.handle.clone());
37            let key = key.to_v8_value(scope);
38            let value = value.to_v8_value(scope);
39            object.set(scope, key, value);
40            self.mv8.exception(scope)
41        })
42    }
43
44    /// Removes the property associated with the given key from the object. This function does
45    /// nothing if the property does not exist.
46    ///
47    /// Returns an error if `ToValue::to_value` fails for the key or if the key value could not be
48    /// cast to a property key string.
49    pub fn remove<K: ToValue>(&self, key: K) -> Result<()> {
50        let key = key.to_value(&self.mv8)?;
51        self.mv8.try_catch(|scope| {
52            let object = v8::Local::new(scope, self.handle.clone());
53            let key = key.to_v8_value(scope);
54            object.delete(scope, key);
55            self.mv8.exception(scope)
56        })
57    }
58
59    /// Returns `true` if the given key is a property of the object, `false` otherwise.
60    ///
61    /// Returns an error if `ToValue::to_value` fails for the key or if the key value could not be
62    /// cast to a property key string.
63    pub fn has<K: ToValue>(&self, key: K) -> Result<bool> {
64        let key = key.to_value(&self.mv8)?;
65        self.mv8.try_catch(|scope| {
66            let object = v8::Local::new(scope, self.handle.clone());
67            let key = key.to_v8_value(scope);
68            let has = object.has(scope, key);
69            self.mv8.exception(scope)?;
70            Ok(has.unwrap())
71        })
72    }
73
74    /// Calls the function at the key with the given arguments, with `this` set to the object.
75    /// Returns an error if the value at the key is not a function.
76    pub fn call_prop<K, A, R>(&self, key: K, args: A) -> Result<R>
77    where
78        K: ToValue,
79        A: ToValues,
80        R: FromValue,
81    {
82        let func: Function = self.get(key)?;
83        func.call_method(self.clone(), args)
84    }
85
86    /// Returns an array containing all of this object's enumerable property keys. If
87    /// `include_inherited` is `false`, then only the object's own enumerable properties will be
88    /// collected (similar to `Object.getOwnPropertyNames` in Javascript). If `include_inherited` is
89    /// `true`, then the object's own properties and the enumerable properties from its prototype
90    /// chain will be collected.
91    pub fn keys(&self, include_inherited: bool) -> Result<Array> {
92        self.mv8.try_catch(|scope| {
93            let object = v8::Local::new(scope, self.handle.clone());
94            let keys = if include_inherited {
95                object.get_property_names(scope, Default::default())
96            } else {
97                object.get_own_property_names(scope, Default::default())
98            };
99            self.mv8.exception(scope)?;
100            Ok(Array {
101                mv8: self.mv8.clone(),
102                handle: v8::Global::new(scope, keys.unwrap()),
103            })
104        })
105    }
106
107    /// Converts the object into an iterator over the object's keys and values, acting like a
108    /// `for-in` loop.
109    ///
110    /// For information on the `include_inherited` argument, see `Object::keys`.
111    pub fn properties<K, V>(self, include_inherited: bool) -> Result<Properties<K, V>>
112    where
113        K: FromValue,
114        V: FromValue,
115    {
116        let keys = self.keys(include_inherited)?;
117        Ok(Properties { object: self, keys, index: 0, _phantom: PhantomData })
118    }
119}
120
121impl fmt::Debug for Object {
122    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
123        let keys = match self.keys(false) {
124            Ok(keys) => keys,
125            Err(_) => return write!(f, "<object with keys exception>"),
126        };
127
128        let len = keys.len();
129        if len == 0 {
130            return write!(f, "{{}}");
131        }
132
133        write!(f, "{{ ")?;
134        for i in 0..len {
135            if let Ok(k) = keys.get::<Value>(i).and_then(|k| k.coerce_string(&self.mv8)) {
136                write!(f, "{:?}: ", k)?;
137                match self.get::<_, Value>(k) {
138                    Ok(v) => write!(f, "{:?}", v)?,
139                    Err(_) => write!(f, "?")?,
140                };
141            } else {
142                write!(f, "?")?;
143            }
144            if i + 1 < len {
145                write!(f, ", ")?;
146            }
147        }
148        write!(f, " }}")
149    }
150}
151
152/// An iterator over an object's keys and values, acting like a `for-in` loop.
153pub struct Properties<K, V> {
154    object: Object,
155    keys: Array,
156    index: u32,
157    _phantom: PhantomData<(K, V)>,
158}
159
160impl<K, V> Iterator for Properties<K, V>
161where
162    K: FromValue,
163    V: FromValue,
164{
165    type Item = Result<(K, V)>;
166
167    /// This will return `Some(Err(...))` if the next property's key or value failed to be converted
168    /// into `K` or `V` respectively (through `ToValue`).
169    fn next(&mut self) -> Option<Self::Item> {
170        if self.index >= self.keys.len() {
171            return None;
172        }
173
174        let key = self.keys.get::<Value>(self.index);
175        self.index += 1;
176
177        let key = match key {
178            Ok(v) => v,
179            Err(e) => return Some(Err(e)),
180        };
181
182        let value = match self.object.get::<_, V>(key.clone()) {
183            Ok(v) => v,
184            Err(e) => return Some(Err(e)),
185        };
186
187        let key = match key.into(&self.object.mv8) {
188            Ok(v) => v,
189            Err(e) => return Some(Err(e)),
190        };
191
192        Some(Ok((key, value)))
193    }
194}