Skip to content
File

Blob: src/rust/jsg/macros.rs

rust160 lines
1// Copyright (c) 2026 Cloudflare, Inc.
2// Licensed under the Apache 2.0 license found in the LICENSE file or at:
3// https://opensource.org/licenses/Apache-2.0
4 
5/// Generates a no-op [`Traced`](crate::Traced) implementation for types with no GC-visible
6/// references.
7///
8/// Types that contain only plain data (no `jsg::Rc`, `jsg::v8::Global`, etc.) do not
9/// need to participate in GC tracing. This macro produces an empty `Traced` impl
10/// whose `trace` method is a no-op.
11///
12/// # Example
13///
14/// ```ignore
15/// use jsg::jsg_traced;
16///
17/// struct Id(u64);
18/// jsg_traced!(Id);
19/// ```
20#[macro_export]
21macro_rules! jsg_traced {
22 ($($t:ty),* $(,)?) => {
23 $(impl $crate::Traced for $t {})*
24 };
25}
26 
27/// Validates a condition at runtime, returning a [`jsg::Error`](crate::Error) if the condition
28/// is `false`.
29///
30/// This is the Rust equivalent of the C++ `JSG_REQUIRE` macro defined in
31/// `src/workerd/jsg/exception.h`. While the C++ version throws a KJ exception, this macro
32/// returns `Err(jsg::Error)` via the `?` operator, following Rust's error-handling conventions.
33///
34/// # Syntax
35///
36/// ```ignore
37/// jsg_require!(condition, ExceptionVariant, "message");
38/// jsg_require!(condition, ExceptionVariant, "format {} string", arg1, arg2);
39/// ```
40///
41/// # Parameters
42///
43/// - `condition` — Any expression that evaluates to `bool`. When `true`, the macro is a no-op.
44/// When `false`, an error is returned.
45/// - `ExceptionVariant` — One of the [`ExceptionType`](crate::v8::ffi::ExceptionType) variants
46/// that determines the JavaScript error class thrown to user code. Available variants:
47/// `TypeError`, `RangeError`, `ReferenceError`, `SyntaxError`, `Error`,
48/// `OperationError`, `DataError`, `DataCloneError`, `InvalidAccessError`,
49/// `InvalidStateError`, `InvalidCharacterError`, `NotSupportedError`,
50/// `TimeoutError`, `TypeMismatchError`, `AbortError`, `NotFoundError`.
51/// - `"message"` / `"format string", args...` — A message string, optionally with `format!`-style
52/// arguments. Unlike the C++ `JSG_REQUIRE` which concatenates via `kj::str()`, this macro uses
53/// Rust's standard `format!()` for string interpolation.
54///
55/// # Return Type
56///
57/// The enclosing function must return `jsg::Result<T>` (or any `Result<T, E>` where
58/// `E: From<jsg::Error>`). The macro expands to an early `return Err(...)` on failure.
59///
60/// # Examples
61///
62/// ```ignore
63/// use jsg::{jsg_require, Result};
64///
65/// fn parse_port(value: u32) -> Result<u16> {
66/// jsg_require!(value <= 65535, RangeError, "port {} out of range", value);
67/// Ok(value as u16)
68/// }
69///
70/// fn require_non_empty(s: &str) -> Result<()> {
71/// jsg_require!(!s.is_empty(), TypeError, "string must not be empty");
72/// Ok(())
73/// }
74/// ```
75///
76/// # Comparison with C++
77///
78/// | C++ | Rust |
79/// |-----|------|
80/// | `JSG_REQUIRE(port <= 65535, RangeError, "port ", port, " out of range")` | `jsg_require!(port <= 65535, RangeError, "port {} out of range", port)` |
81/// | Throws `kj::Exception` | Returns `Err(jsg::Error)` |
82/// | Uses `kj::str()` concatenation | Uses `format!()` interpolation |
83#[macro_export]
84macro_rules! jsg_require {
85 ($cond:expr, $err_type:ident, $msg:literal $(, $arg:expr)* $(,)?) => {
86 if !($cond) {
87 return Err($crate::Error {
88 name: $crate::ExceptionType::$err_type,
89 message: format!($msg $(, $arg)*),
90 });
91 }
92 };
93}
94 
95/// Unconditionally returns a [`jsg::Error`](crate::Error) from the enclosing function.
96///
97/// This is the Rust equivalent of the C++ `JSG_FAIL_REQUIRE` macro defined in
98/// `src/workerd/jsg/exception.h`. While the C++ version throws a KJ exception
99/// unconditionally, this macro returns `Err(jsg::Error)` via an early `return`,
100/// following Rust's error-handling conventions.
101///
102/// # Syntax
103///
104/// ```ignore
105/// jsg_fail_require!(ExceptionVariant, "message");
106/// jsg_fail_require!(ExceptionVariant, "format {} string", arg1, arg2);
107/// ```
108///
109/// # Parameters
110///
111/// - `ExceptionVariant` — One of the [`ExceptionType`](crate::v8::ffi::ExceptionType) variants
112/// that determines the JavaScript error class thrown to user code. Available variants:
113/// `TypeError`, `RangeError`, `ReferenceError`, `SyntaxError`, `Error`,
114/// `OperationError`, `DataError`, `DataCloneError`, `InvalidAccessError`,
115/// `InvalidStateError`, `InvalidCharacterError`, `NotSupportedError`,
116/// `TimeoutError`, `TypeMismatchError`, `AbortError`, `NotFoundError`.
117/// - `"message"` / `"format string", args...` — A message string, optionally with `format!`-style
118/// arguments. Unlike the C++ `JSG_FAIL_REQUIRE` which concatenates via `kj::str()`, this macro
119/// uses Rust's standard `format!()` for string interpolation.
120///
121/// # Return Type
122///
123/// The enclosing function must return `jsg::Result<T>` (or any `Result<T, E>` where
124/// `E: From<jsg::Error>`). The macro always triggers an early `return Err(...)`.
125///
126/// # Examples
127///
128/// ```ignore
129/// use jsg::{jsg_fail_require, Result};
130///
131/// fn unsupported_algorithm(name: &str) -> Result<()> {
132/// jsg_fail_require!(NotSupportedError, "algorithm '{}' is not supported", name);
133/// }
134///
135/// fn validate_input(mode: &str) -> Result<String> {
136/// match mode {
137/// "fast" => Ok("fast-path".to_owned()),
138/// "slow" => Ok("slow-path".to_owned()),
139/// _ => jsg_fail_require!(TypeError, "invalid mode: '{}'", mode),
140/// }
141/// }
142/// ```
143///
144/// # Comparison with C++
145///
146/// | C++ | Rust |
147/// |-----|------|
148/// | `JSG_FAIL_REQUIRE(TypeError, "invalid mode: ", mode)` | `jsg_fail_require!(TypeError, "invalid mode: '{}'", mode)` |
149/// | Throws `kj::Exception` | Returns `Err(jsg::Error)` |
150/// | Uses `kj::str()` concatenation | Uses `format!()` interpolation |
151#[macro_export]
152macro_rules! jsg_fail_require {
153 ($err_type:ident, $msg:literal $(, $arg:expr)* $(,)?) => {
154 return Err($crate::Error {
155 name: $crate::ExceptionType::$err_type,
156 message: format!($msg $(, $arg)*),
157 })
158 };
159}