1use std::mem::transmute;
8use std::ops::{Deref, DerefMut};
9
10use mozjs::jsapi::{
11 GetSymbolCode, GetSymbolDescription, GetSymbolFor, GetWellKnownSymbol, NewSymbol, Symbol as JSSymbol,
12 SymbolCode as JSSymbolCode,
13};
14
15use crate::conversions::{FromValue as _, ToValue as _};
16use crate::{Context, Local};
17
18#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
23#[repr(u32)]
24pub enum WellKnownSymbolCode {
25 IsConcatSpreadable,
26 Iterator,
27 Match,
28 Replace,
29 Search,
30 Species,
31 HasInstance,
32 Split,
33 ToPrimitive,
34 ToStringTag,
35 Unscopables,
36 AsyncIterator,
37 MatchAll,
38}
39
40#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
43pub enum SymbolCode {
44 WellKnown(WellKnownSymbolCode),
45 PrivateNameSymbol,
46 InSymbolRegistry,
47 UniqueSymbol,
48}
49
50impl WellKnownSymbolCode {
51 pub const fn identifier(&self) -> &'static str {
54 use WellKnownSymbolCode as WKSC;
55 match self {
56 WKSC::IsConcatSpreadable => "isConcatSpreadable",
57 WKSC::Iterator => "iterator",
58 WKSC::Match => "match",
59 WKSC::Replace => "replace",
60 WKSC::Search => "search",
61 WKSC::Species => "species",
62 WKSC::HasInstance => "hasInstance",
63 WKSC::Split => "split",
64 WKSC::ToPrimitive => "toPrimitive",
65 WKSC::ToStringTag => "toStringTag",
66 WKSC::Unscopables => "unscopables",
67 WKSC::AsyncIterator => "asyncIterator",
68 WKSC::MatchAll => "matchAll",
69 }
70 }
71}
72
73impl SymbolCode {
74 pub fn well_known(&self) -> Option<WellKnownSymbolCode> {
76 if let SymbolCode::WellKnown(code) = self {
77 Some(*code)
78 } else {
79 None
80 }
81 }
82}
83
84impl From<JSSymbolCode> for SymbolCode {
85 fn from(code: JSSymbolCode) -> SymbolCode {
86 if (code as u32) < JSSymbolCode::Limit as u32 {
87 SymbolCode::WellKnown(unsafe { transmute::<JSSymbolCode, WellKnownSymbolCode>(code) })
88 } else {
89 use JSSymbolCode as JSSC;
90 match code {
91 JSSC::PrivateNameSymbol => SymbolCode::PrivateNameSymbol,
92 JSSC::InSymbolRegistry => SymbolCode::InSymbolRegistry,
93 JSSC::UniqueSymbol => SymbolCode::UniqueSymbol,
94 _ => unreachable!(),
95 }
96 }
97 }
98}
99
100impl From<WellKnownSymbolCode> for SymbolCode {
101 fn from(code: WellKnownSymbolCode) -> SymbolCode {
102 SymbolCode::WellKnown(code)
103 }
104}
105
106impl From<WellKnownSymbolCode> for JSSymbolCode {
107 fn from(code: WellKnownSymbolCode) -> Self {
108 unsafe { transmute(code) }
109 }
110}
111
112impl From<SymbolCode> for JSSymbolCode {
113 fn from(code: SymbolCode) -> JSSymbolCode {
114 use JSSymbolCode as JSSC;
115 match code {
116 SymbolCode::WellKnown(code) => code.into(),
117 SymbolCode::PrivateNameSymbol => JSSC::PrivateNameSymbol,
118 SymbolCode::InSymbolRegistry => JSSC::InSymbolRegistry,
119 SymbolCode::UniqueSymbol => JSSC::UniqueSymbol,
120 }
121 }
122}
123
124#[derive(Debug)]
127pub struct Symbol<'s> {
128 sym: Local<'s, *mut JSSymbol>,
129}
130
131impl Symbol<'_> {
132 pub fn new<'cx>(cx: &'cx Context, description: &str) -> Symbol<'cx> {
134 let description = description.as_value(cx);
135 let description = cx.root(description.handle().to_string());
136
137 let symbol = unsafe { NewSymbol(cx.as_ptr(), description.handle().into()) };
138 Symbol { sym: cx.root(symbol) }
139 }
140
141 pub fn for_key<'cx>(cx: &'cx Context, key: &str) -> Symbol<'cx> {
143 let key = key.as_value(cx);
144 let key = cx.root(key.handle().to_string());
145
146 let symbol = unsafe { GetSymbolFor(cx.as_ptr(), key.handle().into()) };
147 Symbol { sym: cx.root(symbol) }
148 }
149
150 pub fn well_known(cx: &Context, code: WellKnownSymbolCode) -> Symbol<'_> {
152 let symbol = unsafe { GetWellKnownSymbol(cx.as_ptr(), code.into()) };
153 Symbol { sym: cx.root(symbol) }
154 }
155
156 pub fn code(&self) -> SymbolCode {
158 unsafe { GetSymbolCode(self.sym.handle().into()).into() }
159 }
160
161 pub fn description(&self, cx: &Context) -> Option<String> {
164 let description = unsafe { GetSymbolDescription(self.sym.handle().into()) };
165 if !description.is_null() {
166 let description = description.as_value(cx);
167 String::from_value(cx, &description, true, ()).ok()
168 } else {
169 None
170 }
171 }
172}
173
174impl<'o> From<Local<'o, *mut JSSymbol>> for Symbol<'o> {
175 fn from(sym: Local<'o, *mut JSSymbol>) -> Symbol<'o> {
176 Symbol { sym }
177 }
178}
179
180impl<'s> Deref for Symbol<'s> {
181 type Target = Local<'s, *mut JSSymbol>;
182
183 fn deref(&self) -> &Self::Target {
184 &self.sym
185 }
186}
187
188impl DerefMut for Symbol<'_> {
189 fn deref_mut(&mut self) -> &mut Self::Target {
190 &mut self.sym
191 }
192}
193
194#[cfg(test)]
195mod tests {
196 use mozjs::jsapi::SymbolCode as JSSymbolCode;
197
198 use crate::symbol::{SymbolCode, WellKnownSymbolCode};
199
200 macro_rules! convert_codes {
201 ($(($js:expr, $native:expr)$(,)?)*) => {
202 $(
203 assert_eq!($js, JSSymbolCode::from($native));
204 assert_eq!($native, SymbolCode::from($js));
205 )*
206 }
207 }
208
209 #[test]
210 fn code_conversion() {
211 use {JSSymbolCode as JSSC, SymbolCode as SC, WellKnownSymbolCode as WKSC};
212
213 convert_codes! {
215 (JSSC::isConcatSpreadable, SC::WellKnown(WKSC::IsConcatSpreadable)),
216 (JSSC::iterator, SC::WellKnown(WKSC::Iterator)),
217 (JSSC::match_, SC::WellKnown(WKSC::Match)),
218 (JSSC::replace, SC::WellKnown(WKSC::Replace)),
219 (JSSC::search, SC::WellKnown(WKSC::Search)),
220 (JSSC::species, SC::WellKnown(WKSC::Species)),
221 (JSSC::hasInstance, SC::WellKnown(WKSC::HasInstance)),
222 (JSSC::toPrimitive, SC::WellKnown(WKSC::ToPrimitive)),
223 (JSSC::toStringTag, SC::WellKnown(WKSC::ToStringTag)),
224 (JSSC::unscopables, SC::WellKnown(WKSC::Unscopables)),
225 (JSSC::asyncIterator, SC::WellKnown(WKSC::AsyncIterator)),
226 (JSSC::matchAll, SC::WellKnown(WKSC::MatchAll)),
227 }
228
229 convert_codes! {
231 (JSSC::PrivateNameSymbol, SC::PrivateNameSymbol),
232 (JSSC::InSymbolRegistry, SC::InSymbolRegistry),
233 (JSSC::UniqueSymbol, SC::UniqueSymbol),
234 }
235 }
236}