use crate::data_type::DataType;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub struct EvolutionAnswers {
pub parameterized_string_widening_is_lossless: bool,
pub nested_field_addition_is_lossless: bool,
pub drops_a_column_in_place_before_an_index_column: bool,
}
impl Default for EvolutionAnswers {
fn default() -> Self {
Self::none()
}
}
impl EvolutionAnswers {
#[must_use]
pub const fn none() -> Self {
Self {
parameterized_string_widening_is_lossless: false,
nested_field_addition_is_lossless: false,
drops_a_column_in_place_before_an_index_column: true,
}
}
#[must_use]
pub const fn widening_a_string_in_place() -> Self {
Self { parameterized_string_widening_is_lossless: true, ..Self::none() }
}
#[must_use]
pub const fn and_a_nested_field(self) -> Self {
Self { nested_field_addition_is_lossless: true, ..self }
}
#[must_use]
pub const fn but_no_drop_before_an_index_column(self) -> Self {
Self { drops_a_column_in_place_before_an_index_column: false, ..self }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub struct WrittenObject<'a> {
pub schema: Option<&'a str>,
pub name: &'a str,
pub relation: &'a str,
}
impl<'a> WrittenObject<'a> {
#[must_use]
pub const fn new(schema: Option<&'a str>, name: &'a str, relation: &'a str) -> Self {
Self { schema, name, relation }
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum Written {
Statement(String),
Block(String),
}
impl Written {
#[must_use]
pub fn text(&self) -> &str {
match self {
Self::Statement(text) | Self::Block(text) => text,
}
}
}
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub enum ColumnEvolution<'a> {
AddColumn {
schema: Option<&'a str>,
table: &'a str,
column: &'a str,
data_type: &'a DataType,
},
WidenColumn {
schema: Option<&'a str>,
table: &'a str,
column: &'a str,
data_type: &'a DataType,
},
AddNestedField {
schema: Option<&'a str>,
table: &'a str,
column: &'a str,
field: &'a str,
data_type: &'a DataType,
},
RequireColumn {
schema: Option<&'a str>,
table: &'a str,
column: &'a str,
data_type: &'a DataType,
},
}
impl ColumnEvolution<'_> {
#[must_use]
pub const fn schema(&self) -> Option<&str> {
match self {
Self::AddColumn { schema, .. }
| Self::WidenColumn { schema, .. }
| Self::AddNestedField { schema, .. }
| Self::RequireColumn { schema, .. } => *schema,
}
}
#[must_use]
pub const fn table(&self) -> &str {
match self {
Self::AddColumn { table, .. }
| Self::WidenColumn { table, .. }
| Self::AddNestedField { table, .. }
| Self::RequireColumn { table, .. } => table,
}
}
#[must_use]
pub const fn column(&self) -> &str {
match self {
Self::AddColumn { column, .. }
| Self::WidenColumn { column, .. }
| Self::AddNestedField { column, .. }
| Self::RequireColumn { column, .. } => column,
}
}
#[must_use]
pub const fn data_type(&self) -> &DataType {
match self {
Self::AddColumn { data_type, .. }
| Self::WidenColumn { data_type, .. }
| Self::AddNestedField { data_type, .. }
| Self::RequireColumn { data_type, .. } => data_type,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn nothing_is_claimed_until_a_dialect_claims_it() {
let silent = EvolutionAnswers::default();
assert!(!silent.parameterized_string_widening_is_lossless);
assert!(!silent.nested_field_addition_is_lossless);
assert_eq!(silent, EvolutionAnswers::none());
}
#[test]
fn a_drop_is_made_in_place_until_a_dialect_says_its_engine_cannot() {
assert!(EvolutionAnswers::none().drops_a_column_in_place_before_an_index_column);
assert!(
EvolutionAnswers::widening_a_string_in_place()
.drops_a_column_in_place_before_an_index_column
);
let duckdb_like = EvolutionAnswers::none().but_no_drop_before_an_index_column();
assert!(!duckdb_like.drops_a_column_in_place_before_an_index_column);
assert!(!duckdb_like.parameterized_string_widening_is_lossless);
}
#[test]
fn an_engine_that_keeps_both_states_both() {
let both = EvolutionAnswers::widening_a_string_in_place().and_a_nested_field();
assert!(both.parameterized_string_widening_is_lossless);
assert!(both.nested_field_addition_is_lossless);
}
#[test]
fn a_change_names_its_table_and_column_whichever_change_it_is() {
let data_type = DataType::boolean();
let change = ColumnEvolution::AddColumn {
schema: Some("telemetry_core"),
table: "core_clients_daily",
column: "is_default_browser",
data_type: &data_type,
};
assert_eq!(change.schema(), Some("telemetry_core"));
assert_eq!(change.table(), "core_clients_daily");
assert_eq!(change.column(), "is_default_browser");
}
}