use std::fmt::{self, Display, Formatter};
use thiserror::Error;
use crate::role::Role;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum NameLimit {
Bytes(u16),
Characters(u16),
}
impl Display for NameLimit {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::Bytes(limit) => write!(f, "{limit} bytes"),
Self::Characters(limit) => write!(f, "{limit} characters"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Error)]
#[non_exhaustive]
pub enum StatementError {
#[error(
"the {statement} statement needs a column playing `{role}`: add `#[field({role})]` to \
the struct"
)]
MissingRole {
statement: &'static str,
role: Role,
},
#[error("the {dialect} dialect has no statements for the {form} form")]
UnsupportedForm {
dialect: &'static str,
form: &'static str,
},
#[error("the {statement} statement does not serve a table in the {form} form")]
FormMismatch {
statement: &'static str,
form: &'static str,
},
#[error("the {dialect} dialect has no claim for FIFO groups")]
UnsupportedFifo {
dialect: &'static str,
},
#[error(
"FIFO groups do not combine with the advisory lock form in the {dialect} dialect: drop \
`fifo` and put the group's column into the lock key"
)]
AdvisoryFifo {
dialect: &'static str,
},
#[error("`{identifier}` is longer than the {limit} the {dialect} dialect allows in a name")]
IdentifierTooLong {
dialect: &'static str,
identifier: String,
limit: NameLimit,
},
#[error(
"the {statement} statement needs every column, and a `#[sqlx(flatten)]` field hides some: \
write it in the service"
)]
Flattened {
statement: &'static str,
},
#[error(
"the {dialect} dialect needs {required} or later for this form; the server reports \
`{server}`"
)]
ServerTooOld {
dialect: &'static str,
server: String,
required: &'static str,
},
#[error(
"the {dialect} dialect cannot read rows by a list of ids: list `fetch` in `custom(..)` \
beside `claim`"
)]
UnsupportedFetch {
dialect: &'static str,
},
#[error(
"the lease form computes its expiry from the crate's clock: a table on `DatabaseClock` \
cannot declare `locked_until`"
)]
LeaseOnDatabaseClock {
dialect: &'static str,
},
#[error("the {dialect} dialect opens no transaction at {opening}")]
UnsupportedOpening {
dialect: &'static str,
opening: &'static str,
},
#[error(
"the {dialect} dialect claims FIFO groups below SERIALIZABLE: InnoDB turns every read of a \
SERIALIZABLE transaction into a locking read, so a claim would wait for the group's row in \
work; declare `isolation = repeatable_read` or a lower level"
)]
FifoAtSerializable {
dialect: &'static str,
},
}
#[cfg(test)]
mod tests {
use super::{NameLimit, StatementError};
use crate::role::Role;
#[test]
fn errors_name_the_missing_piece() {
assert_eq!(
StatementError::UnsupportedForm {
dialect: "postgres",
form: "advisory lock",
}
.to_string(),
"the postgres dialect has no statements for the advisory lock form"
);
assert_eq!(
StatementError::UnsupportedFifo {
dialect: "postgres"
}
.to_string(),
"the postgres dialect has no claim for FIFO groups"
);
assert_eq!(
StatementError::MissingRole {
statement: "dead_letter_group",
role: Role::Group,
}
.to_string(),
"the dead_letter_group statement needs a column playing `group`: add `#[field(group)]` \
to the struct"
);
}
#[test]
fn the_new_errors_name_what_to_fix() {
assert_eq!(
StatementError::IdentifierTooLong {
dialect: "postgres",
identifier: "jobs".to_owned(),
limit: NameLimit::Bytes(63),
}
.to_string(),
"`jobs` is longer than the 63 bytes the postgres dialect allows in a name"
);
assert_eq!(
StatementError::IdentifierTooLong {
dialect: "mysql",
identifier: "jobs".to_owned(),
limit: NameLimit::Characters(64),
}
.to_string(),
"`jobs` is longer than the 64 characters the mysql dialect allows in a name"
);
assert_eq!(
StatementError::Flattened {
statement: "insert"
}
.to_string(),
"the insert statement needs every column, and a `#[sqlx(flatten)]` field hides some: \
write it in the service"
);
assert_eq!(
StatementError::FormMismatch {
statement: "extend",
form: "row lock",
}
.to_string(),
"the extend statement does not serve a table in the row lock form"
);
assert_eq!(
StatementError::UnsupportedOpening {
dialect: "postgres",
opening: "isolation `read_uncommitted`",
}
.to_string(),
"the postgres dialect opens no transaction at isolation `read_uncommitted`"
);
assert_eq!(
StatementError::FifoAtSerializable { dialect: "mysql" }.to_string(),
"the mysql dialect claims FIFO groups below SERIALIZABLE: InnoDB turns every read of a \
SERIALIZABLE transaction into a locking read, so a claim would wait for the group's \
row in work; declare `isolation = repeatable_read` or a lower level"
);
}
}