breaker_machines 0.4.0 → 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.
- checksums.yaml +4 -4
- data/README.md +25 -3
- data/ext/breaker_machines_native/Cargo.toml +8 -0
- data/ext/breaker_machines_native/core/Cargo.toml +18 -0
- data/ext/breaker_machines_native/core/examples/basic.rs +61 -0
- data/ext/breaker_machines_native/core/src/builder.rs +232 -0
- data/ext/breaker_machines_native/core/src/bulkhead.rs +223 -0
- data/ext/breaker_machines_native/core/src/callbacks.rs +58 -0
- data/ext/breaker_machines_native/core/src/circuit.rs +1156 -0
- data/ext/breaker_machines_native/core/src/classifier.rs +177 -0
- data/ext/breaker_machines_native/core/src/errors.rs +47 -0
- data/ext/breaker_machines_native/core/src/lib.rs +62 -0
- data/ext/breaker_machines_native/core/src/storage.rs +377 -0
- data/ext/breaker_machines_native/extconf.rb +40 -0
- data/ext/breaker_machines_native/ffi/Cargo.toml +16 -0
- data/ext/breaker_machines_native/ffi/src/lib.rs +218 -0
- data/ext/breaker_machines_native/target/debug/build/clang-sys-d961dfabd5f43fba/out/common.rs +355 -0
- data/ext/breaker_machines_native/target/debug/build/clang-sys-d961dfabd5f43fba/out/dynamic.rs +276 -0
- data/ext/breaker_machines_native/target/debug/build/clang-sys-d961dfabd5f43fba/out/macros.rs +49 -0
- data/ext/breaker_machines_native/target/debug/build/rb-sys-2bb7281aac8faec8/out/bindings-0.9.117-mri-arm64-darwin24-3.4.7.rs +8936 -0
- data/ext/breaker_machines_native/target/debug/build/rb-sys-54cb99ea6aeab8bc/out/bindings-0.9.117-mri-arm64-darwin24-3.4.7.rs +8936 -0
- data/ext/breaker_machines_native/target/debug/build/rb-sys-9e64a270c6421e93/out/bindings-0.9.117-mri-arm64-darwin24-3.4.7.rs +8936 -0
- data/ext/breaker_machines_native/target/debug/build/rb-sys-e627030114d3fc19/out/bindings-0.9.117-mri-arm64-darwin24-3.4.7.rs +8936 -0
- data/ext/breaker_machines_native/target/package/breaker-machines-0.1.0/Cargo.toml +48 -0
- data/ext/breaker_machines_native/target/package/breaker-machines-0.1.0/examples/basic.rs +61 -0
- data/ext/breaker_machines_native/target/package/breaker-machines-0.1.0/src/builder.rs +154 -0
- data/ext/breaker_machines_native/target/package/breaker-machines-0.1.0/src/callbacks.rs +55 -0
- data/ext/breaker_machines_native/target/package/breaker-machines-0.1.0/src/circuit.rs +607 -0
- data/ext/breaker_machines_native/target/package/breaker-machines-0.1.0/src/errors.rs +38 -0
- data/ext/breaker_machines_native/target/package/breaker-machines-0.1.0/src/lib.rs +58 -0
- data/ext/breaker_machines_native/target/package/breaker-machines-0.1.0/src/storage.rs +377 -0
- data/ext/breaker_machines_native/target/package/breaker-machines-0.2.0/Cargo.toml +48 -0
- data/ext/breaker_machines_native/target/package/breaker-machines-0.2.0/examples/basic.rs +61 -0
- data/ext/breaker_machines_native/target/package/breaker-machines-0.2.0/src/builder.rs +173 -0
- data/ext/breaker_machines_native/target/package/breaker-machines-0.2.0/src/callbacks.rs +55 -0
- data/ext/breaker_machines_native/target/package/breaker-machines-0.2.0/src/circuit.rs +855 -0
- data/ext/breaker_machines_native/target/package/breaker-machines-0.2.0/src/errors.rs +38 -0
- data/ext/breaker_machines_native/target/package/breaker-machines-0.2.0/src/lib.rs +58 -0
- data/ext/breaker_machines_native/target/package/breaker-machines-0.2.0/src/storage.rs +377 -0
- data/ext/breaker_machines_native/target/package/breaker-machines-0.5.0/Cargo.toml +48 -0
- data/ext/breaker_machines_native/target/package/breaker-machines-0.5.0/examples/basic.rs +61 -0
- data/ext/breaker_machines_native/target/package/breaker-machines-0.5.0/src/builder.rs +154 -0
- data/ext/breaker_machines_native/target/package/breaker-machines-0.5.0/src/callbacks.rs +55 -0
- data/ext/breaker_machines_native/target/package/breaker-machines-0.5.0/src/circuit.rs +607 -0
- data/ext/breaker_machines_native/target/package/breaker-machines-0.5.0/src/errors.rs +38 -0
- data/ext/breaker_machines_native/target/package/breaker-machines-0.5.0/src/lib.rs +58 -0
- data/ext/breaker_machines_native/target/package/breaker-machines-0.5.0/src/storage.rs +377 -0
- data/ext/breaker_machines_native/target/package/breaker-machines-0.6.0/Cargo.toml +48 -0
- data/ext/breaker_machines_native/target/package/breaker-machines-0.6.0/examples/basic.rs +61 -0
- data/ext/breaker_machines_native/target/package/breaker-machines-0.6.0/src/builder.rs +232 -0
- data/ext/breaker_machines_native/target/package/breaker-machines-0.6.0/src/bulkhead.rs +223 -0
- data/ext/breaker_machines_native/target/package/breaker-machines-0.6.0/src/callbacks.rs +58 -0
- data/ext/breaker_machines_native/target/package/breaker-machines-0.6.0/src/circuit.rs +1156 -0
- data/ext/breaker_machines_native/target/package/breaker-machines-0.6.0/src/classifier.rs +177 -0
- data/ext/breaker_machines_native/target/package/breaker-machines-0.6.0/src/errors.rs +47 -0
- data/ext/breaker_machines_native/target/package/breaker-machines-0.6.0/src/lib.rs +62 -0
- data/ext/breaker_machines_native/target/package/breaker-machines-0.6.0/src/storage.rs +377 -0
- data/ext/breaker_machines_native/target/release/build/clang-sys-ef8ad8b846ac8b75/out/common.rs +355 -0
- data/ext/breaker_machines_native/target/release/build/clang-sys-ef8ad8b846ac8b75/out/dynamic.rs +276 -0
- data/ext/breaker_machines_native/target/release/build/clang-sys-ef8ad8b846ac8b75/out/macros.rs +49 -0
- data/ext/breaker_machines_native/target/release/build/rb-sys-064bf9961dd17810/out/bindings-0.9.117-mri-arm64-darwin24-3.4.7.rs +8936 -0
- data/lib/breaker_machines/async_circuit.rb +47 -0
- data/lib/breaker_machines/async_support.rb +4 -3
- data/lib/breaker_machines/cascading_circuit.rb +5 -3
- data/lib/breaker_machines/circuit/async_state_management.rb +71 -0
- data/lib/breaker_machines/circuit/base.rb +59 -0
- data/lib/breaker_machines/circuit/callbacks.rb +7 -12
- data/lib/breaker_machines/circuit/configuration.rb +6 -26
- data/lib/breaker_machines/circuit/coordinated_state_management.rb +117 -0
- data/lib/breaker_machines/circuit/hedged_execution.rb +115 -0
- data/lib/breaker_machines/circuit/introspection.rb +1 -0
- data/lib/breaker_machines/circuit/native.rb +127 -0
- data/lib/breaker_machines/circuit/state_callbacks.rb +72 -0
- data/lib/breaker_machines/circuit/state_management.rb +14 -61
- data/lib/breaker_machines/circuit.rb +1 -7
- data/lib/breaker_machines/circuit_group.rb +153 -0
- data/lib/breaker_machines/coordinated_circuit.rb +10 -0
- data/lib/breaker_machines/dsl.rb +2 -2
- data/lib/breaker_machines/errors.rb +20 -0
- data/lib/breaker_machines/hedged_async_support.rb +29 -36
- data/lib/breaker_machines/native_extension.rb +36 -0
- data/lib/breaker_machines/native_speedup.rb +6 -0
- data/lib/breaker_machines/storage/bucket_memory.rb +4 -1
- data/lib/breaker_machines/storage/memory.rb +4 -1
- data/lib/breaker_machines/storage/native.rb +90 -0
- data/lib/breaker_machines/version.rb +1 -1
- data/lib/breaker_machines.rb +115 -11
- data/lib/breaker_machines_native/breaker_machines_native.bundle +0 -0
- data/sig/breaker_machines.rbs +20 -8
- metadata +107 -7
- data/lib/breaker_machines/hedged_execution.rb +0 -113
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
|
2
|
+
#
|
|
3
|
+
# When uploading crates to the registry Cargo will automatically
|
|
4
|
+
# "normalize" Cargo.toml files for maximal compatibility
|
|
5
|
+
# with all versions of Cargo and also rewrite `path` dependencies
|
|
6
|
+
# to registry (e.g., crates.io) dependencies.
|
|
7
|
+
#
|
|
8
|
+
# If you are reading this file be aware that the original Cargo.toml
|
|
9
|
+
# will likely look very different (and much more reasonable).
|
|
10
|
+
# See Cargo.toml.orig for the original contents.
|
|
11
|
+
|
|
12
|
+
[package]
|
|
13
|
+
edition = "2024"
|
|
14
|
+
name = "breaker-machines"
|
|
15
|
+
version = "0.1.0"
|
|
16
|
+
authors = ["Abdelkader Boudih <terminale@gmail.com>"]
|
|
17
|
+
build = false
|
|
18
|
+
autolib = false
|
|
19
|
+
autobins = false
|
|
20
|
+
autoexamples = false
|
|
21
|
+
autotests = false
|
|
22
|
+
autobenches = false
|
|
23
|
+
description = "High-performance circuit breaker storage backend"
|
|
24
|
+
readme = "README.md"
|
|
25
|
+
keywords = [
|
|
26
|
+
"circuit-breaker",
|
|
27
|
+
"reliability",
|
|
28
|
+
"fault-tolerance",
|
|
29
|
+
]
|
|
30
|
+
categories = ["concurrency"]
|
|
31
|
+
license = "MIT"
|
|
32
|
+
repository = "https://github.com/seuros/breaker_machines"
|
|
33
|
+
resolver = "2"
|
|
34
|
+
|
|
35
|
+
[lib]
|
|
36
|
+
name = "breaker_machines"
|
|
37
|
+
crate-type = ["lib"]
|
|
38
|
+
path = "src/lib.rs"
|
|
39
|
+
|
|
40
|
+
[[example]]
|
|
41
|
+
name = "basic"
|
|
42
|
+
path = "examples/basic.rs"
|
|
43
|
+
|
|
44
|
+
[dependencies.chrono-machines]
|
|
45
|
+
version = "0.2.1"
|
|
46
|
+
|
|
47
|
+
[dependencies.state-machines]
|
|
48
|
+
version = "0.6"
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
//! Basic circuit breaker usage example
|
|
2
|
+
|
|
3
|
+
use breaker_machines::CircuitBreaker;
|
|
4
|
+
|
|
5
|
+
fn main() {
|
|
6
|
+
println!("=== Circuit Breaker Basic Example ===\n");
|
|
7
|
+
|
|
8
|
+
// Create a circuit with builder API
|
|
9
|
+
let mut circuit = CircuitBreaker::builder("payment_api")
|
|
10
|
+
.failure_threshold(3)
|
|
11
|
+
.failure_window_secs(10.0)
|
|
12
|
+
.half_open_timeout_secs(5.0)
|
|
13
|
+
.success_threshold(2)
|
|
14
|
+
.on_open(|name| println!("🔴 Circuit '{}' opened!", name))
|
|
15
|
+
.on_close(|name| println!("🟢 Circuit '{}' closed!", name))
|
|
16
|
+
.on_half_open(|name| println!("🟡 Circuit '{}' half-open, testing...", name))
|
|
17
|
+
.build();
|
|
18
|
+
|
|
19
|
+
println!("Initial state: {}\n", circuit.state_name());
|
|
20
|
+
|
|
21
|
+
// Simulate successful calls
|
|
22
|
+
println!("--- Successful calls ---");
|
|
23
|
+
for i in 1..=2 {
|
|
24
|
+
match circuit.call(|| Ok::<_, String>(format!("Payment {}", i))) {
|
|
25
|
+
Ok(result) => println!("✓ {}", result),
|
|
26
|
+
Err(e) => println!("✗ Error: {}", e),
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
println!("State: {}\n", circuit.state_name());
|
|
30
|
+
|
|
31
|
+
// Simulate failures
|
|
32
|
+
println!("--- Triggering failures ---");
|
|
33
|
+
for i in 1..=3 {
|
|
34
|
+
match circuit.call(|| Err::<String, _>(format!("Payment failed {}", i))) {
|
|
35
|
+
Ok(_) => println!("✓ Success"),
|
|
36
|
+
Err(e) => println!("✗ {}", e),
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
println!("State: {} (circuit opened)\n", circuit.state_name());
|
|
40
|
+
|
|
41
|
+
// Try calling while open
|
|
42
|
+
println!("--- Attempting call while open ---");
|
|
43
|
+
match circuit.call(|| Ok::<_, String>("Should be rejected")) {
|
|
44
|
+
Ok(_) => println!("✓ Success"),
|
|
45
|
+
Err(e) => println!("✗ {}", e),
|
|
46
|
+
}
|
|
47
|
+
println!();
|
|
48
|
+
|
|
49
|
+
// Reset and demonstrate recovery
|
|
50
|
+
println!("--- Resetting circuit ---");
|
|
51
|
+
circuit.reset();
|
|
52
|
+
println!("State after reset: {}\n", circuit.state_name());
|
|
53
|
+
|
|
54
|
+
// Successful calls after reset
|
|
55
|
+
println!("--- Calls after reset ---");
|
|
56
|
+
match circuit.call(|| Ok::<_, String>("Payment successful")) {
|
|
57
|
+
Ok(result) => println!("✓ {}", result),
|
|
58
|
+
Err(e) => println!("✗ {}", e),
|
|
59
|
+
}
|
|
60
|
+
println!("State: {}", circuit.state_name());
|
|
61
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
//! Builder API for ergonomic circuit breaker configuration
|
|
2
|
+
|
|
3
|
+
use crate::{
|
|
4
|
+
callbacks::Callbacks,
|
|
5
|
+
circuit::{CircuitBreaker, CircuitContext, Config},
|
|
6
|
+
MemoryStorage, StorageBackend,
|
|
7
|
+
};
|
|
8
|
+
use std::sync::Arc;
|
|
9
|
+
|
|
10
|
+
/// Builder for creating circuit breakers with fluent API
|
|
11
|
+
pub struct CircuitBuilder {
|
|
12
|
+
name: String,
|
|
13
|
+
config: Config,
|
|
14
|
+
storage: Option<Arc<dyn StorageBackend>>,
|
|
15
|
+
callbacks: Callbacks,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
impl CircuitBuilder {
|
|
19
|
+
/// Create a new builder for a circuit with the given name
|
|
20
|
+
pub fn new(name: impl Into<String>) -> Self {
|
|
21
|
+
Self {
|
|
22
|
+
name: name.into(),
|
|
23
|
+
config: Config::default(),
|
|
24
|
+
storage: None,
|
|
25
|
+
callbacks: Callbacks::new(),
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/// Set the failure threshold (number of failures to open circuit)
|
|
30
|
+
pub fn failure_threshold(mut self, threshold: usize) -> Self {
|
|
31
|
+
self.config.failure_threshold = threshold;
|
|
32
|
+
self
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/// Set the failure window in seconds
|
|
36
|
+
pub fn failure_window_secs(mut self, seconds: f64) -> Self {
|
|
37
|
+
self.config.failure_window_secs = seconds;
|
|
38
|
+
self
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/// Set the half-open timeout in seconds
|
|
42
|
+
pub fn half_open_timeout_secs(mut self, seconds: f64) -> Self {
|
|
43
|
+
self.config.half_open_timeout_secs = seconds;
|
|
44
|
+
self
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/// Set the success threshold (successes needed to close from half-open)
|
|
48
|
+
pub fn success_threshold(mut self, threshold: usize) -> Self {
|
|
49
|
+
self.config.success_threshold = threshold;
|
|
50
|
+
self
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/// Set the jitter factor (0.0 = no jitter, 1.0 = full jitter)
|
|
54
|
+
/// Uses chrono-machines formula: timeout * (1 - jitter + rand * jitter)
|
|
55
|
+
pub fn jitter_factor(mut self, factor: f64) -> Self {
|
|
56
|
+
self.config.jitter_factor = factor;
|
|
57
|
+
self
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/// Set custom storage backend
|
|
61
|
+
pub fn storage(mut self, storage: Arc<dyn StorageBackend>) -> Self {
|
|
62
|
+
self.storage = Some(storage);
|
|
63
|
+
self
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/// Set callback for when circuit opens
|
|
67
|
+
pub fn on_open<F>(mut self, f: F) -> Self
|
|
68
|
+
where
|
|
69
|
+
F: Fn(&str) + Send + Sync + 'static,
|
|
70
|
+
{
|
|
71
|
+
self.callbacks.on_open = Some(Arc::new(f));
|
|
72
|
+
self
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/// Set callback for when circuit closes
|
|
76
|
+
pub fn on_close<F>(mut self, f: F) -> Self
|
|
77
|
+
where
|
|
78
|
+
F: Fn(&str) + Send + Sync + 'static,
|
|
79
|
+
{
|
|
80
|
+
self.callbacks.on_close = Some(Arc::new(f));
|
|
81
|
+
self
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/// Set callback for when circuit enters half-open
|
|
85
|
+
pub fn on_half_open<F>(mut self, f: F) -> Self
|
|
86
|
+
where
|
|
87
|
+
F: Fn(&str) + Send + Sync + 'static,
|
|
88
|
+
{
|
|
89
|
+
self.callbacks.on_half_open = Some(Arc::new(f));
|
|
90
|
+
self
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/// Build the circuit breaker
|
|
94
|
+
pub fn build(self) -> CircuitBreaker {
|
|
95
|
+
let storage = self
|
|
96
|
+
.storage
|
|
97
|
+
.unwrap_or_else(|| Arc::new(MemoryStorage::new()));
|
|
98
|
+
|
|
99
|
+
let context = CircuitContext {
|
|
100
|
+
name: self.name,
|
|
101
|
+
config: self.config,
|
|
102
|
+
storage,
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
CircuitBreaker::with_context_and_callbacks(context, self.callbacks)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
#[cfg(test)]
|
|
110
|
+
mod tests {
|
|
111
|
+
use super::*;
|
|
112
|
+
|
|
113
|
+
#[test]
|
|
114
|
+
fn test_builder_defaults() {
|
|
115
|
+
let circuit = CircuitBuilder::new("test").build();
|
|
116
|
+
|
|
117
|
+
assert_eq!(circuit.state_name(), "Closed");
|
|
118
|
+
assert!(circuit.is_closed());
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
#[test]
|
|
122
|
+
fn test_builder_custom_config() {
|
|
123
|
+
let circuit = CircuitBuilder::new("test")
|
|
124
|
+
.failure_threshold(10)
|
|
125
|
+
.failure_window_secs(120.0)
|
|
126
|
+
.half_open_timeout_secs(60.0)
|
|
127
|
+
.success_threshold(3)
|
|
128
|
+
.build();
|
|
129
|
+
|
|
130
|
+
assert!(circuit.is_closed());
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
#[test]
|
|
134
|
+
fn test_builder_with_callbacks() {
|
|
135
|
+
use std::sync::atomic::{AtomicBool, Ordering};
|
|
136
|
+
|
|
137
|
+
let opened = Arc::new(AtomicBool::new(false));
|
|
138
|
+
let opened_clone = opened.clone();
|
|
139
|
+
|
|
140
|
+
let mut circuit = CircuitBuilder::new("test")
|
|
141
|
+
.failure_threshold(2)
|
|
142
|
+
.on_open(move |_name| {
|
|
143
|
+
opened_clone.store(true, Ordering::SeqCst);
|
|
144
|
+
})
|
|
145
|
+
.build();
|
|
146
|
+
|
|
147
|
+
// Trigger failures to open circuit
|
|
148
|
+
let _ = circuit.call(|| Err::<(), _>("error 1"));
|
|
149
|
+
let _ = circuit.call(|| Err::<(), _>("error 2"));
|
|
150
|
+
|
|
151
|
+
// Callback should have been triggered
|
|
152
|
+
assert!(opened.load(Ordering::SeqCst));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
//! Callback system for circuit breaker state transitions
|
|
2
|
+
|
|
3
|
+
use std::sync::Arc;
|
|
4
|
+
|
|
5
|
+
/// Callbacks for circuit breaker events
|
|
6
|
+
#[derive(Clone)]
|
|
7
|
+
pub struct Callbacks {
|
|
8
|
+
pub on_open: Option<Arc<dyn Fn(&str) + Send + Sync>>,
|
|
9
|
+
pub on_close: Option<Arc<dyn Fn(&str) + Send + Sync>>,
|
|
10
|
+
pub on_half_open: Option<Arc<dyn Fn(&str) + Send + Sync>>,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
impl Callbacks {
|
|
14
|
+
pub fn new() -> Self {
|
|
15
|
+
Self {
|
|
16
|
+
on_open: None,
|
|
17
|
+
on_close: None,
|
|
18
|
+
on_half_open: None,
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
pub fn trigger_open(&self, circuit: &str) {
|
|
23
|
+
if let Some(ref callback) = self.on_open {
|
|
24
|
+
callback(circuit);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
pub fn trigger_close(&self, circuit: &str) {
|
|
29
|
+
if let Some(ref callback) = self.on_close {
|
|
30
|
+
callback(circuit);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
pub fn trigger_half_open(&self, circuit: &str) {
|
|
35
|
+
if let Some(ref callback) = self.on_half_open {
|
|
36
|
+
callback(circuit);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
impl Default for Callbacks {
|
|
42
|
+
fn default() -> Self {
|
|
43
|
+
Self::new()
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
impl std::fmt::Debug for Callbacks {
|
|
48
|
+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
49
|
+
f.debug_struct("Callbacks")
|
|
50
|
+
.field("on_open", &self.on_open.is_some())
|
|
51
|
+
.field("on_close", &self.on_close.is_some())
|
|
52
|
+
.field("on_half_open", &self.on_half_open.is_some())
|
|
53
|
+
.finish()
|
|
54
|
+
}
|
|
55
|
+
}
|