1use 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 pub fn with_int(cx: &'k Context, int: i32) -> PropertyKey<'k> {
24 PropertyKey::from(cx.root(IntId(int)))
25 }
26
27 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#[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<'_> {}