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:
@@ -560,6 +560,9 @@ enum Request {
|
||||
/// from the same helpers the items panel and describe view use.
|
||||
ShowList {
|
||||
kind: crate::dsl::command::ShowListKind,
|
||||
/// `None` lists all items of the kind; `Some(name)` shows
|
||||
/// one named relationship/index's detail (V5a).
|
||||
name: Option<String>,
|
||||
reply: oneshot::Sender<Result<Vec<String>, DbError>>,
|
||||
},
|
||||
DescribeTable {
|
||||
@@ -1330,9 +1333,10 @@ impl Database {
|
||||
pub async fn show_list(
|
||||
&self,
|
||||
kind: crate::dsl::command::ShowListKind,
|
||||
name: Option<String>,
|
||||
) -> Result<Vec<String>, DbError> {
|
||||
let (reply, recv) = oneshot::channel();
|
||||
self.send(Request::ShowList { kind, reply }).await?;
|
||||
self.send(Request::ShowList { kind, name, reply }).await?;
|
||||
recv.await.map_err(|_| DbError::WorkerGone)?
|
||||
}
|
||||
|
||||
@@ -2264,8 +2268,8 @@ fn handle_request(
|
||||
Request::ListTables { reply } => {
|
||||
let _ = reply.send(do_list_tables(conn));
|
||||
}
|
||||
Request::ShowList { kind, reply } => {
|
||||
let _ = reply.send(do_show_list(conn, kind));
|
||||
Request::ShowList { kind, name, reply } => {
|
||||
let _ = reply.send(do_show_list(conn, kind, name.as_deref()));
|
||||
}
|
||||
Request::DescribeTable {
|
||||
name,
|
||||
@@ -5865,8 +5869,13 @@ fn do_list_tables(conn: &Connection) -> Result<Vec<String>, DbError> {
|
||||
fn do_show_list(
|
||||
conn: &Connection,
|
||||
kind: crate::dsl::command::ShowListKind,
|
||||
name: Option<&str>,
|
||||
) -> Result<Vec<String>, DbError> {
|
||||
use crate::dsl::command::ShowListKind;
|
||||
// V5a: a named item shows one relationship/index's detail.
|
||||
if let Some(name) = name {
|
||||
return do_show_one(conn, kind, name);
|
||||
}
|
||||
let mut lines = Vec::new();
|
||||
match kind {
|
||||
ShowListKind::Tables => {
|
||||
@@ -5929,6 +5938,64 @@ fn do_show_list(
|
||||
Ok(lines)
|
||||
}
|
||||
|
||||
/// Detail lines for one named relationship or index (V5a): a
|
||||
/// labelled block, or a friendly "no such item" line. `Tables` is
|
||||
/// never routed here (the table singular is `ShowTable`); the
|
||||
/// defensive arm keeps the match total without a panic.
|
||||
fn do_show_one(
|
||||
conn: &Connection,
|
||||
kind: crate::dsl::command::ShowListKind,
|
||||
name: &str,
|
||||
) -> Result<Vec<String>, DbError> {
|
||||
use crate::dsl::command::ShowListKind;
|
||||
let mut lines = Vec::new();
|
||||
match kind {
|
||||
ShowListKind::Relationships => match read_all_relationships(conn)?
|
||||
.into_iter()
|
||||
.find(|r| r.name == name)
|
||||
{
|
||||
None => lines.push(format!("No relationship named `{name}`.")),
|
||||
Some(r) => {
|
||||
lines.push(format!("Relationship `{}`:", r.name));
|
||||
lines.push(format!(
|
||||
" {}.{} → {}.{}",
|
||||
r.parent_table, r.parent_column, r.child_table, r.child_column
|
||||
));
|
||||
lines.push(format!(" on delete {}", r.on_delete.keyword()));
|
||||
lines.push(format!(" on update {}", r.on_update.keyword()));
|
||||
}
|
||||
},
|
||||
ShowListKind::Indexes => {
|
||||
// Find the user-created index by name across all tables.
|
||||
let mut found = None;
|
||||
for table in do_list_tables(conn)? {
|
||||
if let Some(ix) = read_table_indexes(conn, &table)?
|
||||
.into_iter()
|
||||
.find(|ix| ix.name == name)
|
||||
{
|
||||
found = Some((table, ix));
|
||||
break;
|
||||
}
|
||||
}
|
||||
match found {
|
||||
None => lines.push(format!("No index named `{name}`.")),
|
||||
Some((table, ix)) => {
|
||||
lines.push(format!("Index `{}` on {table}:", ix.name));
|
||||
lines.push(format!(" columns: {}", ix.columns.join(", ")));
|
||||
lines.push(format!(
|
||||
" unique: {}",
|
||||
if ix.unique { "yes" } else { "no" }
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
ShowListKind::Tables => {
|
||||
lines.push(format!("No relationship or index named `{name}`."));
|
||||
}
|
||||
}
|
||||
Ok(lines)
|
||||
}
|
||||
|
||||
/// Internal full schema of a table, sufficient to regenerate
|
||||
/// its `CREATE TABLE` statement during the rebuild dance.
|
||||
#[derive(Debug, Clone)]
|
||||
|
||||
Reference in New Issue
Block a user