rquickjs_macro/lib.rs
1#![allow(clippy::uninlined_format_args)]
2#![allow(clippy::needless_doctest_main)]
3#![allow(clippy::doc_lazy_continuation)]
4
5use attrs::OptionList;
6use class::ClassOption;
7use function::FunctionOption;
8use methods::ImplOption;
9use module::ModuleOption;
10use proc_macro::TokenStream as TokenStream1;
11use syn::{parse_macro_input, spanned::Spanned, DeriveInput, Error, Item};
12
13#[cfg(test)]
14macro_rules! assert_eq_tokens {
15 ($actual:expr, $expected:expr) => {
16 let actual = $actual.to_string();
17 let expected = $expected.to_string();
18 difference::assert_diff!(&actual, &expected, " ", 0);
19 };
20}
21
22mod attrs;
23mod class;
24mod common;
25mod convert;
26mod embed;
27mod exotic;
28mod fields;
29mod function;
30mod js_lifetime;
31mod methods;
32mod module;
33mod trace;
34
35/// An attribute for implementing [`JsClass`](rquickjs_core::class::JsClass`) for a Rust type.
36///
37/// # Attribute options
38///
39/// The attribute has a number of options for configuring the generated trait implementation. These
40/// attributes can be passed to the `class` attribute as an argument: `#[class(rename =
41/// "AnotherName")]` or with a separate `qjs` attribute on the struct item: `#[qjs(rename =
42/// "AnotherName")]`. A option which is a Flag can be set just by adding the attribute:
43/// `#[qjs(flag)]` or by setting it to specific boolean value: `#[qjs(flag = true)]`.
44///
45/// | **Option** | **Value** | **Description** |
46/// |--------------|-----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
47/// | `crate` | String | Changes the name from which the attribute tries to use rquickjs types. Use when the name behind which the rquickjs crate is declared is not properly resolved by the macro. |
48/// | `rename` | String | Changes the name of the implemented class on the JavaScript side. |
49/// | `rename_all` | Casing | Converts the case of all the fields of this struct which have implement accessors. Can be one of `lowercase`, `UPPERCASE`, `camelCase`, `PascalCase`,`snake_case`, or `SCREAMING_SNAKE` |
50/// | `frozen` | Flag | Changes the class implementation to only allow borrowing immutably. Trying to borrow mutably will result in an error. |
51/// | `exotic` | Flag | Changes the class implementation to support exotic methods. Must be used in combination with the macro [`macro@exotic`]. |
52///
53/// # Field options
54///
55/// The fields of a struct (doesn't work on enums) can also tagged with an attribute to, for
56/// example make the fields accessible from JavaScript. These attributes are all in the form of
57/// `#[qjs(option = value)]`.
58///
59/// | **Option** | **Value** | **Description** |
60/// |----------------|-----------|-----------------------------------------------------------------------------------------|
61/// | `get` | Flag | Creates a getter for this field, allowing read access to the field from JavaScript. |
62/// | `set` | Flag | Creates a setter for this field, allowing write access to the field from JavaSccript. |
63/// | `enumerable` | Flag | Makes the field, if it has a getter or setter, enumerable in JavaScript. |
64/// | `configurable` | Flag | Makes the field, if it has a getter or setter, configurable in JavaScript. |
65/// | `skip_trace` | Flag | Skips the field deriving the `Trace` trait. |
66/// | `rename` | String | Changes the name of the field getter and/or setter to the specified name in JavaScript. |
67///
68///
69/// # Example
70/// ```
71/// use rquickjs::{class::Trace, CatchResultExt, Class, Context, Object, Runtime, JsLifetime};
72///
73/// /// Implement JsClass for TestClass.
74/// /// This allows passing any instance of TestClass straight to JavaScript.
75/// /// It is command to also add #[derive(Trace)] as all types which implement JsClass need to
76/// /// also implement trace.
77/// #[derive(Trace, JsLifetime)]
78/// #[rquickjs::class(rename_all = "camelCase")]
79/// pub struct TestClass<'js> {
80/// /// These attribute make the accessible from JavaScript with getters and setters.
81/// /// As we used `rename_all = "camelCase"` in the attribute it will be called `innerObject`
82/// /// on the JavaScript side.
83/// #[qjs(get, set)]
84/// inner_object: Object<'js>,
85///
86/// /// This works for any value which implements `IntoJs` and `FromJs` and is clonable.
87/// #[qjs(get, set)]
88/// some_value: u32,
89/// /// Make a field enumerable.
90/// #[qjs(get, set, enumerable)]
91/// another_value: u32,
92/// }
93///
94/// pub fn main() {
95/// let rt = Runtime::new().unwrap();
96/// let ctx = Context::full(&rt).unwrap();
97///
98/// ctx.with(|ctx| {
99/// /// Create an insance of a JsClass
100/// let cls = Class::instance(
101/// ctx.clone(),
102/// TestClass {
103/// inner_object: Object::new(ctx.clone()).unwrap(),
104/// some_value: 1,
105/// another_value: 2,
106/// },
107/// )
108/// .unwrap();
109/// /// Pass it to JavaScript
110/// ctx.globals().set("t", cls.clone()).unwrap();
111/// ctx.eval::<(), _>(
112/// r#"
113/// // use the actual value.
114/// if(t.someValue !== 1){
115/// throw new Error(1)
116/// }"#
117/// ).unwrap();
118/// })
119/// }
120/// ```
121///
122/// # Incompatibility with the plain-data derives
123///
124/// This attribute cannot be combined with [`macro@FromJs`] or
125/// [`macro@IntoJs`] on the same type. `#[class]` already generates a
126/// `FromJs`/`IntoJs` pair that round-trips through a
127/// [`Class<Self>`](rquickjs_core::class::Class) instance, whereas the
128/// derives round-trip through a plain JS object. If both are present the
129/// macro emits a targeted compile error pointing at the offending derive.
130#[proc_macro_attribute]
131pub fn class(attr: TokenStream1, item: TokenStream1) -> TokenStream1 {
132 let options = parse_macro_input!(attr as OptionList<ClassOption>);
133 let item = parse_macro_input!(item as Item);
134 match class::expand(options, item) {
135 Ok(x) => x.into(),
136 Err(e) => e.into_compile_error().into(),
137 }
138}
139
140/// A attribute for implementing `IntoJsFunc` for a certain function.
141///
142/// Using this attribute allows a wider range of functions to be used as callbacks from JavaScript
143/// then when you use closures or the functions for which the proper traits are already
144/// implemented..
145///
146#[proc_macro_attribute]
147pub fn function(attr: TokenStream1, item: TokenStream1) -> TokenStream1 {
148 let options = parse_macro_input!(attr as OptionList<FunctionOption>);
149 let item = parse_macro_input!(item as Item);
150 match item {
151 Item::Fn(func) => match function::expand(options, func) {
152 Ok(x) => x.into(),
153 Err(e) => e.into_compile_error().into(),
154 },
155 item => Error::new(
156 item.span(),
157 "#[function] macro can only be used on functions",
158 )
159 .into_compile_error()
160 .into(),
161 }
162}
163
164/// A attribute for implementing methods for a class.
165///
166/// This attribute can be added to a impl block which implements methods for a type which uses the
167/// [`macro@class`] attribute to derive [`JsClass`](rquickjs_core::class::JsClass).
168///
169/// # Limitations
170/// Due to limitations in the Rust type system this attribute can be used on only one impl block
171/// per type.
172///
173/// # Attribute options
174///
175/// The attribute has a number of options for configuring the generated trait implementation. These
176/// attributes can be passed to the `methods` attribute as an argument: `#[methods(rename =
177/// "AnotherName")]` or with a separate `qjs` attribute on the impl item: `#[qjs(rename =
178/// "AnotherName")]`. A option which is a Flag can be set just by adding the attribute:
179/// `#[qjs(flag)]` or by setting it to specific boolean value: `#[qjs(flag = true)]`.
180///
181/// | **Option** | **Value** | **Description** |
182/// |--------------|-----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
183/// | `crate` | String | Changes the name from which the attribute tries to use rquickjs types. Use when the name behind which the rquickjs crate is declared is not properly resolved by the macro. |
184/// | `rename` | String | Changes the name of the implemented class on the JavaScript side. |
185/// | `rename_all` | Casing | Converts the case of all the fields of this struct which have implement accessors. Can be one of `lowercase`, `UPPERCASE`, `camelCase`, `PascalCase`,`snake_case`, or `SCREAMING_SNAKE` |
186///
187///
188/// # Item options
189///
190/// Each item of the impl block can also tagged with an attribute to change the resulting derived method definition.
191/// These attributes are all in the form of `#[qjs(option = value)]`.
192///
193/// | **Option** | **Value** | **Description** |
194/// |----------------|-------------------------------------------------------------------|-------------------------------------------------------------------------------------------------|
195/// | `get` | Flag | Makes this method a getter for a field of the same name. |
196/// | `set` | Flag | Makes this method a setter for a field of the same name. |
197/// | `prop` | Flag | Declares a JS **data property** on the prototype. The function takes no receiver, is evaluated once at class registration time, and its return value becomes the property's value. Useful for Web IDL hooks like `Symbol.toStringTag` where the spec mandates a data (not accessor) descriptor. |
198/// | `enumerable` | Flag | Makes the method, if it is a getter, setter or `prop`, enumerable in JavaScript. |
199/// | `configurable` | Flag | Makes the method, if it is a getter, setter or `prop`, configurable in JavaScript (i.e. the descriptor can later be redefined or deleted). |
200/// | `writable` | Flag | Only valid together with `prop`: makes the data property writable from JavaScript. Defaults to non-writable, matching Web IDL defaults for things like `@@toStringTag`. |
201/// | `rename` | String or [`PredefinedAtom`](rquickjs_core::atom::PredefinedAtom) | Changes the name of the field getter and/or setter to the specified name in JavaScript. |
202/// | `static` | Flag | Makes the method a static method i.e. defined on the type constructor instead of the prototype. |
203/// | `constructor` | Flag | Marks this method a the constructor for this type. |
204/// | `skip` | Flag | Skips defining this method on the JavaScript class. |
205///
206/// # Example
207/// ```
208/// use rquickjs::{
209/// atom::PredefinedAtom, class::Trace, prelude::Func, CatchResultExt, Class, Context, Ctx,
210/// Object, Result, Runtime, JsLifetime
211/// };
212///
213/// #[derive(Trace, JsLifetime)]
214/// #[rquickjs::class]
215/// pub struct TestClass {
216/// value: u32,
217/// another_value: u32,
218/// }
219///
220/// #[rquickjs::methods]
221/// impl TestClass {
222/// /// Marks a method as a constructor.
223/// /// This method will be used when a new TestClass object is created from JavaScript.
224/// #[qjs(constructor)]
225/// pub fn new(value: u32) -> Self {
226/// TestClass {
227/// value,
228/// another_value: value,
229/// }
230/// }
231///
232/// /// Mark a function as a getter.
233/// /// The value of this function can be accessed as a field.
234/// /// This function is also renamed to value
235/// #[qjs(get, rename = "value")]
236/// pub fn get_value(&self) -> u32 {
237/// self.value
238/// }
239///
240/// /// Mark a function as a setter.
241/// /// The value of this function can be set as a field.
242/// /// This function is also renamed to value
243/// #[qjs(set, rename = "value")]
244/// pub fn set_value(&mut self, v: u32) {
245/// self.value = v
246/// }
247///
248/// /// Mark a function as a enumerable gettter.
249/// #[qjs(get, rename = "anotherValue", enumerable)]
250/// pub fn get_another_value(&self) -> u32 {
251/// self.another_value
252/// }
253///
254/// #[qjs(set, rename = "anotherValue", enumerable)]
255/// pub fn set_another_value(&mut self, v: u32) {
256/// self.another_value = v
257/// }
258///
259/// /// Marks a function as static. It will be defined on the constructor object instead of the
260/// /// Class prototype.
261/// #[qjs(static)]
262/// pub fn compare(a: &Self, b: &Self) -> bool {
263/// a.value == b.value && a.another_value == b.another_value
264/// }
265///
266/// /// All functions declared in this impl block will be defined on the prototype of the
267/// /// class. This attributes allows you to skip certain functions.
268/// #[qjs(skip)]
269/// pub fn inner_function(&self) {}
270///
271/// /// Declares a **data property** on the prototype. Unlike `get`, `prop` takes no
272/// /// receiver and is evaluated once at class registration time; the return value
273/// /// becomes the property's value. This is the correct shape for Web IDL hooks
274/// /// like `Symbol.toStringTag`, which the spec mandates be a non-writable,
275/// /// configurable *data* descriptor (so e.g.
276/// /// `Object.prototype.toString.call(Object.create(TestClass.prototype))`
277/// /// works without a real class instance).
278/// #[qjs(prop, rename = PredefinedAtom::SymbolToStringTag, configurable)]
279/// pub fn to_string_tag() -> &'static str {
280/// "TestClass"
281/// }
282///
283/// /// A named, writable data property with all three descriptor flags set.
284/// #[qjs(prop, rename = "kind", configurable, enumerable, writable)]
285/// pub fn kind() -> &'static str {
286/// "test"
287/// }
288///
289/// /// Functions can also be renamed to specific symbols. This allows you to make an Rust type
290/// /// act like an iteratable value for example.
291/// #[qjs(rename = PredefinedAtom::SymbolIterator)]
292/// pub fn iterate<'js>(&self, ctx: Ctx<'js>) -> Result<Object<'js>> {
293/// let res = Object::new(ctx)?;
294///
295/// res.set(
296/// PredefinedAtom::Next,
297/// Func::from(|ctx: Ctx<'js>| -> Result<Object<'js>> {
298/// let res = Object::new(ctx)?;
299/// res.set(PredefinedAtom::Done, true)?;
300/// Ok(res)
301/// }),
302/// )?;
303/// Ok(res)
304/// }
305/// }
306///
307/// pub fn main() {
308/// let rt = Runtime::new().unwrap();
309/// let ctx = Context::full(&rt).unwrap();
310///
311/// ctx.with(|ctx| {
312/// /// Define the class constructor on the globals object.
313/// Class::<TestClass>::define(&ctx.globals()).unwrap();
314/// ctx.eval::<(), _>(
315/// r#"
316/// let nv = new TestClass(5);
317/// if(nv.value !== 5){
318/// throw new Error('invalid value')
319/// }
320/// "#,
321/// ).catch(&ctx).unwrap();
322/// });
323/// }
324/// ```
325#[proc_macro_attribute]
326pub fn methods(attr: TokenStream1, item: TokenStream1) -> TokenStream1 {
327 let options = parse_macro_input!(attr as OptionList<ImplOption>);
328 let item = parse_macro_input!(item as Item);
329 match item {
330 Item::Impl(item) => match methods::expand(options, item) {
331 Ok(x) => x.into(),
332 Err(e) => e.into_compile_error().into(),
333 },
334 item => Error::new(
335 item.span(),
336 "#[methods] macro can only be used on impl blocks",
337 )
338 .into_compile_error()
339 .into(),
340 }
341}
342
343/// An attribute for implementing exotic methods for a class.
344///
345/// This attribute can be added to a impl block which implements methods for a type which uses the
346/// [`macro@class`] attribute to derive [`JsClass`](rquickjs_core::class::JsClass) with the `exotic`
347///
348/// # Limitations
349/// Due to limitations in the Rust type system this attribute can be used on only one impl block
350/// per type.
351///
352/// # Item options
353///
354/// Each item of the impl block must be tagged with an attribute to specify the exotic method it implements.
355/// These attributes are all in the form of `#[qjs(option)]`.
356///
357/// | **Option** | **Value** | **Description** |
358/// |------------|-----------|-----------------|
359/// | `get` | Flag | Makes this method the [[Get]] exotic method |
360/// | `set` | Flag | Makes this method the [[Set]] exotic method |
361/// | `delete` | Flag | Makes this method the [[Delete]] exotic method |
362/// | `has` | Flag | Makes this method the [[HasProperty]] exotic method |
363///
364/// # Example
365/// ```
366/// use rquickjs::{class::Trace, JsLifetime, Context, Runtime, Atom, Class};
367///
368/// #[derive(Trace, JsLifetime)]
369/// #[rquickjs::class(exotic)]
370/// pub struct TestClass {
371/// value: u32,
372/// }
373///
374/// #[rquickjs::exotic]
375/// impl TestClass {
376/// #[qjs(get)]
377/// pub fn value(&self, atom: Atom<'_>) -> Option<u32> {
378/// if atom.to_string().unwrap() == "value" {
379/// Some(self.value)
380/// } else {
381/// None
382/// }
383/// }
384/// }
385///
386/// fn main() {
387/// let rt = Runtime::new().unwrap();
388/// let ctx = Context::full(&rt).unwrap();
389///
390/// ctx.with(|ctx| {
391/// let cls = Class::instance(ctx.clone(), TestClass { value: 42 }).unwrap();
392/// ctx.globals().set("my_class", cls.clone()).unwrap();
393/// let value = ctx.eval::<u32, _>(r#"my_class.value"#).unwrap();
394/// println!("value: {}", value);
395/// assert_eq!(value, 42);
396/// })
397/// }
398/// ```
399#[proc_macro_attribute]
400pub fn exotic(_attr: TokenStream1, item: TokenStream1) -> TokenStream1 {
401 let item = parse_macro_input!(item as Item);
402 match item {
403 Item::Impl(item) => match exotic::expand(item) {
404 Ok(x) => x.into(),
405 Err(e) => e.into_compile_error().into(),
406 },
407 item => Error::new(
408 item.span(),
409 "#[exotic] macro can only be used on impl blocks",
410 )
411 .into_compile_error()
412 .into(),
413 }
414}
415
416/// An attribute which generates code for exporting a module to Rust.
417///
418/// Any supported item inside the module which is marked as `pub` will be exported as a JavaScript value.
419/// Different items result in different JavaScript values.
420/// The supported items are:
421///
422/// - `struct` and `enum` items. These will be exported as JavaScript
423/// classes with their constructor exported as a function from the module.
424/// - `fn` items, these will be exported as JavaScript functions.
425/// - `use` items, the types which are reexported with `pub` will be handled just like `struct` and
426/// `enum` items defined inside the module. The name of the class can be adjusted by renaming the
427/// reexport with `as`.
428/// - `const` and `static` items, these items will be exported as values with the same name.
429///
430/// # Attribute options
431///
432/// The attribute has a number of options for configuring the generated trait implementation. These
433/// attributes can be passed to the `module` attribute as an argument: `#[module(rename =
434/// "AnotherName")]` or with a separate `qjs` attribute on the impl item: `#[qjs(rename =
435/// "AnotherName")]`. A option which is a Flag can be set just by adding the attribute:
436/// `#[qjs(flag)]` or by setting it to specific boolean value: `#[qjs(flag = true)]`.
437///
438/// | **Option** | **Value** | **Description** |
439/// |----------------|-----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
440/// | `crate` | String | Changes the name from which the attribute tries to use rquickjs types. Use when the name behind which the rquickjs crate is declared is not properly resolved by the macro. |
441/// | `rename` | String | Changes the name of the implemented module on the JavaScript side. |
442/// | `rename_vars` | Casing | Alters the name of all items exported as JavaScript values by changing the case. Can be one of `lowercase`, `UPPERCASE`, `camelCase`, `PascalCase`,`snake_case`, or `SCREAMING_SNAKE` |
443/// | `rename_types` | Casing | Alters the name of all items exported as JavaScript classes by changing the case. Can be one of `lowercase`, `UPPERCASE`, `camelCase`, `PascalCase`,`snake_case`, or `SCREAMING_SNAKE` |
444/// | `prefix` | String | The module will be implemented for a new type with roughly the same name as the Rust module with a prefix added. This changes the prefix which will be added. Defaults to `js_` |
445///
446/// # Item options
447///
448/// The attribute also has a number of options for changing the resulting generated module
449/// implementation for specific items.
450/// These attributes are all in the form of `#[qjs(option = value)]`.
451///
452/// | **Option** | **Value** | **Item Type** | **Description** |
453/// |------------|-----------|----------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
454/// | `skip` | Flag | All | Skips exporting this item from the JavaScript module. |
455/// | `rename` | String | All except use | Change the name from which this value is exported. |
456/// | `declare` | Flag | Functions Only | Marks this function as the declaration function. This function will be called when the module is declared allowing for exporting items which otherwise are difficult to export using the attribute. |
457/// | `evaluate` | Flag | Functions Only | Marks this function as the evaluation function. This function will be called when the module is being evaluated allowing for exporting items which otherwise are difficult to export using the attribute. |
458///
459/// # Example
460///
461/// ```
462/// use rquickjs::{CatchResultExt, Context, Module, Runtime};
463///
464/// /// A class which will be exported from the module.
465/// #[derive(rquickjs::class::Trace, rquickjs::JsLifetime)]
466/// #[rquickjs::class]
467/// pub struct Test {
468/// foo: u32,
469/// }
470///
471/// #[rquickjs::methods]
472/// impl Test {
473/// #[qjs(constructor)]
474/// pub fn new() -> Test {
475/// Test { foo: 3 }
476/// }
477/// }
478///
479/// impl Default for Test {
480/// fn default() -> Self {
481/// Self::new()
482/// }
483/// }
484///
485/// #[rquickjs::module(rename_vars = "camelCase")]
486/// mod test_mod {
487/// /// Imports and other declarations which aren't `pub` won't be exported.
488/// use rquickjs::Ctx;
489///
490/// /// You can even use `use` to export types from outside.
491/// ///
492/// /// Note that this tries to export the type, not the value,
493/// /// So this won't work for functions.
494/// ///
495/// /// By using `as` you can change under which name the constructor is exported.
496/// /// The below type will exported as `RenamedTest`.
497/// pub use super::Test as RenamedTest;
498///
499/// /// A class which will be exported from the module under the name `FooBar`.
500/// #[derive(rquickjs::class::Trace, rquickjs::JsLifetime)]
501/// #[rquickjs::class(rename = "FooBar")]
502/// pub struct Test2 {
503/// bar: u32,
504/// }
505///
506/// /// Implement methods for the class like normal.
507/// #[rquickjs::methods]
508/// impl Test2 {
509/// /// A constructor is required for exporting types.
510/// #[qjs(constructor)]
511/// pub fn new() -> Test2 {
512/// Test2 { bar: 3 }
513/// }
514/// }
515///
516/// impl Default for Test2 {
517/// fn default() -> Self {
518/// Self::new()
519/// }
520/// }
521///
522/// /// Two variables exported as `aConstValue` and `aStaticValue` because of the `rename_all` attr.
523/// pub const A_CONST_VALUE: f32 = 2.0;
524/// pub static A_STATIC_VALUE: f32 = 2.0;
525///
526/// /// If your module doesn't quite fit with how this macro exports you can manually export from
527/// /// the declare and evaluate functions.
528/// #[qjs(declare)]
529/// pub fn declare(declare: &rquickjs::module::Declarations) -> rquickjs::Result<()> {
530/// declare.declare("aManuallyExportedValue")?;
531/// Ok(())
532/// }
533///
534/// #[qjs(evaluate)]
535/// pub fn evaluate<'js>(
536/// _ctx: &Ctx<'js>,
537/// exports: &rquickjs::module::Exports<'js>,
538/// ) -> rquickjs::Result<()> {
539/// exports.export("aManuallyExportedValue", "Some Value")?;
540/// Ok(())
541/// }
542///
543/// /// You can also export functions.
544/// #[rquickjs::function]
545/// pub fn foo() -> u32 {
546/// 1 + 1
547/// }
548///
549/// /// You can make items public but not export them to JavaScript by adding the skip attribute.
550/// #[qjs(skip)]
551/// pub fn ignore_function() -> u32 {
552/// 2 + 2
553/// }
554/// }
555///
556/// fn main() {
557/// assert_eq!(test_mod::ignore_function(), 4);
558/// let rt = Runtime::new().unwrap();
559/// let ctx = Context::full(&rt).unwrap();
560///
561/// ctx.with(|ctx| {
562/// // These modules are declared like normal with the declare_def function.
563/// // The name of the module is js_ + the name of the module. This prefix can be changed
564/// // by writing for example `#[rquickjs::module(prefix = "prefix_")]`.
565/// Module::declare_def::<js_test_mod, _>(ctx.clone(), "test").unwrap();
566/// let _ = Module::evaluate(
567/// ctx.clone(),
568/// "test2",
569/// r"
570/// import { RenamedTest, foo,aManuallyExportedValue, aConstValue, aStaticValue, FooBar } from 'test';
571/// if (foo() !== 2){
572/// throw new Error(1);
573/// }
574/// "
575/// )
576/// .catch(&ctx)
577/// .unwrap();
578/// })
579/// }
580/// ```
581#[proc_macro_attribute]
582pub fn module(attr: TokenStream1, item: TokenStream1) -> TokenStream1 {
583 let options = parse_macro_input!(attr as OptionList<ModuleOption>);
584 let item = parse_macro_input!(item as Item);
585 match item {
586 Item::Mod(item) => match module::expand(options, item) {
587 Ok(x) => x.into(),
588 Err(e) => e.into_compile_error().into(),
589 },
590 item => Error::new(item.span(), "#[module] macro can only be used on modules")
591 .into_compile_error()
592 .into(),
593 }
594}
595
596/// A macro for auto deriving the trace trait.
597#[proc_macro_derive(Trace, attributes(qjs))]
598pub fn trace(stream: TokenStream1) -> TokenStream1 {
599 let derive_input = parse_macro_input!(stream as DeriveInput);
600 match trace::expand(derive_input) {
601 Ok(x) => x.into(),
602 Err(e) => e.into_compile_error().into(),
603 }
604}
605
606/// A macro for deriving [`FromJs`](rquickjs_core::FromJs) for plain-data structs.
607///
608/// Generated impls treat the Rust value as a plain JavaScript object (for
609/// named-field structs), a JavaScript array (for tuple structs), or
610/// `undefined` (for unit structs) and read each field in turn via
611/// [`Object::get`](rquickjs_core::Object::get) or
612/// [`Array::get`](rquickjs_core::Array::get).
613///
614/// # Attribute options
615///
616/// Options can be supplied via a `#[qjs(...)]` attribute on the container or
617/// individual fields.
618///
619/// | **Option** | **Scope** | **Value** | **Description** |
620/// |--------------|------------|-----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
621/// | `crate` | Container | String | Path of the rquickjs crate, in case the macro cannot resolve it automatically. |
622/// | `rename_all` | Container | Casing | Rewrites every field name using the given case (`lowercase`, `UPPERCASE`, `camelCase`, `PascalCase`, `snake_case`, or `SCREAMING_SNAKE`) before reading it from the JS object. |
623/// | `rename` | Field | String | Use the supplied string as the JavaScript property name instead of the Rust identifier. |
624///
625/// # Example
626///
627/// ```
628/// use rquickjs::{Context, FromJs, Runtime};
629///
630/// #[derive(Debug, PartialEq, FromJs)]
631/// #[qjs(rename_all = "camelCase")]
632/// struct Spec {
633/// some_value: u32,
634/// #[qjs(rename = "labelText")]
635/// label_text: String,
636/// }
637///
638/// let rt = Runtime::new().unwrap();
639/// let ctx = Context::full(&rt).unwrap();
640/// ctx.with(|ctx| {
641/// let spec: Spec = ctx
642/// .eval(r#"({ someValue: 2, labelText: "beta" })"#)
643/// .unwrap();
644/// assert_eq!(spec, Spec { some_value: 2, label_text: "beta".into() });
645/// });
646/// ```
647///
648/// # Incompatibility with [`macro@class`]
649///
650/// This derive cannot be stacked on a type that is also tagged with
651/// `#[rquickjs::class]`. `#[class]` already generates a `FromJs` impl that
652/// round-trips through a [`Class<Self>`](rquickjs_core::class::Class)
653/// instance, whereas this derive reads field-by-field from a plain JS
654/// object. The two representations are mutually exclusive; combining them
655/// produces a compile error pointing at the offending derive.
656#[proc_macro_derive(FromJs, attributes(qjs))]
657pub fn from_js(stream: TokenStream1) -> TokenStream1 {
658 let derive_input = parse_macro_input!(stream as DeriveInput);
659 match convert::expand_from_js(derive_input) {
660 Ok(x) => x.into(),
661 Err(e) => e.into_compile_error().into(),
662 }
663}
664
665/// A macro for deriving [`IntoJs`](rquickjs_core::IntoJs) for plain-data structs.
666///
667/// Generated impls construct a JavaScript object (for named-field structs),
668/// a JavaScript array (for tuple structs), or `undefined` (for unit structs)
669/// and write each field in turn via
670/// [`Object::set`](rquickjs_core::Object::set) or
671/// [`Array::set`](rquickjs_core::Array::set).
672///
673/// The supported `#[qjs(...)]` attributes are the same as for
674/// [`macro@FromJs`]; see its documentation for details.
675///
676/// # Example
677///
678/// ```
679/// use rquickjs::{CatchResultExt, Context, IntoJs, Runtime};
680///
681/// #[derive(IntoJs)]
682/// struct Pair(u32, String);
683///
684/// let rt = Runtime::new().unwrap();
685/// let ctx = Context::full(&rt).unwrap();
686/// ctx.with(|ctx| {
687/// ctx.globals().set("pair", Pair(3, "gamma".into())).unwrap();
688/// ctx.eval::<(), _>(
689/// r#"if (pair[0] !== 3 || pair[1] !== "gamma") { throw new Error() }"#,
690/// )
691/// .catch(&ctx)
692/// .unwrap();
693/// });
694/// ```
695///
696/// # Incompatibility with [`macro@class`]
697///
698/// This derive cannot be stacked on a type that is also tagged with
699/// `#[rquickjs::class]`. `#[class]` already generates an `IntoJs` impl that
700/// round-trips through a [`Class<Self>`](rquickjs_core::class::Class)
701/// instance, whereas this derive writes the value as a plain JS object.
702/// The two representations are mutually exclusive; combining them produces
703/// a compile error pointing at the offending derive.
704#[proc_macro_derive(IntoJs, attributes(qjs))]
705pub fn into_js(stream: TokenStream1) -> TokenStream1 {
706 let derive_input = parse_macro_input!(stream as DeriveInput);
707 match convert::expand_into_js(derive_input) {
708 Ok(x) => x.into(),
709 Err(e) => e.into_compile_error().into(),
710 }
711}
712
713/// A macro for embedding JavaScript code into a binary.
714///
715/// Compiles a JavaScript module to bytecode and then compiles the resulting bytecode into the
716/// binary. Each file loaded is turned into its own module. The macro takes a list of paths to
717/// files to be compiled into a module with an option name. Module paths are relative to the crate
718/// manifest file.
719///
720/// # Usage
721///
722/// ```
723/// use rquickjs::{embed, loader::Bundle, CatchResultExt, Context, Module, Runtime};
724///
725/// /// load the `my_module.js` file and name it myModule
726/// static BUNDLE: Bundle = embed! {
727/// "myModule": "my_module.js",
728/// };
729///
730/// fn main() {
731/// let rt = Runtime::new().unwrap();
732/// let ctx = Context::full(&rt).unwrap();
733///
734/// rt.set_loader(BUNDLE, BUNDLE);
735/// ctx.with(|ctx| {
736/// Module::evaluate(
737/// ctx.clone(),
738/// "testModule",
739/// r#"
740/// import { foo } from 'myModule';
741/// if(foo() !== 2){
742/// throw new Error("Function didn't return the correct value");
743/// }
744/// "#,
745/// )
746/// .unwrap()
747/// .finish::<()>()
748/// .catch(&ctx)
749/// .unwrap();
750/// })
751/// }
752/// ```
753#[proc_macro]
754pub fn embed(item: TokenStream1) -> TokenStream1 {
755 let embed_modules: embed::EmbedModules = parse_macro_input!(item);
756 match embed::embed(embed_modules) {
757 Ok(x) => x.into(),
758 Err(e) => e.into_compile_error().into(),
759 }
760}
761
762/// A Macro for auto deriving the JsLifetime trait.
763#[proc_macro_derive(JsLifetime, attributes(qjs))]
764pub fn js_lifetime(stream: TokenStream1) -> TokenStream1 {
765 let derive_input = parse_macro_input!(stream as DeriveInput);
766 match js_lifetime::expand(derive_input) {
767 Ok(x) => x.into(),
768 Err(e) => e.into_compile_error().into(),
769 }
770}