#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[non_exhaustive]
pub enum MergeShape {
Merge(MergeRestrictions),
Upsert(UpsertForm),
Unsupported(&'static str),
#[default]
None,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[non_exhaustive]
pub struct MergeRestrictions {
pub requires_two_when_clauses: bool,
}
impl MergeRestrictions {
#[must_use]
pub const fn none() -> Self {
Self { requires_two_when_clauses: false }
}
#[must_use]
pub const fn requiring_two_when_clauses() -> Self {
Self { requires_two_when_clauses: true }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum UpsertForm {
OnDuplicateKeyUpdate,
}
impl MergeShape {
#[must_use]
pub const fn merges(&self) -> bool {
matches!(self, Self::Merge(_))
}
#[must_use]
pub const fn merges_a_single_when_clause(&self) -> bool {
match self {
Self::Merge(restrictions) => !restrictions.requires_two_when_clauses,
_ => false,
}
}
#[must_use]
pub const fn upsert_form(&self) -> Option<UpsertForm> {
match self {
Self::Upsert(form) => Some(*form),
_ => None,
}
}
#[must_use]
pub const fn refusal(&self) -> Option<&'static str> {
match self {
Self::Unsupported(orientation) => Some(orientation),
Self::None => Some(
"this engine states no MERGE and no upsert statement Razeri can compose, so \
there is no generated load for it. A dialect says which it has by returning \
MergeShape::Merge or MergeShape::Upsert; until then Razeri refuses rather than \
emitting another engine's grammar.",
),
_ => None,
}
}
#[must_use]
pub const fn composes_a_load(&self) -> bool {
self.refusal().is_none()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_dialect_that_states_nothing_refuses_rather_than_borrowing_a_grammar() {
let shape = MergeShape::default();
assert!(!shape.merges());
assert!(!shape.merges_a_single_when_clause());
assert_eq!(shape.upsert_form(), None);
assert!(shape.refusal().is_some());
assert!(!shape.composes_a_load());
}
#[test]
fn a_measured_refusal_carries_the_engines_own_sentence_and_a_silent_one_does_not() {
let measured = MergeShape::Unsupported("this engine de-duplicates in the table engine");
assert_eq!(
measured.refusal(),
Some("this engine de-duplicates in the table engine"),
"a stated refusal answers in its own words"
);
assert_ne!(
MergeShape::None.refusal(),
measured.refusal(),
"silence and a measured answer must not read the same"
);
assert!(!measured.composes_a_load());
assert!(!measured.merges());
assert_eq!(measured.upsert_form(), None);
}
#[test]
fn a_shape_that_composes_a_load_carries_no_refusal() {
for shape in [
MergeShape::Merge(MergeRestrictions::none()),
MergeShape::Merge(MergeRestrictions::requiring_two_when_clauses()),
MergeShape::Upsert(UpsertForm::OnDuplicateKeyUpdate),
] {
assert_eq!(shape.refusal(), None, "{shape:?} composes a load");
assert!(shape.composes_a_load(), "{shape:?} composes a load");
}
}
#[test]
fn a_merge_with_a_clause_minimum_answers_the_two_questions_differently() {
let restricted = MergeShape::Merge(MergeRestrictions::requiring_two_when_clauses());
assert!(restricted.merges());
assert!(!restricted.merges_a_single_when_clause());
let unrestricted = MergeShape::Merge(MergeRestrictions::none());
assert!(unrestricted.merges());
assert!(unrestricted.merges_a_single_when_clause());
}
#[test]
fn an_upsert_engine_is_not_a_merge_engine() {
let upsert = MergeShape::Upsert(UpsertForm::OnDuplicateKeyUpdate);
assert!(!upsert.merges());
assert_eq!(upsert.upsert_form(), Some(UpsertForm::OnDuplicateKeyUpdate));
assert!(upsert.refusal().is_none());
}
}