Skip to content

Commit c090d0f

Browse files
author
g
committed
feat: enhance error handling with structured error types
- Add specific error variants for connection timeouts and retry failures - Replace generic ObsOperationError with contextual error messages - Eliminate unsafe unwrap() calls in URL parsing - Improve WebSocket URL parsing with detailed error context - Add ConnectionTimeout, AllConnectionAttemptsFailed, and WebSocketUrlParseError variants
1 parent be9e2fd commit c090d0f

File tree

4 files changed

+41
-10
lines changed

4 files changed

+41
-10
lines changed

src/cli.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ impl FromStr for ObsWebsocket {
2222
);
2323
}
2424

25-
let hostname = unvalidated_websocket.host().unwrap().to_string();
25+
let hostname = unvalidated_websocket
26+
.host()
27+
.ok_or("Invalid hostname in URL")?
28+
.to_string();
2629

2730
let port =
2831
match unvalidated_websocket.port() {

src/connection.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ pub async fn connect_with_retry(
4545
last_error = Some(ObsCmdError::ConnectionError(e));
4646
}
4747
Err(_) => {
48-
last_error = Some(ObsCmdError::ObsOperationError(
49-
"Connection timeout".to_string(),
50-
));
48+
last_error = Some(ObsCmdError::ConnectionTimeout {
49+
timeout: config.timeout_duration.as_secs(),
50+
});
5151
}
5252
}
5353

@@ -62,14 +62,16 @@ pub async fn connect_with_retry(
6262
}
6363

6464
Err(last_error.unwrap_or_else(|| {
65-
ObsCmdError::ObsOperationError("All connection attempts failed".to_string())
65+
ObsCmdError::AllConnectionAttemptsFailed {
66+
attempts: config.max_retries,
67+
}
6668
}))
6769
}
6870

6971
pub async fn check_connection_health(client: &Client) -> Result<()> {
7072
timeout(Duration::from_secs(5), client.general().version())
7173
.await
72-
.map_err(|_| ObsCmdError::ObsOperationError("Connection health check timeout".to_string()))?
74+
.map_err(|_| ObsCmdError::ConnectionTimeout { timeout: 5 })?
7375
.map_err(ObsCmdError::ConnectionError)?;
7476

7577
Ok(())

src/error.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,22 @@ pub enum ObsCmdError {
4040
#[error("No last replay found")]
4141
NoLastReplay,
4242

43+
#[allow(dead_code)]
4344
#[error("OBS operation failed: {0}")]
4445
ObsOperationError(String),
46+
47+
#[allow(dead_code)]
48+
#[error("Invalid URL format: {0}")]
49+
InvalidUrlFormat(String),
50+
51+
#[error("Connection timeout after {timeout} seconds")]
52+
ConnectionTimeout { timeout: u64 },
53+
54+
#[error("All {attempts} connection attempts failed")]
55+
AllConnectionAttemptsFailed { attempts: u32 },
56+
57+
#[error("WebSocket URL parsing failed: {0}")]
58+
WebSocketUrlParseError(String),
4559
}
4660

4761
pub type Result<T> = std::result::Result<T, ObsCmdError>;

src/main.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ mod handler;
66
use clap::Parser;
77
use cli::{Cli, ObsWebsocket};
88
use connection::{connect_with_retry, ConnectionConfig};
9-
use error::Result;
9+
use error::{ObsCmdError, Result};
1010
use handler::handle_commands;
1111

1212
#[tokio::main]
@@ -21,15 +21,27 @@ async fn main() -> Result<()> {
2121
let parsed_url = url::Url::parse(&url)?;
2222
let hostname = parsed_url
2323
.host_str()
24-
.ok_or(url::ParseError::RelativeUrlWithoutBase)?
24+
.ok_or_else(|| {
25+
ObsCmdError::WebSocketUrlParseError(
26+
"Missing hostname in WebSocket URL".to_string(),
27+
)
28+
})?
2529
.to_string();
2630
let port = parsed_url
2731
.port()
28-
.ok_or(url::ParseError::RelativeUrlWithoutBase)?;
32+
.ok_or_else(|| {
33+
ObsCmdError::WebSocketUrlParseError(
34+
"Missing port in WebSocket URL".to_string(),
35+
)
36+
})?;
2937
let password = parsed_url
3038
.path_segments()
3139
.and_then(|mut segments| segments.next())
32-
.ok_or(url::ParseError::RelativeUrlWithoutBase)?;
40+
.ok_or_else(|| {
41+
ObsCmdError::WebSocketUrlParseError(
42+
"Missing password in WebSocket URL path".to_string(),
43+
)
44+
})?;
3345

3446
connect_with_retry(hostname, port, Some(password.to_string()), config).await?
3547
}

0 commit comments

Comments
 (0)