prosody 0.5.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/.config/rail.toml +26 -0
  3. data/.release-please-manifest.json +1 -1
  4. data/.ruby-version +1 -1
  5. data/.taplo.toml +1 -1
  6. data/AGENTS.md +29 -15
  7. data/CHANGELOG.md +7 -0
  8. data/CONFIGURATION.md +19 -16
  9. data/Cargo.lock +254 -235
  10. data/Cargo.toml +11 -8
  11. data/README.md +100 -23
  12. data/examples/keyed_state.rb +10 -2
  13. data/examples/keyed_state.rbs +1 -0
  14. data/ext/prosody/Cargo.toml +1 -1
  15. data/ext/prosody/src/admin.rs +36 -36
  16. data/ext/prosody/src/bridge/mod.rs +5 -10
  17. data/ext/prosody/src/client/config/connections.rs +170 -0
  18. data/ext/prosody/src/client/config/middleware.rs +184 -0
  19. data/ext/prosody/src/client/config/mod.rs +396 -0
  20. data/ext/prosody/src/client/config/state.rs +323 -0
  21. data/ext/prosody/src/client/mod.rs +31 -117
  22. data/ext/prosody/src/client/readers.rs +106 -0
  23. data/ext/prosody/src/client/request.rs +2 -2
  24. data/ext/prosody/src/client/support.rs +13 -32
  25. data/ext/prosody/src/gvl.rs +8 -6
  26. data/ext/prosody/src/handler/{context.rs → context/mod.rs} +36 -150
  27. data/ext/prosody/src/handler/context/vending.rs +138 -0
  28. data/ext/prosody/src/handler/message.rs +36 -0
  29. data/ext/prosody/src/handler/mod.rs +18 -8
  30. data/ext/prosody/src/handler/state/deque.rs +144 -0
  31. data/ext/prosody/src/handler/state/mod.rs +163 -268
  32. data/ext/prosody/src/handler/state/query.rs +264 -0
  33. data/ext/prosody/src/handler/state/registration.rs +15 -86
  34. data/ext/prosody/src/handler/state/scan.rs +33 -141
  35. data/ext/prosody/src/handler/state/set.rs +98 -0
  36. data/ext/prosody/src/lib.rs +54 -36
  37. data/ext/prosody/src/logging.rs +6 -6
  38. data/ext/prosody/src/published.rs +171 -152
  39. data/ext/prosody/src/scheduler/result.rs +2 -1
  40. data/ext/prosody/src/util.rs +65 -3
  41. data/lib/prosody/client.rb +32 -0
  42. data/lib/prosody/configuration.rb +20 -12
  43. data/lib/prosody/demand.rb +27 -0
  44. data/lib/prosody/native_stubs/client.rb +157 -0
  45. data/lib/prosody/native_stubs/context.rb +178 -0
  46. data/lib/prosody/native_stubs/message.rb +133 -0
  47. data/lib/prosody/native_stubs.rb +11 -956
  48. data/lib/prosody/state/deque.rb +253 -0
  49. data/lib/prosody/state/map.rb +313 -0
  50. data/lib/prosody/state/set.rb +121 -0
  51. data/lib/prosody/state/value.rb +58 -0
  52. data/lib/prosody/state.rb +157 -677
  53. data/lib/prosody/version.rb +1 -1
  54. data/lib/prosody.rb +2 -1
  55. data/sig/configuration.rbs +19 -10
  56. data/sig/prosody.rbs +37 -4
  57. data/sig/published.rbs +102 -0
  58. data/sig/state.rbs +109 -122
  59. data/typecheck/payload_types.rb +12 -0
  60. data/typecheck/payload_types.rbs +1 -0
  61. metadata +30 -11
  62. data/ext/prosody/src/client/config.rs +0 -1300
@@ -14,52 +14,36 @@ use crate::ROOT_MOD;
14
14
  use crate::bridge::Bridge;
15
15
  use crate::handler::message::Message;
16
16
  use crate::tracing_util::extract_opentelemetry_context;
17
- use magnus::value::{Lazy, ReprValue};
18
- use magnus::{Error, ExceptionClass, IntoValue, Module, Ruby, StaticSymbol, TryConvert, Value};
17
+ use magnus::value::ReprValue;
18
+ use magnus::{
19
+ Error, ExceptionClass, IntoValue, Module, RClass, RHash, RModule, Ruby, StaticSymbol,
20
+ TryConvert, Value, method,
21
+ };
19
22
  use opentelemetry::propagation::TextMapCompositePropagator;
20
23
  use opentelemetry::trace::FutureExt;
21
24
  use prosody::consumer::event_context::{
22
- DynDequeState, DynMapState, DynValueState, ErasedCategory, ErasedStateError,
25
+ DynMapState, DynValueState, ErasedCategory, ErasedStateError,
23
26
  };
24
27
  use prosody::consumer::message::ConsumerMessage;
25
- use prosody::state::Direction;
28
+ use prosody::state::StoreOutcome;
26
29
  use serde_json::Value as JsonValue;
27
30
  use serde_magnus::{deserialize, serialize};
28
31
  use std::sync::Arc;
29
32
  use tracing::Span;
30
33
 
31
- /// Lazily resolved `Prosody::PermanentStateError` class (defined in Ruby).
32
- #[allow(
33
- clippy::expect_used,
34
- reason = "mirrors bridge.rs QUEUE_CLASS Lazy pattern"
35
- )]
36
- static PERMANENT_STATE_ERROR: Lazy<ExceptionClass> = Lazy::new(|ruby| {
37
- ruby.get_inner(&ROOT_MOD)
38
- .const_get("PermanentStateError")
39
- .expect("Prosody::PermanentStateError")
40
- });
41
-
42
- /// Lazily resolved `Prosody::TransientStateError` class (defined in Ruby).
43
- #[allow(
44
- clippy::expect_used,
45
- reason = "mirrors bridge.rs QUEUE_CLASS Lazy pattern"
46
- )]
47
- static TRANSIENT_STATE_ERROR: Lazy<ExceptionClass> = Lazy::new(|ruby| {
48
- ruby.get_inner(&ROOT_MOD)
49
- .const_get("TransientStateError")
50
- .expect("Prosody::TransientStateError")
51
- });
52
-
53
- /// Lazily resolved `Prosody::NullValueError` class (defined in Ruby).
54
- #[allow(
55
- clippy::expect_used,
56
- reason = "mirrors bridge.rs QUEUE_CLASS Lazy pattern"
57
- )]
58
- static NULL_VALUE_ERROR: Lazy<ExceptionClass> = Lazy::new(|ruby| {
59
- ruby.get_inner(&ROOT_MOD)
60
- .const_get("NullValueError")
61
- .expect("Prosody::NullValueError")
62
- });
34
+ /// Builds an error of the Ruby-defined `Prosody::<class>` exception class.
35
+ ///
36
+ /// A failed class lookup returns the lookup error in place of the state
37
+ /// error.
38
+ fn ruby_error(ruby: &Ruby, class: &str, message: String) -> Error {
39
+ match ruby
40
+ .get_inner(&ROOT_MOD)
41
+ .const_get::<_, ExceptionClass>(class)
42
+ {
43
+ Ok(class) => Error::new(class, message),
44
+ Err(error) => error,
45
+ }
46
+ }
63
47
 
64
48
  /// Maps an erased state error to the matching Ruby state-error class.
65
49
  ///
@@ -68,60 +52,32 @@ static NULL_VALUE_ERROR: Lazy<ExceptionClass> = Lazy::new(|ruby| {
68
52
  /// `#permanent?` path reclassifies a rethrown error with no change.
69
53
  pub(crate) fn state_error(ruby: &Ruby, error: &ErasedStateError) -> Error {
70
54
  let class = match error.category() {
71
- ErasedCategory::Permanent => ruby.get_inner(&PERMANENT_STATE_ERROR),
72
- ErasedCategory::Transient => ruby.get_inner(&TRANSIENT_STATE_ERROR),
55
+ ErasedCategory::Permanent => "PermanentStateError",
56
+ ErasedCategory::Transient => "TransientStateError",
73
57
  };
74
- Error::new(class, error.message().to_owned())
58
+ ruby_error(ruby, class, error.message().to_owned())
75
59
  }
76
60
 
77
61
  /// Builds a transient state error for a caller-caused condition the glue
78
62
  /// detects (a wrong item shape, an invalid direction token, an unrepresentable
79
63
  /// value).
80
64
  fn transient_state_error(ruby: &Ruby, message: String) -> Error {
81
- Error::new(ruby.get_inner(&TRANSIENT_STATE_ERROR), message)
82
- }
83
-
84
- /// Builds a null-value error for a rejected JSON-null write.
85
- fn null_value_error(ruby: &Ruby, message: String) -> Error {
86
- Error::new(ruby.get_inner(&NULL_VALUE_ERROR), message)
87
- }
88
-
89
- /// Converts an optional JSON item into a Ruby value or `nil`.
90
- fn json_or_nil(ruby: &Ruby, item: Option<JsonValue>) -> Result<Value, Error> {
91
- match item {
92
- Some(value) => serialize(ruby, &value),
93
- None => Ok(ruby.qnil().as_value()),
94
- }
65
+ ruby_error(ruby, "TransientStateError", message)
95
66
  }
96
67
 
97
68
  /// Converts an optional message item into a `Prosody::Message` or `nil`.
98
- #[allow(
99
- clippy::unnecessary_wraps,
100
- reason = "parity with json_or_nil for uniform call sites"
101
- )]
102
- fn message_or_nil(ruby: &Ruby, item: Option<ConsumerMessage<JsonValue>>) -> Result<Value, Error> {
103
- match item {
104
- Some(message) => Ok(Message::from(message).into_value_with(ruby)),
105
- None => Ok(ruby.qnil().as_value()),
106
- }
69
+ fn message_or_nil(ruby: &Ruby, item: Option<ConsumerMessage<JsonValue>>) -> Value {
70
+ item.map(Message::from).into_value_with(ruby)
107
71
  }
108
72
 
109
- /// Converts a Ruby argument into a storable JSON item.
73
+ /// Converts a Ruby argument into a JSON item.
110
74
  ///
111
75
  /// A value with no JSON representation is a caller mistake and rejects
112
- /// transient. JSON `null` is rejected with a [`crate::NullValueError`] naming
113
- /// the deletion verb via `null_advice`.
114
- fn json_write_item(ruby: &Ruby, value: Value, null_advice: &str) -> Result<JsonValue, Error> {
115
- let value: JsonValue = deserialize(ruby, value).map_err(|error| {
76
+ /// transient. Core rejects a JSON `null` write as permanent.
77
+ fn json_write_item(ruby: &Ruby, value: Value) -> Result<JsonValue, Error> {
78
+ deserialize(ruby, value).map_err(|error| {
116
79
  transient_state_error(ruby, format!("value is not representable as JSON: {error}"))
117
- })?;
118
- if value.is_null() {
119
- return Err(null_value_error(
120
- ruby,
121
- format!("JSON null is not a storable value{null_advice}"),
122
- ));
123
- }
124
- Ok(value)
80
+ })
125
81
  }
126
82
 
127
83
  /// Converts a Ruby argument into a storable message item.
@@ -140,49 +96,48 @@ fn message_write_item(
140
96
  Ok(message.consumer_message())
141
97
  }
142
98
 
143
- /// Parses a scan-direction token into the core [`Direction`].
99
+ /// Maps a core [`StoreOutcome`] to `:applied` or `:no_op`.
144
100
  ///
145
- /// An invalid token is a caller mistake and rejects transient.
146
- pub(crate) fn parse_direction(ruby: &Ruby, direction: StaticSymbol) -> Result<Direction, Error> {
147
- match direction.name()? {
148
- "forward" => Ok(Direction::Forward),
149
- "backward" => Ok(Direction::Backward),
150
- other => Err(transient_state_error(
151
- ruby,
152
- format!("direction: expected :forward or :backward, got :{other}"),
153
- )),
101
+ /// `:applied` means the call drained buffered operations. `:no_op` means
102
+ /// nothing was buffered.
103
+ fn outcome_symbol(ruby: &Ruby, outcome: StoreOutcome) -> StaticSymbol {
104
+ match outcome {
105
+ StoreOutcome::Applied => ruby.sym_new("applied"),
106
+ StoreOutcome::NoOp => ruby.sym_new("no_op"),
154
107
  }
155
108
  }
156
109
 
157
- /// Drives an erased async op that returns `Result<_, ErasedStateError>` through
158
- /// [`Bridge::wait_for`] with the extracted carrier active, yielding the op's
159
- /// `Ok` value (state error mapped to the matching Ruby class).
160
- macro_rules! run_op {
161
- ($ruby:expr, $this:expr, $handle:expr, $call:ident ( $($arg:expr),* )) => {{
162
- let handle = Arc::clone($handle);
163
- let context = extract_opentelemetry_context($ruby, &$this.propagator)?;
164
- $this
165
- .bridge
166
- .wait_for(
167
- $ruby,
168
- async move { handle.$call($($arg),*).with_context(context).await },
169
- Span::current(),
170
- )?
171
- .map_err(|error| state_error($ruby, &error))
172
- }};
110
+ /// Defines the class that a `magnus::wrap` path such as
111
+ /// `"Prosody::NativeSetState"` names.
112
+ fn wrapped_class(ruby: &Ruby, module: RModule, path: &str) -> Result<RClass, Error> {
113
+ module.define_class(path.trim_start_matches("Prosody::"), ruby.class_object())
173
114
  }
174
115
 
175
- /// Drives an infallible erased async op (returning `()`) through
176
- /// [`Bridge::wait_for`] with the extracted carrier active.
177
- macro_rules! run_infallible {
178
- ($ruby:expr, $this:expr, $handle:expr, $call:ident ()) => {{
116
+ /// Waits for one state operation in the caller's trace. An
117
+ /// [`ErasedStateError`] raises its matching Ruby class.
118
+ pub(crate) fn run_state<F, T>(
119
+ ruby: &Ruby,
120
+ bridge: &Bridge,
121
+ propagator: &Arc<TextMapCompositePropagator>,
122
+ operation: F,
123
+ ) -> Result<T, Error>
124
+ where
125
+ F: Future<Output = Result<T, ErasedStateError>> + Send + 'static,
126
+ T: Send + 'static,
127
+ {
128
+ let context = extract_opentelemetry_context(ruby, propagator)?;
129
+ bridge
130
+ .wait_for(ruby, operation.with_context(context), Span::current())?
131
+ .map_err(|error| state_error(ruby, &error))
132
+ }
133
+
134
+ /// Calls one method of an erased handle through [`run_state`].
135
+ macro_rules! run_op {
136
+ ($ruby:expr, $this:expr, $handle:expr, $call:ident ( $($arg:expr),* )) => {{
179
137
  let handle = Arc::clone($handle);
180
- let context = extract_opentelemetry_context($ruby, &$this.propagator)?;
181
- $this.bridge.wait_for(
182
- $ruby,
183
- async move { handle.$call().with_context(context).await },
184
- Span::current(),
185
- )?
138
+ run_state($ruby, &$this.bridge, &$this.propagator, async move {
139
+ handle.$call($($arg),*).await
140
+ })
186
141
  }};
187
142
  }
188
143
 
@@ -213,25 +168,36 @@ macro_rules! value_state {
213
168
  ($restore)(ruby, run_op!(ruby, this, &this.state, get())?)
214
169
  }
215
170
 
216
- fn set(ruby: &Ruby, this: &Self, value: Value) -> Result<Value, Error> {
171
+ fn set(ruby: &Ruby, this: &Self, value: Value) -> Result<(), Error> {
217
172
  let item = ($prepare)(ruby, value)?;
218
- run_op!(ruby, this, &this.state, set(item))?;
219
- Ok(ruby.qnil().as_value())
173
+ run_op!(ruby, this, &this.state, set(item))
174
+ }
175
+
176
+ fn clear(ruby: &Ruby, this: &Self) -> Result<(), Error> {
177
+ run_op!(ruby, this, &this.state, clear())
220
178
  }
221
179
 
222
- fn clear(ruby: &Ruby, this: &Self) -> Result<Value, Error> {
223
- run_op!(ruby, this, &this.state, clear())?;
224
- Ok(ruby.qnil().as_value())
180
+ fn commit(ruby: &Ruby, this: &Self) -> Result<StaticSymbol, Error> {
181
+ let outcome = run_op!(ruby, this, &this.state, commit())?;
182
+ Ok(outcome_symbol(ruby, outcome))
225
183
  }
226
184
 
227
- fn commit(ruby: &Ruby, this: &Self) -> Result<Value, Error> {
228
- run_op!(ruby, this, &this.state, commit())?;
229
- Ok(ruby.qnil().as_value())
185
+ fn rollback(ruby: &Ruby, this: &Self) -> Result<StaticSymbol, Error> {
186
+ let state = Arc::clone(&this.state);
187
+ let outcome = run_state(ruby, &this.bridge, &this.propagator, async move {
188
+ Ok(state.rollback().await)
189
+ })?;
190
+ Ok(outcome_symbol(ruby, outcome))
230
191
  }
231
192
 
232
- fn rollback(ruby: &Ruby, this: &Self) -> Result<Value, Error> {
233
- run_infallible!(ruby, this, &this.state, rollback());
234
- Ok(ruby.qnil().as_value())
193
+ pub(super) fn register(ruby: &Ruby, module: RModule) -> Result<(), Error> {
194
+ let class = wrapped_class(ruby, module, $class)?;
195
+ class.define_method("get", method!($name::get, 0))?;
196
+ class.define_method("set", method!($name::set, 1))?;
197
+ class.define_method("clear", method!($name::clear, 0))?;
198
+ class.define_method("commit", method!($name::commit, 0))?;
199
+ class.define_method("rollback", method!($name::rollback, 0))?;
200
+ Ok(())
235
201
  }
236
202
  }
237
203
  };
@@ -241,8 +207,8 @@ value_state!(
241
207
  NativeJsonValueState,
242
208
  "Prosody::NativeJsonValueState",
243
209
  JsonValue,
244
- |ruby, value| json_write_item(ruby, value, "; use clear to remove the value"),
245
- json_or_nil
210
+ json_write_item,
211
+ |ruby, item| serialize::<_, Value>(ruby, &item)
246
212
  );
247
213
  value_state!(
248
214
  NativeMessageValueState,
@@ -253,7 +219,7 @@ value_state!(
253
219
  value,
254
220
  "; use clear to delete a message value collection"
255
221
  ),
256
- message_or_nil
222
+ |ruby, item| Ok::<_, Error>(message_or_nil(ruby, item))
257
223
  );
258
224
 
259
225
  macro_rules! map_state {
@@ -283,8 +249,12 @@ macro_rules! map_state {
283
249
  ($restore)(ruby, run_op!(ruby, this, &this.state, get(key))?)
284
250
  }
285
251
 
286
- fn contains_key(ruby: &Ruby, this: &Self, key: String) -> Result<Value, Error> {
287
- Ok(run_op!(ruby, this, &this.state, contains_key(key))?.into_value_with(ruby))
252
+ fn contains_key(ruby: &Ruby, this: &Self, key: String) -> Result<bool, Error> {
253
+ run_op!(ruby, this, &this.state, contains_key(key))
254
+ }
255
+
256
+ fn is_empty(ruby: &Ruby, this: &Self) -> Result<bool, Error> {
257
+ run_op!(ruby, this, &this.state, is_empty())
288
258
  }
289
259
 
290
260
  fn get_many(ruby: &Ruby, this: &Self, keys: Vec<String>) -> Result<Value, Error> {
@@ -294,56 +264,83 @@ macro_rules! map_state {
294
264
  Ok(array.as_value())
295
265
  }
296
266
 
297
- fn set(ruby: &Ruby, this: &Self, key: String, value: Value) -> Result<Value, Error> {
267
+ fn contains_many(
268
+ ruby: &Ruby,
269
+ this: &Self,
270
+ keys: Vec<String>,
271
+ ) -> Result<Vec<bool>, Error> {
272
+ run_op!(ruby, this, &this.state, contains_many(keys))
273
+ }
274
+
275
+ fn set(ruby: &Ruby, this: &Self, key: String, value: Value) -> Result<(), Error> {
298
276
  let item = ($prepare)(ruby, value)?;
299
- run_op!(ruby, this, &this.state, set(key, item))?;
300
- Ok(ruby.qnil().as_value())
277
+ run_op!(ruby, this, &this.state, set(key, item))
301
278
  }
302
279
 
303
- fn remove(ruby: &Ruby, this: &Self, key: String) -> Result<Value, Error> {
304
- run_op!(ruby, this, &this.state, remove(key))?;
305
- Ok(ruby.qnil().as_value())
280
+ fn remove(ruby: &Ruby, this: &Self, key: String) -> Result<(), Error> {
281
+ run_op!(ruby, this, &this.state, remove(key))
306
282
  }
307
283
 
308
- fn clear(ruby: &Ruby, this: &Self) -> Result<Value, Error> {
309
- run_op!(ruby, this, &this.state, clear())?;
310
- Ok(ruby.qnil().as_value())
284
+ fn clear(ruby: &Ruby, this: &Self) -> Result<(), Error> {
285
+ run_op!(ruby, this, &this.state, clear())
311
286
  }
312
287
 
313
- fn scan(ruby: &Ruby, this: &Self, direction: StaticSymbol) -> Result<$scan, Error> {
314
- let direction = parse_direction(ruby, direction)?;
315
- let _guard = extract_opentelemetry_context(ruby, &this.propagator)?.attach();
316
- $scan::new(
317
- ruby,
318
- this.state.scan(direction),
288
+ fn scan(
289
+ ruby: &Ruby,
290
+ this: &Self,
291
+ direction: StaticSymbol,
292
+ options: RHash,
293
+ ) -> Result<$scan, Error> {
294
+ let query = key_query(ruby, direction, options)?;
295
+ Ok($scan::new(
296
+ this.state.entries().with_query(query).stream(),
319
297
  this.bridge.clone(),
320
298
  Arc::clone(&this.propagator),
321
- )
299
+ ))
322
300
  }
323
301
 
324
302
  fn keys(
325
303
  ruby: &Ruby,
326
304
  this: &Self,
327
305
  direction: StaticSymbol,
306
+ options: RHash,
328
307
  ) -> Result<NativeMapKeyScan, Error> {
329
- let direction = parse_direction(ruby, direction)?;
330
- let _guard = extract_opentelemetry_context(ruby, &this.propagator)?.attach();
331
- NativeMapKeyScan::new(
332
- ruby,
333
- this.state.keys(direction),
308
+ let query = key_query(ruby, direction, options)?;
309
+ Ok(NativeMapKeyScan::new(
310
+ this.state.keys().with_query(query).stream(),
334
311
  this.bridge.clone(),
335
312
  Arc::clone(&this.propagator),
336
- )
313
+ ))
337
314
  }
338
315
 
339
- fn commit(ruby: &Ruby, this: &Self) -> Result<Value, Error> {
340
- run_op!(ruby, this, &this.state, commit())?;
341
- Ok(ruby.qnil().as_value())
316
+ fn commit(ruby: &Ruby, this: &Self) -> Result<StaticSymbol, Error> {
317
+ let outcome = run_op!(ruby, this, &this.state, commit())?;
318
+ Ok(outcome_symbol(ruby, outcome))
342
319
  }
343
320
 
344
- fn rollback(ruby: &Ruby, this: &Self) -> Result<Value, Error> {
345
- run_infallible!(ruby, this, &this.state, rollback());
346
- Ok(ruby.qnil().as_value())
321
+ fn rollback(ruby: &Ruby, this: &Self) -> Result<StaticSymbol, Error> {
322
+ let state = Arc::clone(&this.state);
323
+ let outcome = run_state(ruby, &this.bridge, &this.propagator, async move {
324
+ Ok(state.rollback().await)
325
+ })?;
326
+ Ok(outcome_symbol(ruby, outcome))
327
+ }
328
+
329
+ pub(super) fn register(ruby: &Ruby, module: RModule) -> Result<(), Error> {
330
+ let class = wrapped_class(ruby, module, $class)?;
331
+ class.define_method("get", method!($name::get, 1))?;
332
+ class.define_method("contains_key", method!($name::contains_key, 1))?;
333
+ class.define_method("is_empty", method!($name::is_empty, 0))?;
334
+ class.define_method("get_many", method!($name::get_many, 1))?;
335
+ class.define_method("contains_many", method!($name::contains_many, 1))?;
336
+ class.define_method("set", method!($name::set, 2))?;
337
+ class.define_method("remove", method!($name::remove, 1))?;
338
+ class.define_method("clear", method!($name::clear, 0))?;
339
+ class.define_method("scan", method!($name::scan, 2))?;
340
+ class.define_method("keys", method!($name::keys, 2))?;
341
+ class.define_method("commit", method!($name::commit, 0))?;
342
+ class.define_method("rollback", method!($name::rollback, 0))?;
343
+ Ok(())
347
344
  }
348
345
  }
349
346
  };
@@ -354,8 +351,8 @@ map_state!(
354
351
  "Prosody::NativeJsonMapState",
355
352
  JsonValue,
356
353
  NativeJsonMapScan,
357
- |ruby, value| json_write_item(ruby, value, "; use delete(key) to remove the entry"),
358
- json_or_nil
354
+ json_write_item,
355
+ |ruby, item| serialize::<_, Value>(ruby, &item)
359
356
  );
360
357
  map_state!(
361
358
  NativeMessageMapState,
@@ -367,122 +364,20 @@ map_state!(
367
364
  value,
368
365
  "; use delete(key) to remove a message map entry"
369
366
  ),
370
- message_or_nil
371
- );
372
- macro_rules! deque_state {
373
- ($name:ident, $class:literal, $item:ty, $scan:ident, $prepare:expr, $restore:expr) => {
374
- /// Native deque handle with one payload type.
375
- #[magnus::wrap(class = $class)]
376
- pub struct $name {
377
- state: Arc<dyn DynDequeState<$item>>,
378
- bridge: Bridge,
379
- propagator: Arc<TextMapCompositePropagator>,
380
- }
381
-
382
- impl $name {
383
- pub(crate) fn new(
384
- state: Arc<dyn DynDequeState<$item>>,
385
- bridge: Bridge,
386
- propagator: Arc<TextMapCompositePropagator>,
387
- ) -> Self {
388
- Self {
389
- state,
390
- bridge,
391
- propagator,
392
- }
393
- }
394
-
395
- fn len(ruby: &Ruby, this: &Self) -> Result<Value, Error> {
396
- Ok(run_op!(ruby, this, &this.state, len())?.into_value_with(ruby))
397
- }
398
-
399
- fn is_empty(ruby: &Ruby, this: &Self) -> Result<Value, Error> {
400
- Ok(run_op!(ruby, this, &this.state, is_empty())?.into_value_with(ruby))
401
- }
402
-
403
- fn get(ruby: &Ruby, this: &Self, index: usize) -> Result<Value, Error> {
404
- ($restore)(ruby, run_op!(ruby, this, &this.state, get(index))?)
405
- }
406
-
407
- fn peek_front(ruby: &Ruby, this: &Self) -> Result<Value, Error> {
408
- ($restore)(ruby, run_op!(ruby, this, &this.state, peek_front())?)
409
- }
410
-
411
- fn peek_back(ruby: &Ruby, this: &Self) -> Result<Value, Error> {
412
- ($restore)(ruby, run_op!(ruby, this, &this.state, peek_back())?)
413
- }
414
-
415
- fn push_back(ruby: &Ruby, this: &Self, value: Value) -> Result<Value, Error> {
416
- let item = ($prepare)(ruby, value)?;
417
- run_op!(ruby, this, &this.state, push_back(item))?;
418
- Ok(ruby.qnil().as_value())
419
- }
420
-
421
- fn push_front(ruby: &Ruby, this: &Self, value: Value) -> Result<Value, Error> {
422
- let item = ($prepare)(ruby, value)?;
423
- run_op!(ruby, this, &this.state, push_front(item))?;
424
- Ok(ruby.qnil().as_value())
425
- }
426
-
427
- fn pop_front(ruby: &Ruby, this: &Self) -> Result<Value, Error> {
428
- ($restore)(ruby, run_op!(ruby, this, &this.state, pop_front())?)
429
- }
430
-
431
- fn pop_back(ruby: &Ruby, this: &Self) -> Result<Value, Error> {
432
- ($restore)(ruby, run_op!(ruby, this, &this.state, pop_back())?)
433
- }
434
-
435
- fn clear(ruby: &Ruby, this: &Self) -> Result<Value, Error> {
436
- run_op!(ruby, this, &this.state, clear())?;
437
- Ok(ruby.qnil().as_value())
438
- }
439
-
440
- fn scan(ruby: &Ruby, this: &Self, direction: StaticSymbol) -> Result<$scan, Error> {
441
- let direction = parse_direction(ruby, direction)?;
442
- let _guard = extract_opentelemetry_context(ruby, &this.propagator)?.attach();
443
- $scan::new(
444
- ruby,
445
- this.state.scan(direction),
446
- this.bridge.clone(),
447
- Arc::clone(&this.propagator),
448
- )
449
- }
450
-
451
- fn commit(ruby: &Ruby, this: &Self) -> Result<Value, Error> {
452
- run_op!(ruby, this, &this.state, commit())?;
453
- Ok(ruby.qnil().as_value())
454
- }
455
-
456
- fn rollback(ruby: &Ruby, this: &Self) -> Result<Value, Error> {
457
- run_infallible!(ruby, this, &this.state, rollback());
458
- Ok(ruby.qnil().as_value())
459
- }
460
- }
461
- };
462
- }
463
-
464
- deque_state!(
465
- NativeJsonDequeState,
466
- "Prosody::NativeJsonDequeState",
467
- JsonValue,
468
- NativeJsonDequeScan,
469
- |ruby, value| json_write_item(ruby, value, " in a deque"),
470
- json_or_nil
471
- );
472
- deque_state!(
473
- NativeMessageDequeState,
474
- "Prosody::NativeMessageDequeState",
475
- ConsumerMessage<JsonValue>,
476
- NativeMessageDequeScan,
477
- |ruby, value| message_write_item(ruby, value, " to push into a message deque"),
478
- message_or_nil
367
+ |ruby, item| Ok::<_, Error>(message_or_nil(ruby, item))
479
368
  );
369
+ mod deque;
370
+ mod query;
480
371
  mod scan;
372
+ mod set;
481
373
 
374
+ pub(crate) use deque::{NativeJsonDequeState, NativeMessageDequeState};
375
+ pub(crate) use query::{key_query, position_query};
482
376
  pub(crate) use scan::{
483
377
  NativeJsonDequeScan, NativeJsonMapScan, NativeMapKeyScan, NativeMessageDequeScan,
484
- NativeMessageMapScan, published_deque_scan, published_map_key_scan, published_map_scan,
378
+ NativeMessageMapScan,
485
379
  };
380
+ pub(crate) use set::NativeSetState;
486
381
  mod registration;
487
382
 
488
383
  pub(crate) use registration::register;