ion/object/
key.rs

1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 */
6
7use std::hash::{Hash, Hasher};
8use std::mem::discriminant;
9use std::ops::{Deref, DerefMut};
10
11use mozjs::jsapi::{JS_IdToProtoKey, JS_ValueToId, JSProtoKey, PropertyKey as JSPropertyKey, ProtoKeyToId};
12use mozjs::jsid::{IntId, VoidId};
13
14use crate::conversions::ToPropertyKey as _;
15use crate::{Context, Local, Result, String, Symbol, Value};
16
17pub struct PropertyKey<'k> {
18	key: Local<'k, JSPropertyKey>,
19}
20
21impl<'k> PropertyKey<'k> {
22	/// Creates a [PropertyKey] from an integer.
23	pub fn with_int(cx: &'k Context, int: i32) -> PropertyKey<'k> {
24		PropertyKey::from(cx.root(IntId(int)))
25	}
26
27	/// Creates a [PropertyKey] from a string.
28	pub fn with_string(cx: &'k Context, string: &str) -> Option<PropertyKey<'k>> {
29		let string = String::copy_from_str(cx, string)?;
30		string.to_key(cx)
31	}
32
33	pub fn with_symbol(cx: &'k Context, symbol: &Symbol) -> PropertyKey<'k> {
34		symbol.to_key(cx).unwrap()
35	}
36
37	pub fn from_proto_key(cx: &'k Context, proto_key: JSProtoKey) -> PropertyKey<'k> {
38		let mut key = PropertyKey::from(cx.root(VoidId()));
39		unsafe { ProtoKeyToId(cx.as_ptr(), proto_key, key.handle_mut().into()) }
40		key
41	}
42
43	pub fn from_value(cx: &'k Context, value: &Value) -> Option<PropertyKey<'k>> {
44		let mut key = PropertyKey::from(cx.root(VoidId()));
45		(unsafe { JS_ValueToId(cx.as_ptr(), value.handle().into(), key.handle_mut().into()) }).then_some(key)
46	}
47
48	pub fn to_proto_key(&self, cx: &Context) -> Option<JSProtoKey> {
49		let proto_key = unsafe { JS_IdToProtoKey(cx.as_ptr(), self.handle().into()) };
50		(proto_key != JSProtoKey::JSProto_Null).then_some(proto_key)
51	}
52
53	pub fn to_owned_key<'cx>(&self, cx: &'cx Context) -> Result<OwnedKey<'cx>> {
54		if self.handle().is_int() {
55			Ok(OwnedKey::Int(self.handle().to_int()))
56		} else if self.handle().is_string() {
57			Ok(OwnedKey::String(
58				String::from(cx.root(self.handle().to_string())).to_owned(cx)?,
59			))
60		} else if self.handle().is_symbol() {
61			Ok(OwnedKey::Symbol(cx.root(self.handle().to_symbol()).into()))
62		} else {
63			Ok(OwnedKey::Void)
64		}
65	}
66
67	pub fn into_local(self) -> Local<'k, JSPropertyKey> {
68		self.key
69	}
70}
71
72impl<'k> From<Local<'k, JSPropertyKey>> for PropertyKey<'k> {
73	fn from(key: Local<'k, JSPropertyKey>) -> PropertyKey<'k> {
74		PropertyKey { key }
75	}
76}
77
78impl<'k> Deref for PropertyKey<'k> {
79	type Target = Local<'k, JSPropertyKey>;
80
81	fn deref(&self) -> &Self::Target {
82		&self.key
83	}
84}
85
86impl DerefMut for PropertyKey<'_> {
87	fn deref_mut(&mut self) -> &mut Self::Target {
88		&mut self.key
89	}
90}
91
92/// Represents the key on an object.
93#[derive(Debug)]
94pub enum OwnedKey<'k> {
95	Int(i32),
96	String(std::string::String),
97	Symbol(Symbol<'k>),
98	Void,
99}
100
101impl<'k> OwnedKey<'k> {
102	pub fn clone(&self, cx: &'k Context) -> OwnedKey<'k> {
103		match self {
104			OwnedKey::Int(i) => OwnedKey::Int(*i),
105			OwnedKey::String(str) => OwnedKey::String(str.clone()),
106			OwnedKey::Symbol(symbol) => OwnedKey::Symbol(cx.root(symbol.get()).into()),
107			OwnedKey::Void => OwnedKey::Void,
108		}
109	}
110}
111
112impl Hash for OwnedKey<'_> {
113	fn hash<H: Hasher>(&self, state: &mut H) {
114		discriminant(self).hash(state);
115		match self {
116			OwnedKey::Int(i) => i.hash(state),
117			OwnedKey::String(str) => str.hash(state),
118			OwnedKey::Symbol(symbol) => symbol.handle().hash(state),
119			OwnedKey::Void => (),
120		}
121	}
122}
123
124impl PartialEq for OwnedKey<'_> {
125	fn eq(&self, other: &OwnedKey<'_>) -> bool {
126		match (self, other) {
127			(OwnedKey::Int(i), OwnedKey::Int(i2)) => *i == *i2,
128			(OwnedKey::String(str), OwnedKey::String(str2)) => *str == *str2,
129			(OwnedKey::Symbol(symbol), OwnedKey::Symbol(symbol2)) => symbol.get() == symbol2.get(),
130			(OwnedKey::Void, OwnedKey::Void) => true,
131			_ => false,
132		}
133	}
134}
135
136impl Eq for OwnedKey<'_> {}