mod code;
mod connect_error;
mod detail;
mod wire;
pub use code::Code;
pub use connect_error::ConnectError;
pub use detail::{ErrorDetail, ERRSTACKS_TYPE_URL};
pub use wire::{EndStreamResponse, WireError, WireErrorDetail};
use std::error::Error as StdError;
use foundation_errstacks::ErrorTrace;
pub type ConnectResult<T> = Result<T, ErrorTrace<ConnectError>>;
#[must_use]
pub fn code_of(err: &(dyn StdError + 'static)) -> Code {
let mut current: Option<&(dyn StdError + 'static)> = Some(err);
while let Some(e) = current {
if let Some(ce) = e.downcast_ref::<ConnectError>() {
return ce.code();
}
current = e.source();
}
Code::Unknown
}
fn chain_text(err: &(dyn StdError + 'static)) -> String {
let mut text = String::new();
let mut current: Option<&(dyn StdError + 'static)> = Some(err);
while let Some(e) = current {
if !text.is_empty() {
text.push_str(": ");
}
text.push_str(&e.to_string());
current = e.source();
}
text.to_lowercase()
}
#[must_use]
pub fn wrap_if_context(err: &(dyn StdError + 'static)) -> Option<Code> {
let text = chain_text(err);
if text.contains("deadline") || text.contains("timed out") || text.contains("timeout") {
Some(Code::DeadlineExceeded)
} else if text.contains("cancel") {
Some(Code::Canceled)
} else {
None
}
}
#[must_use]
pub fn wrap_if_rst(err: &(dyn StdError + 'static)) -> Option<Code> {
let text = chain_text(err);
if text.contains("rst_stream") || text.contains("stream reset") || text.contains("stream closed")
{
Some(Code::Canceled)
} else {
None
}
}
#[must_use]
pub fn wrap_if_h2c(err: &(dyn StdError + 'static)) -> Option<Code> {
let text = chain_text(err);
let looks_h2 = text.contains("http2") || text.contains("h2c");
if looks_h2
&& (text.contains("unexpected")
|| text.contains("malformed")
|| text.contains("goaway")
|| text.contains("not configured"))
{
Some(Code::Unavailable)
} else {
None
}
}
#[must_use]
pub fn wrap_if_uncoded<E>(err: E) -> ConnectError
where
E: StdError + Send + Sync + 'static,
{
let code = code_of(&err);
ConnectError::with_source(code, err)
}