nadavrot / layout

Layout is a rust library and a tool that renders Graphviz dot files.
MIT License
626 stars 35 forks source link

Feature request for ability to render UML class or erd diagrams #8

Open bartekupartek opened 2 years ago

bartekupartek commented 2 years ago

I found your library and it looks it has a great support for rendering orthogonal edge connections and nodes from dot files however I couldn’t find in Rust ecosystem any library thigh might support erd diagrams rendering from ie. dot format. From my research I found only GraphViz is able to render html tables embedded in node labels or edges with from and to arrow with cardianity. Have you considered to extend your library in such direction in the future?

The example dot file:

graph {
    graph [
        label=<<FONT POINT-SIZE="20">nfldb Entity-Relationship diagram (condensed)</FONT>>,
        labeljust=l,
        labelloc=t,
        rankdir=LR,
        splines=spline,
    ];
    node [
        label="\N",
        shape=plaintext,
    ];
    edge [
        color=gray50,
        minlen=2,
        style=dashed,
    ];
    "player" [
        label=<
<FONT FACE="Helvetica">
  <TABLE BORDER="0" CELLBORDER="1" CELLPADDING="4" CELLSPACING="0" BGCOLOR="#d0e0d0">
    <TR><TD><B><FONT POINT-SIZE="16">player</FONT></B></TD></TR>
    <TR><TD ALIGN="LEFT"><U>player_id</U> [varchar, not null]</TD></TR>
    <TR><TD ALIGN="LEFT">full_name [varchar, null]</TD></TR>
    <TR><TD ALIGN="LEFT">team [varchar, not null]</TD></TR>
    <TR><TD ALIGN="LEFT">position [player_pos, not null]</TD></TR>
    <TR><TD ALIGN="LEFT">status [player_status, not null]</TD></TR>
  </TABLE>
</FONT>
>];
    "team" [
        label=<
<FONT FACE="Helvetica">
  <TABLE BORDER="0" CELLBORDER="1" CELLPADDING="4" CELLSPACING="0" BGCOLOR="#d0e0d0">
    <TR><TD><B><FONT POINT-SIZE="16">team</FONT></B></TD></TR>
    <TR><TD ALIGN="LEFT"><U>team_id</U> [varchar, not null]</TD></TR>
    <TR><TD ALIGN="LEFT">city [varchar, not null]</TD></TR>
    <TR><TD ALIGN="LEFT">name [varchar, not null]</TD></TR>
  </TABLE>
</FONT>
>];
    "game" [
        label=<
<FONT FACE="Helvetica">
  <TABLE BORDER="0" CELLBORDER="1" CELLPADDING="4" CELLSPACING="0" BGCOLOR="#ececfc">
    <TR><TD><B><FONT POINT-SIZE="16">game</FONT></B></TD></TR>
    <TR><TD ALIGN="LEFT"><U>gsis_id</U> [gameid, not null]</TD></TR>
    <TR><TD ALIGN="LEFT">start_time [utctime, not null]</TD></TR>
    <TR><TD ALIGN="LEFT">week [usmallint, not null]</TD></TR>
    <TR><TD ALIGN="LEFT">season_year [usmallint, not null]</TD></TR>
    <TR><TD ALIGN="LEFT">season_type [season_phase, not null]</TD></TR>
    <TR><TD ALIGN="LEFT">finished [boolean, not null]</TD></TR>
    <TR><TD ALIGN="LEFT">home_team [varchar, not null]</TD></TR>
    <TR><TD ALIGN="LEFT">home_score [usmallint, not null]</TD></TR>
    <TR><TD ALIGN="LEFT">away_team [varchar, not null]</TD></TR>
    <TR><TD ALIGN="LEFT">away_score [usmallint, not null]</TD></TR>
  </TABLE>
</FONT>
>];
    "drive" [
        label=<
<FONT FACE="Helvetica">
  <TABLE BORDER="0" CELLBORDER="1" CELLPADDING="4" CELLSPACING="0" BGCOLOR="#ececfc">
    <TR><TD><B><FONT POINT-SIZE="16">drive</FONT></B></TD></TR>
    <TR><TD ALIGN="LEFT"><I><U>gsis_id</U></I> [gameid, not null]</TD></TR>
    <TR><TD ALIGN="LEFT"><U>drive_id</U> [usmallint, not null]</TD></TR>
    <TR><TD ALIGN="LEFT">start_field [field_pos, null]</TD></TR>
    <TR><TD ALIGN="LEFT">start_time [game_time, not null]</TD></TR>
    <TR><TD ALIGN="LEFT">end_field [field_pos, null]</TD></TR>
    <TR><TD ALIGN="LEFT">end_time [game_time, not null]</TD></TR>
    <TR><TD ALIGN="LEFT">pos_team [varchar, not null]</TD></TR>
    <TR><TD ALIGN="LEFT">pos_time [pos_period, null]</TD></TR>
  </TABLE>
</FONT>
>];
    "play" [
        label=<
<FONT FACE="Helvetica">
  <TABLE BORDER="0" CELLBORDER="1" CELLPADDING="4" CELLSPACING="0" BGCOLOR="#ececfc">
    <TR><TD><B><FONT POINT-SIZE="16">play</FONT></B></TD></TR>
    <TR><TD ALIGN="LEFT"><I><U>gsis_id</U></I> [gameid, not null]</TD></TR>
    <TR><TD ALIGN="LEFT"><I><U>drive_id</U></I> [usmallint, not null]</TD></TR>
    <TR><TD ALIGN="LEFT"><U>play_id</U> [usmallint, not null]</TD></TR>
    <TR><TD ALIGN="LEFT">time [game_time, not null]</TD></TR>
    <TR><TD ALIGN="LEFT">pos_team [varchar, not null]</TD></TR>
    <TR><TD ALIGN="LEFT">yardline [field_pos, null]</TD></TR>
    <TR><TD ALIGN="LEFT">down [smallint, null]</TD></TR>
    <TR><TD ALIGN="LEFT">yards_to_go [smallint, null]</TD></TR>
  </TABLE>
</FONT>
>];
    "play_player" [
        label=<
<FONT FACE="Helvetica">
  <TABLE BORDER="0" CELLBORDER="1" CELLPADDING="4" CELLSPACING="0" BGCOLOR="#ececfc">
    <TR><TD><B><FONT POINT-SIZE="16">play_player</FONT></B></TD></TR>
    <TR><TD ALIGN="LEFT"><I><U>gsis_id</U></I> [gameid, not null]</TD></TR>
    <TR><TD ALIGN="LEFT"><I><U>drive_id</U></I> [usmallint, not null]</TD></TR>
    <TR><TD ALIGN="LEFT"><I><U>play_id</U></I> [usmallint, not null]</TD></TR>
    <TR><TD ALIGN="LEFT"><I><U>player_id</U></I> [varchar, not null]</TD></TR>
    <TR><TD ALIGN="LEFT">team [varchar, not null]</TD></TR>
  </TABLE>
</FONT>
>];
    "meta" [
        label=<
<FONT FACE="Helvetica">
  <TABLE BORDER="0" CELLBORDER="1" CELLPADDING="4" CELLSPACING="0" BGCOLOR="#fcecec">
    <TR><TD><B><FONT POINT-SIZE="16">meta</FONT></B></TD></TR>
    <TR><TD ALIGN="LEFT">version [smallint, null]</TD></TR>
    <TR><TD ALIGN="LEFT">season_type [season_phase, null]</TD></TR>
    <TR><TD ALIGN="LEFT">season_year [usmallint, null]</TD></TR>
    <TR><TD ALIGN="LEFT">week [usmallint, null]</TD></TR>
  </TABLE>
</FONT>
>];
    "player" -- "team" [ headlabel="1", taillabel="0..N" ];
    "game" -- "team" [ headlabel="1", taillabel="0..N" ];
    "game" -- "team" [ headlabel="1", taillabel="0..N" ];
    "drive" -- "team" [ headlabel="1", taillabel="0..N" ];
    "play" -- "team" [ headlabel="1", taillabel="0..N" ];
    "play_player" -- "team" [ headlabel="1", taillabel="0..N" ];
    "game" -- "drive" [ headlabel="0..N", taillabel="1" ];
    "game" -- "play" [ headlabel="0..N", taillabel="1" ];
    "game" -- "play_player" [ headlabel="0..N", taillabel="1" ];
    "drive" -- "play" [ headlabel="0..N", taillabel="1" ];
    "drive" -- "play_player" [ headlabel="0..N", taillabel="1" ];
    "play" -- "play_player" [ headlabel="0..N", taillabel="1" ];
    "player" -- "play_player" [ headlabel="0..N", taillabel="1" ];
}

Would it be some better method to draw tables than html tables? I generated this dot syntax by https://github.com/BurntSushi/erd which parses some custom markup syntax and produces dot files but as in all similar projects it uses GraphViz to render final digram. I would be nice to have everything in Rust to use it in eg wasm. I also think erd or class diagrams might need some different way of rendering in square grid not in top down hierarchy, I think similarly as in your example here https://github.com/nadavrot/layout/blob/master/inputs/5.dot but more compact also arrows should point the whole table node or given record from FK if it would match relation key also it shouldn't cross the node but anchored on the outside edge.