use std::{error::Error as StdError, fmt};
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug)]
pub enum Error {
InvalidApiKey,
InvalidInput(String),
Transport(String),
Provider {
status: u16,
code: Option<String>,
message: String,
request_id: Option<String>,
},
Protocol(String),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidApiKey => f.write_str("the OpenAI API key is empty or invalid"),
Self::InvalidInput(message) => write!(f, "invalid OpenAI request: {message}"),
Self::Transport(message) => write!(f, "OpenAI transport failed: {message}"),
Self::Provider {
status,
code,
message,
request_id,
} => {
write!(f, "OpenAI returned HTTP {status}")?;
if let Some(code) = code {
write!(f, " ({code})")?;
}
write!(f, ": {message}")?;
if let Some(request_id) = request_id {
write!(f, " [request_id: {request_id}]")?;
}
Ok(())
}
Self::Protocol(message) => write!(f, "invalid OpenAI response: {message}"),
}
}
}
impl StdError for Error {}
pub(crate) fn transport(error: impl fmt::Display) -> Error {
let message = error.to_string();
Error::Transport(if message.to_ascii_lowercase().contains("timed out") {
"the request timed out".into()
} else {
"the service could not be reached".into()
})
}
pub(crate) fn clean_message(value: &str, limit: usize) -> String {
let value = value
.chars()
.map(|character| {
if character.is_control() {
' '
} else {
character
}
})
.take(limit)
.collect::<String>()
.split_whitespace()
.collect::<Vec<_>>()
.join(" ");
if value.is_empty() {
"unknown error".into()
} else {
value
}
}