Skip to content
File

Blob: src/rust/jsg/feature_flags.rs

rust124 lines
1//! Rust-native access to workerd compatibility flags.
2//!
3//! ```ignore
4//! if lock.feature_flags().get_node_js_compat() {
5//! // Node.js compatibility behavior
6//! }
7//! ```
8 
9use capnp::message::ReaderOptions;
10pub use compatibility_date_capnp::compatibility_flags;
11 
12/// Provides access to the current worker's compatibility flags.
13///
14/// Parsed once from canonical Cap'n Proto bytes during Realm construction
15/// and stored in the per-context [`Realm`](crate::Realm). Access via
16/// [`Lock::feature_flags()`](crate::Lock::feature_flags).
17pub struct FeatureFlags {
18 message: capnp::message::Reader<Vec<Vec<u8>>>,
19}
20 
21impl FeatureFlags {
22 /// Create from canonical (single-segment, no segment table) Cap'n Proto bytes.
23 ///
24 /// On the C++ side, produce these via `capnp::canonicalize(reader)`.
25 ///
26 /// # Panics
27 ///
28 /// Panics if `data` is empty or not word-aligned.
29 pub(crate) fn from_bytes(data: &[u8]) -> Self {
30 assert!(!data.is_empty(), "FeatureFlags data must not be empty");
31 assert!(
32 data.len().is_multiple_of(8),
33 "FeatureFlags data must be word-aligned (got {} bytes)",
34 data.len()
35 );
36 let segments = vec![data.to_vec()];
37 let message = capnp::message::Reader::new(segments, ReaderOptions::new());
38 Self { message }
39 }
40 
41 /// Returns the `CompatibilityFlags` reader.
42 ///
43 /// The reader has a getter for each flag defined in `compatibility-date.capnp`
44 /// (e.g., `get_node_js_compat()`).
45 ///
46 /// # Panics
47 ///
48 /// Panics if the stored message has an invalid capnp root (should never happen
49 /// when constructed via `from_bytes`).
50 pub fn reader(&self) -> compatibility_flags::Reader<'_> {
51 self.message
52 .get_root::<compatibility_flags::Reader<'_>>()
53 .expect("Invalid FeatureFlags capnp root")
54 }
55}
56 
57#[cfg(test)]
58mod tests {
59 use super::*;
60 
61 /// Helper: build a `CompatibilityFlags` capnp message with the given flag setter,
62 /// return the raw single-segment bytes (no wire-format header).
63 fn build_flags<F>(setter: F) -> Vec<u8>
64 where
65 F: FnOnce(compatibility_flags::Builder<'_>),
66 {
67 let mut message = capnp::message::Builder::new_default();
68 {
69 let flags = message.init_root::<compatibility_flags::Builder<'_>>();
70 setter(flags);
71 }
72 let output = message.get_segments_for_output();
73 output[0].to_vec()
74 }
75 
76 #[test]
77 fn from_bytes_roundtrip() {
78 let bytes = build_flags(|mut f| {
79 f.set_node_js_compat(true);
80 });
81 let ff = FeatureFlags::from_bytes(&bytes);
82 assert!(ff.reader().get_node_js_compat());
83 }
84 
85 #[test]
86 #[should_panic(expected = "FeatureFlags data must not be empty")]
87 fn from_bytes_empty_panics() {
88 FeatureFlags::from_bytes(&[]);
89 }
90 
91 #[test]
92 fn default_flags_are_false() {
93 let bytes = build_flags(|_| {});
94 let ff = FeatureFlags::from_bytes(&bytes);
95 assert!(!ff.reader().get_node_js_compat());
96 assert!(!ff.reader().get_node_js_compat_v2());
97 assert!(!ff.reader().get_fetch_refuses_unknown_protocols());
98 }
99 
100 #[test]
101 fn multiple_flags() {
102 let bytes = build_flags(|mut f| {
103 f.set_node_js_compat(true);
104 f.set_node_js_compat_v2(true);
105 f.set_fetch_refuses_unknown_protocols(false);
106 });
107 let ff = FeatureFlags::from_bytes(&bytes);
108 assert!(ff.reader().get_node_js_compat());
109 assert!(ff.reader().get_node_js_compat_v2());
110 assert!(!ff.reader().get_fetch_refuses_unknown_protocols());
111 }
112 
113 #[test]
114 fn reader_called_multiple_times() {
115 let bytes = build_flags(|mut f| {
116 f.set_node_js_compat(true);
117 });
118 let ff = FeatureFlags::from_bytes(&bytes);
119 // Reader can be obtained multiple times from the same FeatureFlags.
120 assert!(ff.reader().get_node_js_compat());
121 assert!(ff.reader().get_node_js_compat());
122 }
123}