Ensuring Valid API Request States with Type-Safe Enums
Owner: SnippetBot
Created: 2026-11-01 00:01:22
Size: 3.46 KB
Expires: Never
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#![allow(dead_code)] // Allow dead code for demo purposes
use md5;
use rand::{self, Rng};
// Add `rand = "0.8"` and `md5 = "0.7"` to your Cargo.toml
#[derive(Debug, Clone)] // Derive Clone to simplify state updates in the example
enum WebhookProcessingState {
AwaitingPayload { webhook_id: String },
PayloadReceived { webhook_id: String, payload_hash: String },
Processing { webhook_id: String, handler_id: String, start_time_ms: u64 },
Completed { webhook_id: String, status_code: u16, end_time_ms: u64 },
Failed { webhook_id: String, error_reason: String, failed_time_ms: u64 },
}
impl WebhookProcessingState {
/// Attempts to transition the webhook state safely.
/// Returns a new state or an error if the transition is invalid.
/// This prevents transitioning to an illogical state or accessing irrelevant data.
fn try_advance(self) -> Result<Self, String> {
match self {
WebhookProcessingState::AwaitingPayload { webhook_id } => {
println!("Webhook {} received payload.", webhook_id);
Ok(WebhookProcessingState::PayloadReceived {
webhook_id: webhook_id,
payload_hash: format!("{:x}", md5::compute(format!("{}-payload", webhook_id))), // Simulate payload hashing
})
}
WebhookProcessingState::PayloadReceived { webhook_id, payload_hash } => {
println!("Webhook {} (hash {}) starting processing.", webhook_id, payload_hash);
Ok(WebhookProcessingState::Processing {
webhook_id: webhook_id,
handler_id: "worker-node-1".to_string(),
start_time_ms: 1_678_886_400_000, // Example timestamp
})
}
WebhookProcessingState::Processing { webhook_id, handler_id, start_time_ms } => {
println!("Webhook {} processed by {} started at {}.", webhook_id, handler_id, start_time_ms);
let mut rng = rand::thread_rng();
if rng.gen_bool(0.7) { // 70% chance of success
Ok(WebhookProcessingState::Completed {
webhook_id: webhook_id,
status_code: 200,
end_time_ms: 1_678_886_401_000, // Example timestamp
})
} else {
Ok(WebhookProcessingState::Failed {
webhook_id: webhook_id,
error_reason: "Simulated processing error".to_string(),
failed_time_ms: 1_678_886_401_000, // Example timestamp
})
}
}
// All other states (Completed, Failed) are terminal and cannot be advanced from.
_ => Err(format!("Cannot advance from terminal state: {:?}", self)),
}
}
}
fn main() {
let mut state = WebhookProcessingState::AwaitingPayload { webhook_id: "wh-001".to_string() };
println!("Initial: {:?}", state);
state = state.try_advance().expect("Failed to advance state");
println!("-> {:?}", state);
state = state.try_advance().expect("Failed to advance state");
println!("-> {:?}", state);
state = state.try_advance().expect("Failed to advance state");
println!("-> {:?}", state);
// This will likely result in an error as it's a terminal state
match state.try_advance() {
Ok(s) => println!("-> {:?}", s),
Err(e) => println!("Error advancing: {}", e),
}
}