fix: replace unwrap/expect in production paths with fallible alternatives

This commit is contained in:
2026-03-04 13:29:33 +01:00
parent cab03bd3f7
commit a8ed3c4356
5 changed files with 35 additions and 5 deletions

View File

@@ -67,18 +67,33 @@ impl ClientContext {
} }
/// Set (or replace) the auth credentials. /// Set (or replace) the auth credentials.
///
/// # Panics
/// Panics if the RwLock is poisoned (a thread panicked while holding it).
/// A poisoned lock indicates unrecoverable state corruption.
#[allow(clippy::expect_used)]
pub fn set_auth(&self, ctx: ClientAuth) { pub fn set_auth(&self, ctx: ClientAuth) {
let mut guard = self.auth.write().expect("ClientContext auth lock poisoned"); let mut guard = self.auth.write().expect("ClientContext auth lock poisoned");
*guard = Some(ctx); *guard = Some(ctx);
} }
/// Read the current auth snapshot (cloned). /// Read the current auth snapshot (cloned).
///
/// # Panics
/// Panics if the RwLock is poisoned (a thread panicked while holding it).
/// A poisoned lock indicates unrecoverable state corruption.
#[allow(clippy::expect_used)]
pub fn get_auth(&self) -> Option<ClientAuth> { pub fn get_auth(&self) -> Option<ClientAuth> {
let guard = self.auth.read().expect("ClientContext auth lock poisoned"); let guard = self.auth.read().expect("ClientContext auth lock poisoned");
guard.clone() guard.clone()
} }
/// Returns true if auth credentials have been set. /// Returns true if auth credentials have been set.
///
/// # Panics
/// Panics if the RwLock is poisoned (a thread panicked while holding it).
/// A poisoned lock indicates unrecoverable state corruption.
#[allow(clippy::expect_used)]
pub fn is_authenticated(&self) -> bool { pub fn is_authenticated(&self) -> bool {
let guard = self.auth.read().expect("ClientContext auth lock poisoned"); let guard = self.auth.read().expect("ClientContext auth lock poisoned");
guard.is_some() guard.is_some()
@@ -151,6 +166,11 @@ impl ClientAuth {
} }
/// Set (or replace) the global auth context. /// Set (or replace) the global auth context.
///
/// # Panics
/// Panics if the RwLock is poisoned (a thread panicked while holding it).
/// A poisoned lock indicates unrecoverable state corruption.
#[allow(clippy::expect_used)]
pub fn init_auth(ctx: ClientAuth) { pub fn init_auth(ctx: ClientAuth) {
let mut guard = AUTH_CONTEXT.write().expect("AUTH_CONTEXT poisoned"); let mut guard = AUTH_CONTEXT.write().expect("AUTH_CONTEXT poisoned");
*guard = Some(ctx); *guard = Some(ctx);

View File

@@ -336,7 +336,10 @@ pub fn main() {
let rt = tokio::runtime::Builder::new_multi_thread() let rt = tokio::runtime::Builder::new_multi_thread()
.enable_all() .enable_all()
.build() .build()
.expect("failed to create tokio runtime"); .unwrap_or_else(|e| {
eprintln!("fatal: {e}");
std::process::exit(1);
});
if let Err(e) = rt.block_on(run(args)) { if let Err(e) = rt.block_on(run(args)) {
eprintln!("error: {e:#}"); eprintln!("error: {e:#}");

View File

@@ -40,7 +40,11 @@ impl RpcClient {
let quic_tls = quinn::crypto::rustls::QuicClientConfig::try_from(tls) let quic_tls = quinn::crypto::rustls::QuicClientConfig::try_from(tls)
.map_err(|e| RpcError::Connection(format!("TLS config: {e}")))?; .map_err(|e| RpcError::Connection(format!("TLS config: {e}")))?;
let mut endpoint = Endpoint::client("0.0.0.0:0".parse().expect("valid addr")) let bind_addr = std::net::SocketAddr::new(
std::net::IpAddr::V4(std::net::Ipv4Addr::UNSPECIFIED),
0,
);
let mut endpoint = Endpoint::client(bind_addr)
.map_err(|e| RpcError::Connection(e.to_string()))?; .map_err(|e| RpcError::Connection(e.to_string()))?;
endpoint.set_default_client_config(quinn::ClientConfig::new(Arc::new(quic_tls))); endpoint.set_default_client_config(quinn::ClientConfig::new(Arc::new(quic_tls)));

View File

@@ -32,7 +32,10 @@ pub struct ClientConfig {
impl Default for ClientConfig { impl Default for ClientConfig {
fn default() -> Self { fn default() -> Self {
Self { Self {
server_addr: "127.0.0.1:7000".parse().expect("valid addr"), server_addr: std::net::SocketAddr::new(
std::net::IpAddr::V4(std::net::Ipv4Addr::new(127, 0, 0, 1)),
7000,
),
server_name: "localhost".to_string(), server_name: "localhost".to_string(),
db_path: PathBuf::from("conversations.db"), db_path: PathBuf::from("conversations.db"),
db_password: None, db_password: None,

View File

@@ -203,7 +203,7 @@ async fn main() -> anyhow::Result<()> {
transport.max_idle_timeout(Some( transport.max_idle_timeout(Some(
std::time::Duration::from_secs(300) std::time::Duration::from_secs(300)
.try_into() .try_into()
.expect("300s is a valid IdleTimeout"), .map_err(|e| anyhow::anyhow!("idle timeout: {e}"))?,
)); ));
transport.max_concurrent_bidi_streams(1u32.into()); transport.max_concurrent_bidi_streams(1u32.into());
transport.max_concurrent_uni_streams(0u32.into()); transport.max_concurrent_uni_streams(0u32.into());
@@ -390,7 +390,7 @@ async fn main() -> anyhow::Result<()> {
transport.max_idle_timeout(Some( transport.max_idle_timeout(Some(
std::time::Duration::from_secs(120) std::time::Duration::from_secs(120)
.try_into() .try_into()
.expect("120s is valid"), .map_err(|e| anyhow::anyhow!("idle timeout: {e}"))?,
)); ));
qc.transport_config(Arc::new(transport)); qc.transport_config(Arc::new(transport));
Some(qc) Some(qc)