chrono_machines 0.5.2 → 0.9.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a02c5bea1c05535fe666e96b6dc442318ccbb695145b3fffb3f7c259c9c1233b
4
- data.tar.gz: 4a9ad6b5479cfdd7201fa6b52952f4f44743438a2daadd5d12a7e85d76bd3a28
3
+ metadata.gz: 8784b5114cde3b48cafa9ee9dff5fe69778795235610ecce63a44c73204c28f7
4
+ data.tar.gz: b87ddb52e946b220b0a2552647c85c928aa6b2a0e65e513005a40f1fbfd9e33a
5
5
  SHA512:
6
- metadata.gz: b367f7d8a63032b1950e0a120485e87bac44f31d7b6a02caf747fb30135a6b6aad99e4ce04724b01eb74ae9fe5464958f8d015ab88e8d5ac8dece43576b3ebaf
7
- data.tar.gz: 42c6b3908d329f78521d073883abb4aefa5dd0d1c53072773ab305daae4f179f9645e5c5eb931ba0c0efb75bb32446501547fd67d3f3baf2f32febe650ae1556
6
+ metadata.gz: 1c04099978047a09d84ad12e52ec228bf632063f228cc227ca993012044eec481956703bdac03e57a81995ebec87b46c515e3859d2095262c56d866be94b3b64
7
+ data.tar.gz: 387168ff2aa02a535b1c2e25ce9a2541afa535786b50e8cd28bce5cea56deee20a3fedd9255b74f886948f3b4d567fd1f39a13d0e5b7d13ecab8e67a8a49a7ae
data/CHANGELOG.md CHANGED
@@ -1,5 +1,91 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.9.1](https://github.com/seuros/chrono_machines/compare/chrono_machines/v0.9.0...chrono_machines/v0.9.1) (2026-10-05)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **build:** skip host compile when packaging a cross gem ([5d5b61d](https://github.com/seuros/chrono_machines/commit/5d5b61da68a0815ef3a30f6fb731af844cbd45ee))
9
+
10
+ ## [0.9.0](https://github.com/seuros/chrono_machines/compare/chrono_machines/v0.8.0...chrono_machines/v0.9.0) (2026-10-05)
11
+
12
+
13
+ ### Features
14
+
15
+ * require Ruby 4.0, magnus 0.9, Ractor-safe native ext ([316d6f5](https://github.com/seuros/chrono_machines/commit/316d6f5e3de0f58e33d0a124eec4efafefb78cab))
16
+
17
+ ## [0.8.0](https://github.com/seuros/chrono_machines/compare/chrono_machines/v0.5.0...chrono_machines/v0.8.0) (2026-09-14)
18
+
19
+
20
+ ### ⚠ BREAKING CHANGES
21
+
22
+ * **native:** add async retry driver ([#21](https://github.com/seuros/chrono_machines/issues/21))
23
+
24
+ ### Features
25
+
26
+ * **core:** delay_from - server-hinted retry delays (HTTP Retry-After) ([#25](https://github.com/seuros/chrono_machines/issues/25)) ([2dcf987](https://github.com/seuros/chrono_machines/commit/2dcf9871939b70c053144e9fc296914a3b6105e5))
27
+ * **core:** re-export rand so BackoffStrategy is implementable downstream ([dc1cf49](https://github.com/seuros/chrono_machines/commit/dc1cf494df0124032bec6aee6e4f6a33e43abbfb))
28
+ * fix release ([ad40d8f](https://github.com/seuros/chrono_machines/commit/ad40d8f5b6629e49ba2d5f0cb7f213a1cc1e30c7))
29
+ * **native:** add async retry driver ([#21](https://github.com/seuros/chrono_machines/issues/21)) ([766f207](https://github.com/seuros/chrono_machines/commit/766f20799bcc0c36f7ca6a904fadff227549fee7))
30
+
31
+
32
+ ### Bug Fixes
33
+
34
+ * ignore native_speedup.rb in Zeitwerk loader ([#19](https://github.com/seuros/chrono_machines/issues/19)) ([491cc2d](https://github.com/seuros/chrono_machines/commit/491cc2d3f88786cf6d0c6af68dba7d2511fa3f36))
35
+ * package workspace Cargo manifest so source builds work ([#17](https://github.com/seuros/chrono_machines/issues/17)) ([ac0ddad](https://github.com/seuros/chrono_machines/commit/ac0ddad15d26528771825240a05ed8c84fb61520))
36
+ * trigger release ([02184d5](https://github.com/seuros/chrono_machines/commit/02184d51f6831f53ae6142ea5092710d456363d5))
37
+
38
+ ## [0.5.0](https://github.com/seuros/chrono_machines/compare/chrono_machines/v0.5.0...chrono_machines/v0.5.0) (2026-09-14)
39
+
40
+
41
+ ### ⚠ BREAKING CHANGES
42
+
43
+ * **native:** add async retry driver ([#21](https://github.com/seuros/chrono_machines/issues/21))
44
+
45
+ ### Features
46
+
47
+ * **core:** delay_from - server-hinted retry delays (HTTP Retry-After) ([#25](https://github.com/seuros/chrono_machines/issues/25)) ([2dcf987](https://github.com/seuros/chrono_machines/commit/2dcf9871939b70c053144e9fc296914a3b6105e5))
48
+ * **core:** re-export rand so BackoffStrategy is implementable downstream ([dc1cf49](https://github.com/seuros/chrono_machines/commit/dc1cf494df0124032bec6aee6e4f6a33e43abbfb))
49
+ * fix release ([ad40d8f](https://github.com/seuros/chrono_machines/commit/ad40d8f5b6629e49ba2d5f0cb7f213a1cc1e30c7))
50
+ * **native:** add async retry driver ([#21](https://github.com/seuros/chrono_machines/issues/21)) ([766f207](https://github.com/seuros/chrono_machines/commit/766f20799bcc0c36f7ca6a904fadff227549fee7))
51
+
52
+
53
+ ### Bug Fixes
54
+
55
+ * ignore native_speedup.rb in Zeitwerk loader ([#19](https://github.com/seuros/chrono_machines/issues/19)) ([491cc2d](https://github.com/seuros/chrono_machines/commit/491cc2d3f88786cf6d0c6af68dba7d2511fa3f36))
56
+ * package workspace Cargo manifest so source builds work ([#17](https://github.com/seuros/chrono_machines/issues/17)) ([ac0ddad](https://github.com/seuros/chrono_machines/commit/ac0ddad15d26528771825240a05ed8c84fb61520))
57
+
58
+ ## [0.5.0](https://github.com/seuros/chrono_machines/compare/chrono_machines/v0.5.0...chrono_machines/v0.5.0) (2026-08-23)
59
+
60
+
61
+ ### ⚠ BREAKING CHANGES
62
+
63
+ * **native:** add async retry driver ([#21](https://github.com/seuros/chrono_machines/issues/21))
64
+
65
+ ### Features
66
+
67
+ * **core:** re-export rand so BackoffStrategy is implementable downstream ([dc1cf49](https://github.com/seuros/chrono_machines/commit/dc1cf494df0124032bec6aee6e4f6a33e43abbfb))
68
+ * fix release ([ad40d8f](https://github.com/seuros/chrono_machines/commit/ad40d8f5b6629e49ba2d5f0cb7f213a1cc1e30c7))
69
+ * **native:** add async retry driver ([#21](https://github.com/seuros/chrono_machines/issues/21)) ([766f207](https://github.com/seuros/chrono_machines/commit/766f20799bcc0c36f7ca6a904fadff227549fee7))
70
+
71
+
72
+ ### Bug Fixes
73
+
74
+ * ignore native_speedup.rb in Zeitwerk loader ([#19](https://github.com/seuros/chrono_machines/issues/19)) ([491cc2d](https://github.com/seuros/chrono_machines/commit/491cc2d3f88786cf6d0c6af68dba7d2511fa3f36))
75
+ * package workspace Cargo manifest so source builds work ([#17](https://github.com/seuros/chrono_machines/issues/17)) ([ac0ddad](https://github.com/seuros/chrono_machines/commit/ac0ddad15d26528771825240a05ed8c84fb61520))
76
+
77
+ ## [0.5.0](https://github.com/seuros/chrono_machines/compare/chrono_machines/v0.5.2...chrono_machines/v0.5.0) (2026-08-23)
78
+
79
+
80
+ ### ⚠ BREAKING CHANGES
81
+
82
+ * **native:** add async retry driver ([#21](https://github.com/seuros/chrono_machines/issues/21))
83
+
84
+ ### Features
85
+
86
+ * fix release ([ad40d8f](https://github.com/seuros/chrono_machines/commit/ad40d8f5b6629e49ba2d5f0cb7f213a1cc1e30c7))
87
+ * **native:** add async retry driver ([#21](https://github.com/seuros/chrono_machines/issues/21)) ([766f207](https://github.com/seuros/chrono_machines/commit/766f20799bcc0c36f7ca6a904fadff227549fee7))
88
+
3
89
  ## [0.5.2](https://github.com/seuros/chrono_machines/compare/chrono_machines/v0.5.1...chrono_machines/v0.5.2) (2026-08-07)
4
90
 
5
91
 
data/Cargo.lock CHANGED
@@ -17,17 +17,6 @@ version = "1.0.102"
17
17
  source = "registry+https://github.com/rust-lang/crates.io-index"
18
18
  checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
19
19
 
20
- [[package]]
21
- name = "async-trait"
22
- version = "0.1.89"
23
- source = "registry+https://github.com/rust-lang/crates.io-index"
24
- checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb"
25
- dependencies = [
26
- "proc-macro2",
27
- "quote",
28
- "syn",
29
- ]
30
-
31
20
  [[package]]
32
21
  name = "bindgen"
33
22
  version = "0.72.1"
@@ -69,9 +58,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
69
58
 
70
59
  [[package]]
71
60
  name = "chacha20"
72
- version = "0.10.0"
61
+ version = "0.10.2"
73
62
  source = "registry+https://github.com/rust-lang/crates.io-index"
74
- checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601"
63
+ checksum = "65c35e4b699c7e15ccbe7ee35c005e4fc0a278d22238a2857e6ce2dadeda1b06"
75
64
  dependencies = [
76
65
  "cfg-if",
77
66
  "cpufeatures",
@@ -80,16 +69,15 @@ dependencies = [
80
69
 
81
70
  [[package]]
82
71
  name = "chrono-machines"
83
- version = "0.3.2"
72
+ version = "0.8.0"
84
73
  dependencies = [
85
- "async-trait",
86
74
  "rand",
87
75
  "tokio",
88
76
  ]
89
77
 
90
78
  [[package]]
91
79
  name = "chrono_machines_native"
92
- version = "0.2.3"
80
+ version = "0.9.0"
93
81
  dependencies = [
94
82
  "chrono-machines",
95
83
  "magnus",
@@ -245,9 +233,9 @@ checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a"
245
233
 
246
234
  [[package]]
247
235
  name = "magnus"
248
- version = "0.8.2"
236
+ version = "0.9.2"
249
237
  source = "registry+https://github.com/rust-lang/crates.io-index"
250
- checksum = "3b36a5b126bbe97eb0d02d07acfeb327036c6319fd816139a49824a83b7f9012"
238
+ checksum = "b9068710e00d21be762edca59b9c4c8946dbdb29820d4f76ad559afea07982c9"
251
239
  dependencies = [
252
240
  "magnus-macros",
253
241
  "rb-sys",
@@ -257,9 +245,9 @@ dependencies = [
257
245
 
258
246
  [[package]]
259
247
  name = "magnus-macros"
260
- version = "0.8.0"
248
+ version = "0.9.1"
261
249
  source = "registry+https://github.com/rust-lang/crates.io-index"
262
- checksum = "47607461fd8e1513cb4f2076c197d8092d921a1ea75bd08af97398f593751892"
250
+ checksum = "9dafde2eeb66f090e0f326112323eb8324b12cb32b5d1210e541d3df6b0ce77e"
263
251
  dependencies = [
264
252
  "proc-macro2",
265
253
  "quote",
@@ -330,9 +318,9 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
330
318
 
331
319
  [[package]]
332
320
  name = "rand"
333
- version = "0.10.1"
321
+ version = "0.10.3"
334
322
  source = "registry+https://github.com/rust-lang/crates.io-index"
335
- checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207"
323
+ checksum = "65c9fb96cbc91e3478eaae79a69fcd3f1ae4ad052e471fe6732fff548984b4af"
336
324
  dependencies = [
337
325
  "chacha20",
338
326
  "getrandom",
data/README.md CHANGED
@@ -195,6 +195,32 @@ ChronoMachines.retry(
195
195
  end
196
196
  ```
197
197
 
198
+ ### Server-Hinted Delays (`delay_from`)
199
+
200
+ When the server tells you when to come back - HTTP 429 with `Retry-After` - guessing with exponential backoff is rude. `delay_from` lets the exception dictate the next delay:
201
+
202
+ ```ruby
203
+ ChronoMachines.retry(
204
+ max_attempts: 5,
205
+ max_delay: 30,
206
+ retryable_exceptions: [MyClient::RateLimitError],
207
+ delay_from: ->(exception:, attempt:) {
208
+ exception.respond_to?(:retry_after) ? exception.retry_after : nil
209
+ }
210
+ ) do
211
+ call_rate_limited_api
212
+ end
213
+ ```
214
+
215
+ The hook's return value controls the retry:
216
+
217
+ - `nil` - no hint; fall back to the policy's backoff strategy
218
+ - `Numeric` - sleep exactly that many seconds (no jitter: the server picked the time, honor it)
219
+ - a `Numeric` **beyond `max_delay`** - halt: the server asked for more patience than this policy allows, so the original exception propagates and the caller decides (background job, `Retry-After` response header, ...)
220
+ - `:halt` or `false` - stop retrying immediately, propagate the original exception
221
+
222
+ Anything else raises `ArgumentError` - a broken hook fails loudly instead of silently mangling your retry cadence.
223
+
198
224
  ## The Science of Temporal Jitter
199
225
 
200
226
  ChronoMachines implements **full jitter** exponential backoff:
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "chrono-machines"
3
- version = "0.4.0"
3
+ version = "0.8.0"
4
4
  edition = "2024"
5
5
  authors = ["Abdelkader Boudih <terminale@gmail.com>"]
6
6
  license = "MIT"
@@ -9,6 +9,7 @@ repository = "https://github.com/seuros/chrono_machines"
9
9
  keywords = ["retry", "backoff", "exponential", "jitter", "resilience"]
10
10
  categories = ["no-std", "algorithms"]
11
11
  readme = "README.md"
12
+ exclude = ["tests/", "src/**/tests.rs"]
12
13
 
13
14
  [lib]
14
15
  name = "chrono_machines"
@@ -18,11 +19,13 @@ crate-type = ["lib"]
18
19
  default = ["std"]
19
20
  std = ["alloc", "rand/std", "rand/thread_rng"]
20
21
  alloc = []
21
- async = ["std", "dep:async-trait"]
22
+ # Async retry driver. Needs `alloc` for the builder's boxed callbacks, but not
23
+ # `std`: the futures are native `async fn` in trait, so there is no runtime
24
+ # dependency and nothing to box per sleep.
25
+ async = ["alloc"]
22
26
 
23
27
  [dependencies]
24
28
  rand = { version = "0.10", default-features = false, features = ["std_rng"] }
25
- async-trait = { version = "0.1", optional = true }
26
29
 
27
30
  [dev-dependencies]
28
- tokio = { version = "1", features = ["rt", "macros", "time"] }
31
+ tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros", "time"] }
@@ -55,6 +55,39 @@ fn apply_jitter<R: Rng>(base: f64, jitter_factor: f64, rng: &mut R) -> u64 {
55
55
  }
56
56
 
57
57
  /// Trait for backoff strategies that calculate delays between retry attempts
58
+ ///
59
+ /// The three strategies in this module cover the usual shapes. Implement it
60
+ /// yourself when the delay depends on something they cannot see — a server's
61
+ /// `Retry-After`, a token bucket, a circuit breaker's cool-down.
62
+ ///
63
+ /// [`delay`](BackoffStrategy::delay) is generic over [`Rng`], so an
64
+ /// implementation has to name that trait. Take it from
65
+ /// [`chrono_machines::rand`](crate::rand) rather than adding your own `rand`
66
+ /// dependency; a version skew between the two yields two distinct `Rng` traits
67
+ /// and a mismatch error that points nowhere near the cause.
68
+ ///
69
+ /// ```rust
70
+ /// use chrono_machines::{rand::Rng, BackoffStrategy};
71
+ ///
72
+ /// /// Retries as fast as the loop will go — for tests, where sleeping is waste.
73
+ /// struct Immediate {
74
+ /// max_attempts: u8,
75
+ /// }
76
+ ///
77
+ /// impl BackoffStrategy for Immediate {
78
+ /// fn delay<R: Rng>(&self, attempt: u8, _rng: &mut R) -> Option<u64> {
79
+ /// self.should_retry(attempt).then_some(0)
80
+ /// }
81
+ ///
82
+ /// fn should_retry(&self, attempt: u8) -> bool {
83
+ /// attempt < self.max_attempts
84
+ /// }
85
+ ///
86
+ /// fn max_attempts(&self) -> u8 {
87
+ /// self.max_attempts
88
+ /// }
89
+ /// }
90
+ /// ```
58
91
  pub trait BackoffStrategy {
59
92
  /// Calculate the delay in milliseconds for the given attempt number
60
93
  ///
@@ -81,6 +114,14 @@ pub trait BackoffStrategy {
81
114
 
82
115
  /// Maximum number of retry attempts permitted by this strategy.
83
116
  fn max_attempts(&self) -> u8;
117
+
118
+ /// Maximum delay this strategy will ever produce, if it has a cap.
119
+ ///
120
+ /// A `delay_from` hint beyond this cap halts the retry. `None` (the
121
+ /// default) means uncapped: every hint is honoured.
122
+ fn max_delay_ms(&self) -> Option<u64> {
123
+ None
124
+ }
84
125
  }
85
126
 
86
127
  /// Exponential backoff strategy with configurable jitter
@@ -182,6 +223,10 @@ impl BackoffStrategy for ExponentialBackoff {
182
223
  fn max_attempts(&self) -> u8 {
183
224
  self.max_attempts
184
225
  }
226
+
227
+ fn max_delay_ms(&self) -> Option<u64> {
228
+ Some(self.max_delay_ms)
229
+ }
185
230
  }
186
231
 
187
232
  /// Constant backoff strategy with fixed delay
@@ -350,6 +395,10 @@ impl BackoffStrategy for FibonacciBackoff {
350
395
  fn max_attempts(&self) -> u8 {
351
396
  self.max_attempts
352
397
  }
398
+
399
+ fn max_delay_ms(&self) -> Option<u64> {
400
+ Some(self.max_delay_ms)
401
+ }
353
402
  }
354
403
 
355
404
  /// Backoff policy that can represent any supported strategy.
@@ -401,6 +450,14 @@ impl BackoffStrategy for BackoffPolicy {
401
450
  BackoffPolicy::Fibonacci(policy) => policy.max_attempts(),
402
451
  }
403
452
  }
453
+
454
+ fn max_delay_ms(&self) -> Option<u64> {
455
+ match self {
456
+ BackoffPolicy::Exponential(policy) => policy.max_delay_ms(),
457
+ BackoffPolicy::Constant(policy) => policy.max_delay_ms(),
458
+ BackoffPolicy::Fibonacci(policy) => policy.max_delay_ms(),
459
+ }
460
+ }
404
461
  }
405
462
 
406
463
  impl From<ExponentialBackoff> for BackoffPolicy {
@@ -422,95 +479,4 @@ impl From<FibonacciBackoff> for BackoffPolicy {
422
479
  }
423
480
 
424
481
  #[cfg(test)]
425
- mod tests {
426
- use super::*;
427
- use rand::rngs::StdRng;
428
- use rand::SeedableRng;
429
-
430
- #[test]
431
- fn test_exponential_backoff_builder() {
432
- let backoff = ExponentialBackoff::new()
433
- .base_delay_ms(200)
434
- .multiplier(3.0)
435
- .max_delay_ms(5000)
436
- .max_attempts(5)
437
- .jitter_factor(0.5);
438
-
439
- assert_eq!(backoff.base_delay_ms, 200);
440
- assert_eq!(backoff.multiplier, 3.0);
441
- assert_eq!(backoff.max_delay_ms, 5000);
442
- assert_eq!(backoff.max_attempts, 5);
443
- assert_eq!(backoff.jitter_factor, 0.5);
444
- }
445
-
446
- #[test]
447
- fn test_exponential_delays() {
448
- let backoff = ExponentialBackoff::new()
449
- .base_delay_ms(100)
450
- .multiplier(2.0)
451
- .jitter_factor(0.0); // No jitter for predictable testing
452
-
453
- let mut rng = StdRng::seed_from_u64(42);
454
-
455
- assert_eq!(backoff.delay(1, &mut rng), Some(100));
456
- assert_eq!(backoff.delay(2, &mut rng), Some(200));
457
- assert_eq!(backoff.delay(3, &mut rng), None); // Exceeds max_attempts (default 3)
458
- }
459
-
460
- #[test]
461
- fn test_constant_backoff() {
462
- let backoff = ConstantBackoff::new()
463
- .delay_ms(500)
464
- .max_attempts(4)
465
- .jitter_factor(0.0);
466
-
467
- let mut rng = StdRng::seed_from_u64(42);
468
-
469
- assert_eq!(backoff.delay(1, &mut rng), Some(500));
470
- assert_eq!(backoff.delay(2, &mut rng), Some(500));
471
- assert_eq!(backoff.delay(3, &mut rng), Some(500));
472
- assert_eq!(backoff.delay(4, &mut rng), None);
473
- }
474
-
475
- #[test]
476
- fn test_fibonacci_sequence() {
477
- assert_eq!(fibonacci(1), 1);
478
- assert_eq!(fibonacci(2), 1);
479
- assert_eq!(fibonacci(3), 2);
480
- assert_eq!(fibonacci(4), 3);
481
- assert_eq!(fibonacci(5), 5);
482
- assert_eq!(fibonacci(6), 8);
483
- assert_eq!(fibonacci(7), 13);
484
- }
485
-
486
- #[test]
487
- fn test_fibonacci_backoff() {
488
- let backoff = FibonacciBackoff::new()
489
- .base_delay_ms(100)
490
- .max_attempts(5)
491
- .jitter_factor(0.0);
492
-
493
- let mut rng = StdRng::seed_from_u64(42);
494
-
495
- assert_eq!(backoff.delay(1, &mut rng), Some(100)); // 100 * 1
496
- assert_eq!(backoff.delay(2, &mut rng), Some(100)); // 100 * 1
497
- assert_eq!(backoff.delay(3, &mut rng), Some(200)); // 100 * 2
498
- assert_eq!(backoff.delay(4, &mut rng), Some(300)); // 100 * 3
499
- assert_eq!(backoff.delay(5, &mut rng), None); // Exceeds max_attempts
500
- }
501
-
502
- #[test]
503
- fn test_jitter_application() {
504
- let backoff = ConstantBackoff::new().delay_ms(1000).jitter_factor(1.0); // Full jitter
505
-
506
- let mut rng = StdRng::seed_from_u64(42);
507
- let delays: Vec<u64> = (1..10).filter_map(|i| backoff.delay(i, &mut rng)).collect();
508
-
509
- // With full jitter, delays should vary
510
- let all_different = delays.windows(2).any(|w| w[0] != w[1]);
511
- assert!(all_different, "Full jitter should produce varying delays");
512
-
513
- // All delays should be <= base delay
514
- assert!(delays.iter().all(|&d| d <= 1000));
515
- }
516
- }
482
+ mod tests;
@@ -68,41 +68,4 @@ where
68
68
  }
69
69
 
70
70
  #[cfg(test)]
71
- mod tests {
72
- use super::*;
73
- use crate::backoff::{BackoffPolicy, ExponentialBackoff};
74
- use crate::policy::{clear_global_policies, register_global_policy};
75
-
76
- #[test]
77
- fn test_retry_with_policy_success() {
78
- clear_global_policies();
79
- register_global_policy(
80
- "default",
81
- BackoffPolicy::from(ExponentialBackoff::new().max_attempts(2)),
82
- );
83
-
84
- let mut attempts = 0;
85
- let outcome = retry_with_policy("default", || {
86
- attempts += 1;
87
- if attempts == 1 {
88
- Err::<_, &'static str>("fail")
89
- } else {
90
- Ok("ok")
91
- }
92
- })
93
- .expect("dsl retry should succeed");
94
-
95
- assert_eq!(attempts, 2);
96
- assert_eq!(outcome.into_inner(), "ok");
97
- }
98
-
99
- #[test]
100
- fn test_retry_with_policy_missing() {
101
- clear_global_policies();
102
- let result = retry_with_policy::<_, (), &str>("missing", || Ok(()));
103
- match result {
104
- Err(DslError::PolicyMissing(name)) => assert_eq!(name, "missing"),
105
- _ => panic!("expected policy missing error"),
106
- }
107
- }
108
- }
71
+ mod tests;