aliasing3

This commit is contained in:
Priec
2026-08-13 14:41:27 +02:00
parent 5fe630fff7
commit 1f8b73793d
8 changed files with 110 additions and 62 deletions

View File

@@ -21,6 +21,9 @@ use crate::{
/// them, and [`TableDraft::preview_rows`] is where that is explained.
pub(crate) const ACCOUNTING_FIELD_TYPE: &str = "accounting";
/// The compound type whose connectors remain tied to their backend names.
pub(crate) const ACCOUNTING_TRANSFER_FIELD_TYPE: &str = "accounting_transfer";
/// The virtual field the ACCOUNTING foreign key is written as. It is a column
/// like any other once created, so it can be aliased too.
pub(crate) const ACCOUNT_API_COLUMN: &str = "account";
@@ -225,18 +228,13 @@ impl TableDraft {
/// The generated columns this draft would let the user rename, in the order
/// they appear in the column list.
///
/// ACCOUNTING's, and only ACCOUNTING's. A generated column can carry a name
/// of its own once the rest of the system can find it without that name --
/// which for ACCOUNTING is `table_accounting_definitions`, recording its
/// columns by physical name. The ACCOUNTING_TRANSFER connectors and the
/// PHONE and IBAN companions have no such record: a companion is found on
/// write by rebuilding its name from its parent's, so renaming one would
/// hide it from the write path. The backend refuses those, and this does
/// not offer what the backend refuses.
/// Every generated column whose relationship is recorded independently of
/// its display name. ACCOUNTING_TRANSFER's connectors are the exception:
/// the backend still resolves those by their fixed names.
pub(crate) fn aliasable_generated_columns(&self) -> Vec<String> {
let mut names = Vec::new();
for (index, column) in self.columns.added.iter().enumerate() {
if column.data_type != ACCOUNTING_FIELD_TYPE {
if column.data_type == ACCOUNTING_TRANSFER_FIELD_TYPE {
continue;
}
names.extend(
@@ -245,7 +243,9 @@ impl TableDraft {
.iter()
.map(|generated| generated.name.clone()),
);
names.push(ACCOUNT_API_COLUMN.to_string());
if column.data_type == ACCOUNTING_FIELD_TYPE {
names.push(ACCOUNT_API_COLUMN.to_string());
}
}
names
}
@@ -555,24 +555,34 @@ mod tests {
);
}
/// Only ACCOUNTING's columns are offered. The transfer connectors are
/// resolved by name by the posting engine, and a phone or IBAN companion is
/// found on write by rebuilding its name from its parent's -- the backend
/// refuses both, so neither is offered here.
/// ACCOUNTING, PHONE and IBAN companions may be aliased. Transfer
/// connectors remain fixed because the posting engine resolves their names.
#[test]
fn only_accounting_columns_are_aliasable() {
for (name, data_type) in [
("accounting_transfer", "accounting_transfer"),
("work_phone", "phone"),
("bank_account", "iban"),
] {
let draft = draft_with_column(name, data_type);
fn every_recorded_generated_column_is_aliasable() {
let phone = draft_with_column("work_phone", "phone");
assert_eq!(
phone.aliasable_generated_columns(),
[
"work_phone_ext",
"work_phone_type",
"work_phone_country",
"work_phone_calling_code",
]
);
assert!(
draft.aliasable_generated_columns().is_empty(),
"`{data_type}` should offer no aliases"
);
}
let iban = draft_with_column("bank_account", "iban");
assert_eq!(
iban.aliasable_generated_columns(),
[
"bank_account_country",
"bank_account_bban",
"bank_account_bank_identifier",
"bank_account_branch_identifier",
]
);
let transfer = draft_with_column("accounting_transfer", "accounting_transfer");
assert!(transfer.aliasable_generated_columns().is_empty());
}
#[test]