feat: V5a show relationship/index <name> detail views
Fold the singular per-item forms into Command::ShowList { kind,
name: Option<String> } (name: Some = one item). Two grammar
branches reuse the relationship/index completion sources; worker
do_show_one renders a labelled detail block or a friendly
"No ... named X." line, reusing the V5 render path. Help +
parse-usage entries, two ADR-0042 near-miss rows, 5 integration
tests. Mark V5a [x] — V5's [<name>] clause now complete.
This commit is contained in:
@@ -168,6 +168,8 @@ fn near_miss_matrix_committed_multiforms() {
|
||||
("drop index on T", false, &["after `drop index on T`, expected `(`", "drop index on <Table>"]),
|
||||
("drop relationship", false, &["after `drop relationship`, expected `from` or relationship name", "drop relationship <Name>"]),
|
||||
("show table", false, &["after `show table`, expected table name", "show table <Table>"]),
|
||||
("show relationship", false, &["after `show relationship`, expected relationship name", "show relationship <name>"]),
|
||||
("show index", false, &["after `show index`, expected index name", "show index <name>"]),
|
||||
("change column in table T: c", false, &["after `change column in table T: c`, expected `(`", "change column [in] [table]"]),
|
||||
// advanced committed multi-forms
|
||||
("create index on", true, &["after `create index on`, expected table name", "create [unique] index"]),
|
||||
|
||||
+93
-10
@@ -33,7 +33,8 @@ fn show_tables_parses_as_show_list_tables() {
|
||||
assert_eq!(
|
||||
parse_command("show tables").expect("parses"),
|
||||
Command::ShowList {
|
||||
kind: ShowListKind::Tables
|
||||
kind: ShowListKind::Tables,
|
||||
name: None,
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -43,7 +44,8 @@ fn show_relationships_parses_as_show_list_relationships() {
|
||||
assert_eq!(
|
||||
parse_command("show relationships").expect("parses"),
|
||||
Command::ShowList {
|
||||
kind: ShowListKind::Relationships
|
||||
kind: ShowListKind::Relationships,
|
||||
name: None,
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -53,7 +55,30 @@ fn show_indexes_parses_as_show_list_indexes() {
|
||||
assert_eq!(
|
||||
parse_command("show indexes").expect("parses"),
|
||||
Command::ShowList {
|
||||
kind: ShowListKind::Indexes
|
||||
kind: ShowListKind::Indexes,
|
||||
name: None,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn show_relationship_singular_parses_with_name() {
|
||||
assert_eq!(
|
||||
parse_command("show relationship orders_customer").expect("parses"),
|
||||
Command::ShowList {
|
||||
kind: ShowListKind::Relationships,
|
||||
name: Some("orders_customer".to_string()),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn show_index_singular_parses_with_name() {
|
||||
assert_eq!(
|
||||
parse_command("show index idx_orders_customer").expect("parses"),
|
||||
Command::ShowList {
|
||||
kind: ShowListKind::Indexes,
|
||||
name: Some("idx_orders_customer".to_string()),
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -145,7 +170,7 @@ fn show_tables_lists_all_user_tables_with_count_header() {
|
||||
let rt = rt();
|
||||
rt.block_on(seed_schema(&db));
|
||||
let lines = rt
|
||||
.block_on(db.show_list(ShowListKind::Tables))
|
||||
.block_on(db.show_list(ShowListKind::Tables, None))
|
||||
.expect("show tables");
|
||||
assert_eq!(lines[0], "Tables (2):", "count header");
|
||||
assert!(
|
||||
@@ -164,7 +189,7 @@ fn show_relationships_lists_name_endpoints_and_nondefault_action() {
|
||||
let rt = rt();
|
||||
rt.block_on(seed_schema(&db));
|
||||
let lines = rt
|
||||
.block_on(db.show_list(ShowListKind::Relationships))
|
||||
.block_on(db.show_list(ShowListKind::Relationships, None))
|
||||
.expect("show relationships");
|
||||
assert_eq!(lines[0], "Relationships (1):");
|
||||
// Name, both endpoints, and the non-default ON DELETE CASCADE
|
||||
@@ -182,7 +207,7 @@ fn show_indexes_lists_qualified_name_and_columns() {
|
||||
let rt = rt();
|
||||
rt.block_on(seed_schema(&db));
|
||||
let lines = rt
|
||||
.block_on(db.show_list(ShowListKind::Indexes))
|
||||
.block_on(db.show_list(ShowListKind::Indexes, None))
|
||||
.expect("show indexes");
|
||||
assert_eq!(lines[0], "Indexes (1):");
|
||||
assert_eq!(
|
||||
@@ -197,20 +222,76 @@ fn show_lists_report_empty_collections_with_friendly_lines() {
|
||||
let rt = rt();
|
||||
// No schema seeded — every kind is empty.
|
||||
assert_eq!(
|
||||
rt.block_on(db.show_list(ShowListKind::Tables)).unwrap(),
|
||||
rt.block_on(db.show_list(ShowListKind::Tables, None)).unwrap(),
|
||||
vec!["No tables in this project yet.".to_string()],
|
||||
);
|
||||
assert_eq!(
|
||||
rt.block_on(db.show_list(ShowListKind::Relationships))
|
||||
rt.block_on(db.show_list(ShowListKind::Relationships, None))
|
||||
.unwrap(),
|
||||
vec!["No relationships in this project yet.".to_string()],
|
||||
);
|
||||
assert_eq!(
|
||||
rt.block_on(db.show_list(ShowListKind::Indexes)).unwrap(),
|
||||
rt.block_on(db.show_list(ShowListKind::Indexes, None)).unwrap(),
|
||||
vec!["No indexes in this project yet.".to_string()],
|
||||
);
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// V5a — singular per-item detail
|
||||
// =================================================================
|
||||
|
||||
#[test]
|
||||
fn show_one_relationship_renders_detail_block() {
|
||||
let (_p, db, _dir) = open_project_db();
|
||||
let rt = rt();
|
||||
rt.block_on(seed_schema(&db));
|
||||
let lines = rt
|
||||
.block_on(db.show_list(ShowListKind::Relationships, Some("orders_customer".to_string())))
|
||||
.expect("show relationship");
|
||||
assert_eq!(lines[0], "Relationship `orders_customer`:");
|
||||
assert_eq!(lines[1], " Customers.id → Orders.customer_id");
|
||||
assert!(
|
||||
lines.iter().any(|l| l == " on delete cascade"),
|
||||
"on-delete shown: {lines:?}",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn show_one_index_renders_detail_block() {
|
||||
let (_p, db, _dir) = open_project_db();
|
||||
let rt = rt();
|
||||
rt.block_on(seed_schema(&db));
|
||||
let lines = rt
|
||||
.block_on(db.show_list(ShowListKind::Indexes, Some("idx_orders_customer".to_string())))
|
||||
.expect("show index");
|
||||
assert_eq!(lines[0], "Index `idx_orders_customer` on Orders:");
|
||||
assert!(
|
||||
lines.iter().any(|l| l == " columns: customer_id"),
|
||||
"columns shown: {lines:?}",
|
||||
);
|
||||
assert!(
|
||||
lines.iter().any(|l| l == " unique: no"),
|
||||
"uniqueness shown: {lines:?}",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn show_one_unknown_name_reports_friendly_not_found() {
|
||||
let (_p, db, _dir) = open_project_db();
|
||||
let rt = rt();
|
||||
rt.block_on(seed_schema(&db));
|
||||
assert_eq!(
|
||||
rt.block_on(db.show_list(ShowListKind::Relationships, Some("nope".to_string())))
|
||||
.unwrap(),
|
||||
vec!["No relationship named `nope`.".to_string()],
|
||||
);
|
||||
assert_eq!(
|
||||
rt.block_on(db.show_list(ShowListKind::Indexes, Some("nope".to_string())))
|
||||
.unwrap(),
|
||||
vec!["No index named `nope`.".to_string()],
|
||||
);
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// App end-to-end
|
||||
// =================================================================
|
||||
@@ -241,7 +322,8 @@ fn app_show_tables_dispatches_show_list_command() {
|
||||
a,
|
||||
Action::ExecuteDsl {
|
||||
command: Command::ShowList {
|
||||
kind: ShowListKind::Tables
|
||||
kind: ShowListKind::Tables,
|
||||
name: None,
|
||||
},
|
||||
..
|
||||
}
|
||||
@@ -262,6 +344,7 @@ fn app_renders_show_list_lines_as_system_output() {
|
||||
app.update(AppEvent::DslShowListSucceeded {
|
||||
command: Command::ShowList {
|
||||
kind: ShowListKind::Tables,
|
||||
name: None,
|
||||
},
|
||||
lines: vec!["Tables (1):".to_string(), " Customers".to_string()],
|
||||
});
|
||||
|
||||
@@ -233,7 +233,7 @@ fn command_kind_label(cmd: &rdbms_playground::dsl::Command) -> String {
|
||||
AddConstraint { .. } => "AddConstraint".into(),
|
||||
DropConstraint { .. } => "DropConstraint".into(),
|
||||
ShowTable { .. } => "ShowTable".into(),
|
||||
ShowList { kind } => format!("ShowList({kind:?})"),
|
||||
ShowList { kind, name } => format!("ShowList({kind:?}, {})", name.is_some()),
|
||||
Insert { .. } => "Insert".into(),
|
||||
Update { .. } => "Update".into(),
|
||||
Delete { .. } => "Delete".into(),
|
||||
|
||||
Reference in New Issue
Block a user