File
Blob: src/rust/jsg/macros.rs
| 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] |
| 21 | macro_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] |
| 84 | macro_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] |
| 152 | macro_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 | } |