use openigtlink_rust::error::{IgtlError, Result};
use openigtlink_rust::io::ClientBuilder;
use openigtlink_rust::protocol::message::IgtlMessage;
use openigtlink_rust::protocol::types::{StatusMessage, TransformMessage};
use std::env;
use std::io::ErrorKind;
use std::thread;
use std::time::Duration;
fn main() {
if let Err(e) = run() {
eprintln!("[FATAL] Unrecoverable error: {}", e);
std::process::exit(1);
}
}
fn run() -> Result<()> {
let scenario = env::args().nth(1).unwrap_or_else(|| "all".to_string());
println!("=== OpenIGTLink Error Handling Patterns ===\n");
match scenario.as_str() {
"reconnect" => test_reconnect_logic()?,
"timeout" => test_timeout_handling()?,
"crc" => test_crc_error_recovery()?,
"wrong_type" => test_wrong_message_type()?,
"all" => {
println!("[INFO] Running all error handling scenarios\n");
test_reconnect_logic()?;
println!();
test_timeout_handling()?;
println!();
test_crc_error_recovery()?;
println!();
test_wrong_message_type()?;
}
_ => {
println!("Usage: cargo run --example error_handling [SCENARIO]");
println!("\nScenarios:");
println!(" reconnect - Automatic reconnection with exponential backoff");
println!(" timeout - Timeout handling and recovery");
println!(" crc - CRC error detection and retransmission");
println!(" wrong_type - Wrong message type handling");
println!(" all - Run all scenarios (default)");
}
}
Ok(())
}
fn test_reconnect_logic() -> Result<()> {
println!("[SCENARIO 1] Automatic Reconnection with Exponential Backoff");
println!("────────────────────────────────────────────────────────────\n");
const MAX_RETRIES: usize = 5;
let mut retry_count = 0;
let mut delay = Duration::from_secs(1);
println!("[INFO] Attempting to connect to server at 127.0.0.1:18944");
println!(" Max retries: {}", MAX_RETRIES);
println!(" Initial delay: {:?}\n", delay);
loop {
println!(" Attempt {}/{}", retry_count + 1, MAX_RETRIES);
match ClientBuilder::new().tcp("127.0.0.1:18944").sync().build() {
Ok(mut client) => {
println!(" ✓ Connection established!");
println!(" → Sending test message...");
let transform = TransformMessage::identity();
let msg = IgtlMessage::new(transform, "RobustClient")?;
client.send(&msg)?;
println!(" ✓ Message sent successfully");
println!("\n[SUCCESS] Reconnection logic validated");
break;
}
Err(e) => {
retry_count += 1;
println!(" ✗ Connection failed: {}", e);
if retry_count >= MAX_RETRIES {
println!("\n[FAIL] Max retries exceeded");
return Err(e);
}
println!(" → Retrying in {:?}...", delay);
thread::sleep(delay);
delay = delay.saturating_mul(2).min(Duration::from_secs(16));
}
}
}
Ok(())
}
fn test_timeout_handling() -> Result<()> {
println!("[SCENARIO 2] Timeout Handling");
println!("────────────────────────────────────────────────────────────\n");
println!("[INFO] Connecting to server...");
match ClientBuilder::new().tcp("127.0.0.1:18944").sync().build() {
Ok(mut client) => {
println!(" ✓ Connected");
let timeout = Duration::from_secs(2);
client.set_read_timeout(Some(timeout))?;
println!(" → Read timeout set to {:?}", timeout);
println!(" → Attempting to receive message...");
match client.receive::<StatusMessage>() {
Ok(msg) => {
println!(" ✓ Received message: {}", msg.content.status_string);
}
Err(IgtlError::Io(e)) if e.kind() == ErrorKind::WouldBlock => {
println!(" ⏱ Timeout occurred (expected behavior)");
println!(" → Recommended action: Check server status or retry");
}
Err(IgtlError::Io(e)) if e.kind() == ErrorKind::TimedOut => {
println!(" ⏱ Timeout occurred (expected behavior)");
println!(" → Recommended action: Check server status or retry");
}
Err(e) => {
println!(" ✗ Unexpected error: {}", e);
return Err(e);
}
}
println!("\n[SUCCESS] Timeout handling validated");
}
Err(e) => {
println!(" ✗ Connection failed: {}", e);
println!(" → Skipping timeout test (server not available)");
}
}
Ok(())
}
fn test_crc_error_recovery() -> Result<()> {
println!("[SCENARIO 3] CRC Error Detection and Recovery");
println!("────────────────────────────────────────────────────────────\n");
println!("[INFO] CRC Error Handling Strategy");
println!();
println!(" What is CRC?");
println!(" CRC (Cyclic Redundancy Check) detects data corruption");
println!(" during network transmission.");
println!();
println!(" When CRC errors occur:");
println!(" 1. Data has been corrupted (network issues, hardware faults)");
println!(" 2. Message should NOT be processed (data integrity compromised)");
println!(" 3. Application should request retransmission");
println!();
println!(" Error Handling Pattern:");
println!(" ```rust");
println!(" match client.receive::<TransformMessage>() {{");
println!(" Ok(msg) => process_message(msg),");
println!(" Err(IgtlError::CrcMismatch {{ expected, actual }}) => {{");
println!(
" eprintln!(\"CRC error: expected {{:X}}, got {{:X}}\", expected, actual);"
);
println!(" // Request retransmission or skip message");
println!(" }}");
println!(" Err(e) => handle_other_errors(e),");
println!(" }}");
println!(" ```");
println!();
println!(" Recovery Actions:");
println!(" • Request retransmission from sender");
println!(" • Log error for monitoring");
println!(" • Increment error counter");
println!(" • Alert if error rate exceeds threshold");
println!();
println!("[SUCCESS] CRC error handling documented");
Ok(())
}
fn test_wrong_message_type() -> Result<()> {
println!("[SCENARIO 4] Wrong Message Type Handling");
println!("────────────────────────────────────────────────────────────\n");
println!("[INFO] Demonstrating graceful degradation for type mismatches");
println!();
match ClientBuilder::new().tcp("127.0.0.1:18944").sync().build() {
Ok(mut client) => {
println!(" ✓ Connected to server");
println!(" → Sending TRANSFORM message...");
let transform = TransformMessage::identity();
let msg = IgtlMessage::new(transform, "TestClient")?;
client.send(&msg)?;
println!(" ✓ TRANSFORM sent");
println!(" → Expecting STATUS response...");
client.set_read_timeout(Some(Duration::from_secs(2)))?;
match client.receive::<StatusMessage>() {
Ok(msg) => {
println!(" ✓ Received STATUS: {}", msg.content.status_string);
}
Err(IgtlError::UnknownMessageType(type_name)) => {
println!(" ⚠ Received unexpected type: {}", type_name);
println!(" → Action: Discarding message (graceful degradation)");
println!(" → Alternative: Use generic message receiver");
}
Err(IgtlError::Io(e))
if e.kind() == ErrorKind::WouldBlock || e.kind() == ErrorKind::TimedOut =>
{
println!(" ⏱ No response (timeout)");
println!(" → Server may not be sending responses");
}
Err(e) => {
println!(" ✗ Error: {}", e);
}
}
println!();
println!(" Best Practices:");
println!(" • Use protocol negotiation to agree on message types");
println!(" • Implement fallback handlers for unknown types");
println!(" • Log unexpected types for debugging");
println!(" • Consider using a generic message receiver");
println!("\n[SUCCESS] Type mismatch handling demonstrated");
}
Err(e) => {
println!(" ✗ Connection failed: {}", e);
println!(" → Skipping type mismatch test (server not available)");
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_exponential_backoff() {
let mut delay = Duration::from_secs(1);
assert_eq!(delay, Duration::from_secs(1));
delay = delay.saturating_mul(2);
assert_eq!(delay, Duration::from_secs(2));
delay = delay.saturating_mul(2);
assert_eq!(delay, Duration::from_secs(4));
delay = delay.saturating_mul(2);
assert_eq!(delay, Duration::from_secs(8));
delay = delay.saturating_mul(2).min(Duration::from_secs(16));
assert_eq!(delay, Duration::from_secs(16));
delay = delay.saturating_mul(2).min(Duration::from_secs(16));
assert_eq!(delay, Duration::from_secs(16));
}
#[test]
fn test_max_retries() {
const MAX_RETRIES: usize = 5;
let mut retry_count = 0;
for _ in 0..MAX_RETRIES {
retry_count += 1;
assert!(retry_count <= MAX_RETRIES);
}
assert_eq!(retry_count, MAX_RETRIES);
}
#[test]
fn test_timeout_values() {
let short_timeout = Duration::from_secs(2);
let long_timeout = Duration::from_secs(30);
assert!(short_timeout < long_timeout);
assert_eq!(short_timeout.as_secs(), 2);
assert_eq!(long_timeout.as_secs(), 30);
}
#[test]
fn test_error_kind_matching() {
let timeout_error = std::io::Error::new(ErrorKind::TimedOut, "timeout");
assert_eq!(timeout_error.kind(), ErrorKind::TimedOut);
let would_block_error = std::io::Error::new(ErrorKind::WouldBlock, "would block");
assert_eq!(would_block_error.kind(), ErrorKind::WouldBlock);
}
}