feat: show relationship <name> renders a styled two-table diagram (ADR-0044)
The first wired slice of relationship visualization (V1). `show relationship <name>` now renders the relationship as two full structure boxes joined by a width-jogging connector (child-left / parent-right, n…1 cardinality, on delete/update actions), styled App-side, with a vertical-stack fallback for narrow terminals. - db.rs: RelationshipDiagramData + show_relationship worker path (structured data: the relationship + both endpoint TableDescriptions) - runtime.rs: named relationships route to the structured outcome (boxed); other show <kind> forms stay prose - app.rs/event.rs/ui.rs: DslShowRelationshipSucceeded rendered App-side; new diagram OutputStyleClass variants; App::last_output_width from ui.rs - output_render.rs: styled Seg layout engine (boxes, connector routing, side-by-side + vertical), composing the ADR-0016 box primitives Tests: 4 unit + 4 integration; full suite 2201 pass / 0 fail / 1 ignored; clippy nursery clean. requirements.md V1 stays [/] (show table diagrams, compound routing, DDL-echo wiring remain).
This commit is contained in:
@@ -80,6 +80,21 @@ pub struct TableDescription {
|
||||
pub check_constraints: Vec<crate::persistence::TableCheck>,
|
||||
}
|
||||
|
||||
/// Structured payload for rendering one relationship's diagram.
|
||||
///
|
||||
/// ADR-0044: the relationship plus both endpoint table structures.
|
||||
/// Built worker-side; rendered **App-side** (like `QueryPlan`) so the
|
||||
/// diagram can be width-aware and styled.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct RelationshipDiagramData {
|
||||
/// The relationship itself (endpoints + referential actions).
|
||||
pub rel: crate::persistence::RelationshipSchema,
|
||||
/// FK-holder (the `n` side), drawn on the left.
|
||||
pub child: TableDescription,
|
||||
/// Referenced table (the `1` side), drawn on the right.
|
||||
pub parent: TableDescription,
|
||||
}
|
||||
|
||||
/// One user-created index on a table (ADR-0025).
|
||||
///
|
||||
/// Read live from the engine's native catalog
|
||||
@@ -566,6 +581,13 @@ enum Request {
|
||||
name: Option<String>,
|
||||
reply: oneshot::Sender<Result<Vec<String>, DbError>>,
|
||||
},
|
||||
/// Structured data to render one relationship's diagram (ADR-0044
|
||||
/// §6): the relationship + both endpoint table structures, or
|
||||
/// `None` if no relationship by that name exists.
|
||||
ShowRelationship {
|
||||
name: String,
|
||||
reply: oneshot::Sender<Result<Option<RelationshipDiagramData>, DbError>>,
|
||||
},
|
||||
DescribeTable {
|
||||
name: String,
|
||||
source: Option<String>,
|
||||
@@ -1341,6 +1363,18 @@ impl Database {
|
||||
recv.await.map_err(|_| DbError::WorkerGone)?
|
||||
}
|
||||
|
||||
/// Structured data to render one relationship's diagram (ADR-0044):
|
||||
/// the relationship + both endpoint table structures, or `None` if
|
||||
/// no relationship by that name exists.
|
||||
pub async fn show_relationship(
|
||||
&self,
|
||||
name: String,
|
||||
) -> Result<Option<RelationshipDiagramData>, DbError> {
|
||||
let (reply, recv) = oneshot::channel();
|
||||
self.send(Request::ShowRelationship { name, reply }).await?;
|
||||
recv.await.map_err(|_| DbError::WorkerGone)?
|
||||
}
|
||||
|
||||
pub async fn describe_table(
|
||||
&self,
|
||||
name: String,
|
||||
@@ -2272,6 +2306,9 @@ fn handle_request(
|
||||
Request::ShowList { kind, name, reply } => {
|
||||
let _ = reply.send(do_show_list(conn, kind, name.as_deref()));
|
||||
}
|
||||
Request::ShowRelationship { name, reply } => {
|
||||
let _ = reply.send(do_show_relationship(conn, &name));
|
||||
}
|
||||
Request::DescribeTable {
|
||||
name,
|
||||
source,
|
||||
@@ -5870,6 +5907,25 @@ fn do_list_tables(conn: &Connection) -> Result<Vec<String>, DbError> {
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
/// Structured data to render one relationship's diagram (ADR-0044):
|
||||
/// find the named relationship, then describe both endpoint tables.
|
||||
/// `Ok(None)` when no relationship by that name exists (the App shows
|
||||
/// a friendly not-found line).
|
||||
fn do_show_relationship(
|
||||
conn: &Connection,
|
||||
name: &str,
|
||||
) -> Result<Option<RelationshipDiagramData>, DbError> {
|
||||
let Some(rel) = read_all_relationships(conn)?
|
||||
.into_iter()
|
||||
.find(|r| r.name == name)
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
let child = do_describe_table(conn, &rel.child_table)?;
|
||||
let parent = do_describe_table(conn, &rel.parent_table)?;
|
||||
Ok(Some(RelationshipDiagramData { rel, child, parent }))
|
||||
}
|
||||
|
||||
/// Pre-formatted display lines for the `show <kind>` list commands
|
||||
/// (V5). A count header followed by one indented item per line, or a
|
||||
/// single friendly "none yet" line for an empty collection. Reuses
|
||||
|
||||
Reference in New Issue
Block a user