use tokio_postgres::error::SqlState;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error(transparent)]
Db(#[from] pylon_pgcon::Error),
#[error(transparent)]
Compile(#[from] pylon_core::error::PyQLError),
#[error(transparent)]
Decode(#[from] crate::queryable::DecodeError),
#[error("missing query parameter: {0}")]
MissingParam(String),
#[error("{0}")]
InvalidArgument(String),
#[error("expected at most one result, got {got}")]
ResultCardinality { got: usize },
#[error("expected exactly one result, got none")]
NoData,
#[error(
"no schema snapshot found in the database — run `pylon migration create` and `pylon migration apply` first"
)]
NoSchemaSnapshot,
#[error("failed to parse schema JSON: {0}")]
SchemaJson(#[from] serde_json::Error),
#[error("failed to build analyze tree: {0}")]
Analyze(String),
#[error("cache error: {0}")]
Cache(String),
#[error("'{0}' is not a known Channel")]
UnknownChannel(String),
#[error("payload doesn't match its declared Channel shape: {0}")]
MalformedPayload(String),
#[error("transaction rolled back at the request of its body")]
Rollback,
}
impl Error {
pub fn sqlstate(&self) -> Option<&SqlState> {
match self {
Error::Db(e) => e.sqlstate(),
_ => None,
}
}
pub fn is_serialization_error(&self) -> bool {
self.sqlstate() == Some(&SqlState::T_R_SERIALIZATION_FAILURE)
}
pub fn is_deadlock(&self) -> bool {
self.sqlstate() == Some(&SqlState::T_R_DEADLOCK_DETECTED)
}
pub fn is_retriable(&self) -> bool {
self.is_serialization_error() || self.is_deadlock()
}
pub fn is_rollback(&self) -> bool {
matches!(self, Error::Rollback)
}
}