feat(render): incidental-DDL confirmations show structure only, no relationships (#28)
Per ADR-0050 (closing issue #28): the confirmation echo after an incidental structural edit — create table, add/drop/rename/change column, add/drop index — now renders the structure only (header + column box + indexes + constraints) and no longer appends the References:/Referenced by: relationship block. Rationale: a confirmation reports the change just made, not the table's relationships, which the user didn't touch. Relationship info is still one `show table <T>` away, and the relationship-subject surfaces (show table, add/drop relationship) keep their ADR-0044 diagrams unchanged. Scope is all incidental DDL (user-confirmed). Mechanism: drop the relationship-block call from render_structure (all its callers are incidental DDL); the handle_dsl_success diagram-vs-structure routing is unchanged. The orphaned relationship_prose_lines + cols_disp helpers are deleted (the prose format survives in ADR-0016 §5 + git history for a future OOS-7 always-prose setting). ADR-0050 supersedes ADR-0044 §1's incidental-DDL prose clause and the relationship-block half of ADR-0016 §5 (both annotated). Tests: prose- presence unit test + snapshot removed; new unit test locks structure- only with inbound+outbound relationships present; the misnamed add- column integration test inverted + renamed. 2458 pass / 0 fail / 0 skip, clippy clean.
This commit is contained in:
+34
-63
@@ -71,27 +71,22 @@ pub fn render_data_table(data: &DataResult) -> Vec<String> {
|
||||
render_table(&header_cells, &body, &alignments)
|
||||
}
|
||||
|
||||
/// Render a table-structure listing.
|
||||
/// Render an incidental-DDL structure echo (ADR-0050, issue #28).
|
||||
///
|
||||
/// Produces a header line (`<TableName>`), the schema table
|
||||
/// itself, and — for a structure that has FK relationships
|
||||
/// — `References:` / `Referenced by:` blocks below as plain
|
||||
/// indented text (relationship visualization is its own
|
||||
/// future ADR per §5 OOS-1).
|
||||
/// Display a relationship-endpoint column list (ADR-0043): the bare
|
||||
/// column for a single-column FK, `(a, b)` for a compound one.
|
||||
fn cols_disp(cols: &[String]) -> String {
|
||||
if cols.len() == 1 {
|
||||
cols[0].clone()
|
||||
} else {
|
||||
format!("({})", cols.join(", "))
|
||||
}
|
||||
}
|
||||
|
||||
/// Produces a header line (`<TableName>`), the schema table, the
|
||||
/// `Indexes:` section, and the constraint section — **structure only**.
|
||||
/// Relationship information is deliberately omitted: a confirmation
|
||||
/// echo for a structural edit (`create table`, `add`/`drop`/`rename`/
|
||||
/// `change column`, `add`/`drop index`) reports the change just made,
|
||||
/// not the table's relationships, which the user did not touch. The
|
||||
/// relationship-subject surfaces (`show table`, `add`/`drop
|
||||
/// relationship`) render diagrams via [`render_structure_with_diagrams`]
|
||||
/// instead; relationships are one `show table <T>` away. ADR-0050
|
||||
/// supersedes ADR-0044 §1's "incidental DDL keeps prose" and the
|
||||
/// relationship-block half of ADR-0016 §5.
|
||||
#[must_use]
|
||||
pub fn render_structure(desc: &TableDescription) -> Vec<String> {
|
||||
let mut out = structure_box_lines(desc);
|
||||
out.extend(relationship_prose_lines(desc));
|
||||
out.extend(index_lines(desc));
|
||||
out.extend(constraint_lines(desc));
|
||||
out
|
||||
@@ -118,41 +113,6 @@ fn structure_box_lines(desc: &TableDescription) -> Vec<String> {
|
||||
out
|
||||
}
|
||||
|
||||
/// The `References:` / `Referenced by:` prose blocks (ADR-0016 §5),
|
||||
/// retained for the incidental DDL echoes (ADR-0044 §1).
|
||||
fn relationship_prose_lines(desc: &TableDescription) -> Vec<String> {
|
||||
let mut out: Vec<String> = Vec::new();
|
||||
if !desc.outbound_relationships.is_empty() {
|
||||
out.push("References:".to_string());
|
||||
for r in &desc.outbound_relationships {
|
||||
out.push(format!(
|
||||
" {} → {}.{} ({}, on delete {}, on update {})",
|
||||
cols_disp(&r.local_columns),
|
||||
r.other_table,
|
||||
cols_disp(&r.other_columns),
|
||||
r.name,
|
||||
r.on_delete,
|
||||
r.on_update,
|
||||
));
|
||||
}
|
||||
}
|
||||
if !desc.inbound_relationships.is_empty() {
|
||||
out.push("Referenced by:".to_string());
|
||||
for r in &desc.inbound_relationships {
|
||||
out.push(format!(
|
||||
" {}.{} → {} ({}, on delete {}, on update {})",
|
||||
r.other_table,
|
||||
cols_disp(&r.other_columns),
|
||||
cols_disp(&r.local_columns),
|
||||
r.name,
|
||||
r.on_delete,
|
||||
r.on_update,
|
||||
));
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
/// Indexes section (ADR-0025), only when the table carries a
|
||||
/// user-created index. A UNIQUE index is marked `[unique]` (ADR-0035
|
||||
/// §4d).
|
||||
@@ -1591,11 +1551,23 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn render_structure_with_relationships() {
|
||||
fn render_structure_omits_relationship_prose() {
|
||||
// ADR-0050 (issue #28): the incidental-DDL structure echo never
|
||||
// carries the `References:` / `Referenced by:` block, even when
|
||||
// the description carries both inbound and outbound
|
||||
// relationships. (Relationship-subject surfaces render diagrams
|
||||
// via `render_structure_with_diagrams`, not this function.)
|
||||
let desc = TableDescription {
|
||||
name: "Customers".to_string(),
|
||||
columns: vec![col("id", Type::Serial, true, false)],
|
||||
outbound_relationships: Vec::new(),
|
||||
outbound_relationships: vec![RelationshipEnd {
|
||||
name: "cust_region".to_string(),
|
||||
other_table: "Regions".to_string(),
|
||||
other_columns: vec!["id".to_string()],
|
||||
local_columns: vec!["region_id".to_string()],
|
||||
on_delete: ReferentialAction::NoAction,
|
||||
on_update: ReferentialAction::NoAction,
|
||||
}],
|
||||
inbound_relationships: vec![RelationshipEnd {
|
||||
name: "cust_orders".to_string(),
|
||||
other_table: "Orders".to_string(),
|
||||
@@ -1609,15 +1581,14 @@ mod tests {
|
||||
check_constraints: Vec::new(),
|
||||
};
|
||||
let out = render_structure(&desc).join("\n");
|
||||
assert!(
|
||||
out.contains("Referenced by:"),
|
||||
"expected inbound relationship section:\n{out}",
|
||||
);
|
||||
assert!(
|
||||
out.contains("Orders.cust_id → id"),
|
||||
"expected inbound relationship line:\n{out}",
|
||||
);
|
||||
assert_snapshot!(out);
|
||||
// The structure box still renders.
|
||||
assert!(out.contains("Customers"), "structure header:\n{out}");
|
||||
assert!(out.contains("│ id"), "column row:\n{out}");
|
||||
// No relationship block in either direction.
|
||||
assert!(!out.contains("References:"), "no outbound prose:\n{out}");
|
||||
assert!(!out.contains("Referenced by:"), "no inbound prose:\n{out}");
|
||||
assert!(!out.contains("Orders.cust_id"), "no prose line:\n{out}");
|
||||
assert!(!out.contains("Regions"), "no prose line:\n{out}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user