Giter Club home page Giter Club logo

graphviz-rust's Introduction

Description

The library provides the basic access to the graphs in graphviz format with ability to import into or export from it.

Base examples

Parse dot source

use dot_generator::*;
use dot_structures::*;

fn parse_test() {
    let g: Graph = parse(
        r#"
        strict digraph t {
            aa[color=green]
            subgraph v {
                aa[shape=square]
                subgraph vv{a2 -> b2}
                aaa[color=red]
                aaa -> bbb
            }
            aa -> be -> subgraph v { d -> aaa}
            aa -> aaa -> v
        }
        "#,
    )
    .unwrap();

    assert_eq!(
        g,
        graph!(strict di id!("t");
          node!("aa";attr!("color","green")),
          subgraph!("v";
            node!("aa"; attr!("shape","square")),
            subgraph!("vv"; edge!(node_id!("a2") => node_id!("b2"))),
            node!("aaa";attr!("color","red")),
            edge!(node_id!("aaa") => node_id!("bbb"))
            ),
          edge!(node_id!("aa") => node_id!("be") => subgraph!("v"; edge!(node_id!("d") => node_id!("aaa")))),
          edge!(node_id!("aa") => node_id!("aaa") => node_id!("v"))
        )
    )
}

Print graph into dot source

use dot_generator::*;
use dot_structures::*;
use graphviz_rust::printer::{DotPrinter, PrinterContext};

fn print_test() {
    let mut g = graph!(strict di id!("id"));
    assert_eq!(
        "strict digraph id {}".to_string(),
        g.print(&mut PrinterContext::default())
    );
}

Transform graph into external formats with cmd engine

use dot_generator::*;
use dot_structures::*;
use graphviz_rust::{
    attributes::*,
    cmd::{CommandArg, Format},
    exec, parse,
    printer::{DotPrinter, PrinterContext},
};

fn output_test() {
    let mut g = graph!(id!("id");
         node!("nod"),
         subgraph!("sb";
             edge!(node_id!("a") => subgraph!(;
                node!("n";
                NodeAttributes::color(color_name::black), NodeAttributes::shape(shape::egg))
            ))
        ),
        edge!(node_id!("a1") => node_id!(esc "a2"))
    );
    let graph_svg = exec(
        g,
        &mut PrinterContext::default(),
        vec![Format::Svg.into()],
    )
    .unwrap();
}

Structure

The structure pursues to follow the dot notation closely, therefore it has straight accordance. The structures can be found in dot_structures::* and has the following denotion:

strict digraph t {                     : graph with t as id
        aa[color=green]                : node aa and attributes in []
        subgraph v {                   : subgraph v
         aa[shape=square]            : node aa in subgraph
         subgraph vv{a2 -> b2}       : another subgraph carrying edge inside( a type of the edge is Pair)
         aaa[color=red]
         aaa -> subgraph { d -> aaa} : subgraph id is anonymous id
        }
       aa -> be -> d -> aaa            : other edge with a type Chain
   }

Generate a dot structure

The library provides a set of macros alleviating the process of graph construction. The details including examples for every macros are given in the documentation for the macros and can be found in the dot_generator::*

Example

assert_eq!(
    node!("node_id"; attr!("atr1","val1"),attr!("atr2","val2")),
    node!(
        "node_id",
        vec![attr!("atr1", "val1"), attr!("atr2", "val2")]
    )
);

fn graph_test() {
    use dot_generator::*;
    use dot_structures::*;

    let g = r#"
            strict digraph t {
                aa[color=green]
                subgraph v {
                    aa[shape=square]
                    subgraph vv{a2 -> b2}
                    aaa[color=red]
                    aaa -> bbb
                }
                aa -> be -> subgraph v { d -> aaa}
                aa -> aaa -> v
            }
            "#;

    graph!(strict di id!("t");
      node!("aa";attr!("color","green")),
      subgraph!("v";
        node!("aa"; attr!("shape","square")),
        subgraph!("vv"; edge!(node_id!("a2") => node_id!("b2"))),
        node!("aaa";attr!("color","red")),
        edge!(node_id!("aaa") => node_id!("bbb"))
        ),
      edge!(node_id!("aa") => node_id!("be") => subgraph!("v"; edge!(node_id!("d") => node_id!("aaa")))),
      edge!(node_id!("aa") => node_id!("aaa") => node_id!("v"))
    );
}

Attributes

The graphviz provides an enormous amount of possible attributes and to support it, the library provides a set of structures alleviating the navigation among them namely:

  • custom attribute can be easily compound with the macros attr!(id,id) nevertheless another possible formats:
  • using named attributes like graphviz_rust::attributes::color for the color attribute
  • using the particular structures graphviz_rust::attributes::{EdgeAttributes,SubgraphAttributes GraphAttributes, NodeAttributes} grouping and displaying which attribute belongs to the struct.
use dot_generator::*;
use dot_structures::*;
use graphviz_rust::attributes::{
    color, color_name, GraphAttributes, NodeAttributes,
};
use into_attr::IntoAttribute;

fn test() {
    assert_eq!(GraphAttributes::center(true), attr!("center", true));
    assert_eq!(
        NodeAttributes::color(color_name::antiquewhite1),
        attr!("color", "antiquewhite1")
    );
    assert_eq!(color::default().into_attr(), attr!("color", "black"));
}

Transform into string following a dot format

The trait DotPrinter is summoned to transform a graph structure into string.

use dot_generator::*;
use dot_structures::*;
use graphviz_rust::printer::{DotPrinter, PrinterContext};

fn subgraph_test() {
    let mut ctx = PrinterContext::default();
    let s =
        subgraph!("id"; node!("abc"), edge!(node_id!("a") => node_id!("b")));

    assert_eq!(
        s.print(&mut ctx),
        "subgraph id {\n    abc\n    a -- b \n}".to_string()
    );
}

The module allows adjusting some parameters such as indent step or line separator using PrinterContext:

fn ctx() {
    use self::graphviz_rust::printer::PrinterContext;
    let mut ctx = PrinterContext::default();

    ctx.always_inline(); // everything in one line
    ctx.with_semi(); // semicolon at the end of every element
    ctx.with_indent_step(4); // indent 4 (default 2)
    ctx.with_inline_size(60); // size indicating the line needs to break into multilines
}

External formats and others using cmd engine

The library provides an ability to use command commands from the rust code. The details are denoted in graphviz_rust::{exec} and graphviz_rust::{exec_dot} methods

fn output_test() {
    let mut g = graph!(id!("id"));
    exec(
        g,
        PrinterContext::default(),
        vec![
            Format::Svg.into(),
            CommandArg::Output("path_to_file".to_string()),
        ],
    );
}

Caveats

The command client should be installed

Since, the library operates with a cmd client to execute the commands, the client should be installed beforehand, otherwise, the errors like: No file or directory found or program not found (depending on the OS) will be popped up.

graphviz-rust's People

Contributors

aminya avatar besok avatar connorskees avatar flying-sheep avatar ncthbrt avatar orf avatar scottcusa avatar tepperson2 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

graphviz-rust's Issues

Special characters in IDs result in invalid output

Example:

let graph = {
    use graphviz_rust::dot_generator::*;
    graph!(strict di id!("h[t]"))
};

let mut ctx = PrinterContext::default();
ctx.with_indent_step(2);
let out = graph.print(&mut ctx);

Output (invalid DOT):

strict digraph h[t] {

}

Expected output:

strict digraph "h[t]" {

}

Add Clone/Copy impls for enum cmd::Format, export a version of exec that takes a rendered graph, add more descriptive error messages

Hi, I'm currently using graphviz-rust to build some dependency graph visualizations and I've run into some small issues with the API that mildly affect UX.

  1. No way to clone a cmd::Format.
    cmd::Format doesn't implement Clone, so there's no way to set constants for default layouts, or take a desired layout as a command-line argument. It seems that it should be possible to derive Clone on this type given that it's a simple enum.

  2. No way to cache the result of printing a Graph before passing it to exec.
    I'm currently building a Graph then rendering it to svg, but I'd like to export the rendered .dot file as well. The exec function exported from the crate root only takes a Graph as an argument, but runs print on it immediately. There's no way to reuse the result of print and pass it directly to exec afterward - this results in the graph needing to be printed twice in order to both save the raw .dot markup and call graphviz on it. It would be nice to have a similar function, e.g. exec_from_dot_file, that takes a string as input.

  3. Nondescriptive error message when graphviz is missing from PATH
    If the user doesn't have dot in their PATH, exec fails with an error No file or directory found. It isn't immediately obvious what the source of this error is, or how to remedy it. exec should ideally return a message telling the user to install graphviz; this caveat is mentioned in the docs but buried inside the cmd module.

Thank you for releasing this crate, it's super useful!

Can you use the graph! macro from a vec of nodes! macro ?

I'm trying to build a graph programmatically.

I tried to create a list of Stmt using a map like this:

    let nodes : Vec<_> = some_collection.map(|n| {
        let id = ...
        let label = ...
        node!(id; attr!("label",label))
    }).collect();

   let g = graph!(strict di id!("t"); nodes);

However it does not work, as node! returns () and not a Stmt.
Is there a way to do what I'm trying ?

Tests failing on macOS

Hi! Thanks for the wonderful package, but I have a problem on macOS. I tried to perform export to svg example and faced the error No such file or directory.

Then I ran your tests and faced the same error

running 24 tests
test attributes::tests::test ... ok
test parser::test::edge_test ... ok
test parser::test::attr_test ... ok
test parser::test::comments_test ... ok
test parser::test::attr_stmts_test ... ok
test parser::test::edge_stmt_test ... ok
test parser::test::attr_list_test ... ok
test parser::test::node_test ... ok
test parser::test::node_id_test ... ok
test printer::tests::attr_test ... ok
test parser::test::vertex_test ... ok
test printer::tests::edge_test ... ok
test parser::test::stmt_test ... ok
test printer::tests::graph_attr_test ... ok
test parser::test::id_test ... ok
test printer::tests::graph_test ... ok
test printer::tests::node_id_test ... ok
test printer::tests::node_test ... ok
test parser::test::graph_test ... ok
test printer::tests::subgraph_test ... ok
test parser::test::graph_html_test ... ok
test tests::parse_test ... ok
test tests::exec_test ... FAILED
test tests::print_test ... ok

failures:

---- tests::exec_test stdout ----
thread 'tests::exec_test' panicked at 'called `Result::unwrap()` on an `Err` value: Os { code: 2, kind: NotFound, message: "No such file or directory" }', src/lib.rs:194:12
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    tests::exec_test

test result: FAILED. 23 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s

I tried running it with sudo and it didn't help.

Accept labels without \" at the beginning and end

let id = i;
let label = format!(
    "\"data={:.4} grad={:.4} {}\"",
    value.borrow().data,
    value.borrow().grad,
    value.borrow().op.as_ref().unwrap_or(&"".to_string())
);
let node = stmt!(node!(id; attr!("shape", "box"), attr!("label", label)));

The above code works but if I take the \"'s away like so:

let id = i;
let label = format!(
    "data={:.4} grad={:.4} {}",
    value.borrow().data,
    value.borrow().grad,
    value.borrow().op.as_ref().unwrap_or(&"".to_string())
);
let node = stmt!(node!(id; attr!("shape", "box"), attr!("label", label)));

and try and save this graph to a file with

exec(
    graph,
    &mut PrinterContext::default(),
    vec![
        Format::Png.into(),
        CommandArg::Output("./graph2.png".to_string()),
    ],
)
.unwrap();
println!("saved to file");

I crash with

saving to file
thread 'main' panicked at src\graph_rust.rs:86:6:
called `Result::unwrap()` on an `Err` value: Custom { kind: Other, error: "Error: C:\\Users\\BRIOCH~1\\AppData\\Local\\Temp\\.tmpKczFpE: syntax error in line 2 near '='\r\n" }
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
error: process didn't exit successfully: `target\debug\micrograd-rust.exe` (exit code: 101)

This is intuitive and the displayed labels don't even display " at the beginning or end of my strings. I think them macro should encapsulate strings with " on it's own, instead of leaving it as an exercise for the library user.

Graph should be visible

The Graph enum and its fields should be visible.
For reasons where the printer interface does not satisfy more complex use-cases, e.g. interact with a graph database or such stuff

DotPrinter is adding an unneeded space after edges and before the semi-colon when there are no edge attributes.

Hi,

I noticed after enabling semi-colons the DotPrinter is adding an uneeded space after edges and before semicolons when there are no edge attributes.

Current Output Snippet:

    node1 -> node2 [label=TRUE];
    node2 -> node3 [label=FALSE];
    node3 -> node4 ;
    node4 -> node5 ;
    node5 -> node6 ;
    node4 -> node5 ;

Expected Results:

    node1 -> node2 [label=TRUE];
    node2 -> node3 [label=FALSE];
    node3 -> node4;
    node4 -> node5;
    node5 -> node6;
    node4 -> node5;

Change the way of parsing GraphAttributes

The structure of parsing GraphAttributes follows the notation but the fact that the seq of extra elements following after the node is concealed:

graph {
node[style=filled]; a b c d;
}

Therefore now it is being parsed as a set of independent statements but apparently, they have to be merged.

Dot parser is forbidding characters it should not within the "double quoted string" variant of IDs.

The parser is forbidding many characters it should not within the "double quoted string" variant of IDs.

FROM https://graphviz.org/doc/info/lang.html:

An ID is one of the following:

  • Any string of alphabetic ([a-zA-Z\200-\377]) characters, underscores ('_') or digits([0-9]), not beginning with a digit;
  • a numeral [-]?(.[0-9]⁺ | [0-9]⁺(.[0-9]*)? );
  • any double-quoted string ("...") possibly containing escaped quotes (")¹;
  • an HTML string (<...>).
    ...
  1. In quoted strings in DOT, the only escaped character is double-quote ". That is, in quoted strings, the dyad \" is converted to " all other characters are left unchanged. In particular, \ remains \. Layout engines may apply additional escape sequences.

--

This graph doesn't parse yet it is valid dot.

digraph graph_test {
  start [
    label="This shouln't error"
  ];
  end [
    label="End"
  ];
  start-> end;
} 
 --> 4:11
  |
4 |     label="This shouln't error"
  |           ^---
  |
  = expected id

--- it looks like the issue is here:

char = _{
    !("\"" | "\\" | "\'") ~ ANY
    | "\\" ~ ("\"" | "\'" |  "\\" | "/" | "b" | "f" | "n" | "r" | "t")
    // TODO: the following option is only valid for some attributes: https://graphviz.org/docs/attr-types/escString/
    | "\\" ~ ("G" | "N" | "E" | "H" | "T" | "L")
    | "\\" ~ ("u" ~ ASCII_HEX_DIGIT{4})
}

Node ports starting with a "compass letter" breaks parsing

The following graph is not parsed correctly:

digraph "test" {
    A:s0 -> B;
}

It's parsed as

DiGraph {
    id: Escaped("\"test\""), strict: false,
    stmts: [
        Node(Node { id: NodeId(Plain("A"), Some(Port(None, Some("s")))), attributes: [] }),
        Edge(Edge { ty: Pair(N(NodeId(Plain("0"), None)), N(NodeId(Plain("B"), None))), attributes: [] })
    ]
 }

while I would expect

DiGraph {
    id: Escaped("\"test\""), strict: false,
    stmts: [
        Edge(Edge { ty: Pair(N(NodeId(Plain("A"), Some(Port(Some(Plain("s0")), None)))), N(NodeId(Plain("B"), None))), attributes: [] })
    ]
}

I'm able to fix this by swapping the parsing expressions for port

port = {(":" ~ compass) | (":" ~ id ~ (":" ~ compass)?) }

to

port = { (":" ~ id ~ (":" ~ compass)?) | (":" ~ compass) }

but this seems to fail to parse actual "compass" nodes (e.g. nodeId:n, nodeId:s, etc).

png parsing

I am trying to generate a png from a graph and am running into a problem.
The exec function returns Option and it looks like String cannot properly represent the contents of a png file.
When I write the contents of the string to a file, it is not a valid png file.

let graph_png = graphviz_rust::exec(
                self.graph.clone(),
                &mut PrinterContext::default(),
                vec![graphviz_rust::cmd::Format::Png.into()],
            )
            .unwrap()
            
            .as_bytes()
            .to_vec();
std::fs::write("./generated.png", &graph_png).unwrap();

Can’t handle escStrings and attribute names starting with “_” (e.g. the `GraphAttributes` in a simple graph’s `dot`/`xdot` output)

I created a simple test binary, but parsing fails:

use graphviz_rust as gv;
use color_eyre::{self, eyre::{Result, Report}};
use gv::{printer::PrinterContext, cmd::{CommandArg, Layout}};

const TEST: &'static str = "graph { a -- b }";

fn main() -> Result<()> {
    color_eyre::install()?;
    let graph = gv::parse(TEST).map_err(Report::msg)?;
    let mut ctx = PrinterContext::default();
    let layed_out = gv::exec(graph, &mut ctx, vec![CommandArg::Layout(Layout::Dot)])?;
    println!("{}", &layed_out);
    let graph = gv::parse(&layed_out).map_err(Report::msg)?;
    gv::print(graph, &mut ctx);
    Ok(())
}

This prints the following Layout output:

graph {
        graph [bb="0,0,54,108"];
        node [label="\N"];
        a       [height=0.5,
                pos="27,90",
                width=0.75];
        b       [height=0.5,
                pos="27,18",
                width=0.75];
        a -- b  [pos="27,71.697 27,60.846 27,46.917 27,36.104"];
}

And then fails to parse the generated node statement:

Error: 
   0:  --> 3:14
        |
      3 |       node [label="\N"];
        |                   ^---
        |
        = expected id

Trying to parse Xdot output fails even earlier:

Error: 
   0:  --> 2:9
        |
      2 |       graph [_draw_="c 9 -#fffffe00 C 7 -#ffffff P 4 0 0 0 108 54 108 54 0 ",
        |              ^---
        |
        = expected id

(obtained by adding , CommandArg::Format(Format::Xdot) to the above exec call`

Error when parsing comment after final closing bracket

I have a graph that I want to parse which ends like this:

   node_425887->node_425122
} // end G

And I get a parsing error:
thread 'main' panicked at 'called Result::unwrap() on an Err value: " --> 8685:3\n |\n8685 | } // end G\n | ^---\n |\n = expected EOI"', src/main.rs:7:62

Provide example usage of SubgraphAttributes

let subgraph_nodes: Vec<Stmt> = //...
subgraph!(subgraph_id.as_u128(), subgraph_nodes)

Works for me, but my subgraphs have no color or border. I want to assign SubgraphAttributes::label in order to attempt to make the subgraphs visible. What is the valid syntax for that?

dot pest update

Hey!
First of all, thanks for the awesome library!
I am trying to create a uml diagram for my db and have issues with the parse function specifically on the code
<tr><td>address_id: int</td></tr>
I suspect its the dot.pest file the needs to be updated to handle special characters like : or _
thanks

Add support for printing nodes with multiple attributes on separate lines.

I would like to add support for printing attributes for nodes with multiple attributes on separate lines.

I will submit a PR for this shortly. Thanks for your consideration!

For example:

digraph multi {
  a[shape=square]
  aa[
    color=blue,
    shape=Mrecord
  ]
  subgraph v {
    aaa[shape=square]
    aaaa[
      color=red,
      shape=Mrecord
    ]
    aaa -> aaaa [label=FALSE]
  }
  a -> aa [label=TRUE,color=green]
}

image

DotPrinter is adding an extra level of indentation on all statements

Hello,

The DotPrinter is adding an extra level of indentation for all statements of a graph. The default indentation is 2 spaces but statements have 4 spaces before them. I created issue #29 for the floating semi-colons.

With the following PrinterContext I am getting the below example output.

let flow_source = graph.print(PrinterContext::default().with_semi());

Example Output:

digraph graph_id {
    node1[label="Label1"shape="Mrecord"];
    node2[label="Label2",shape="Mrecord"];
    node3[label="Label3",shape="Mrecord"];
    node1 -> node2 ;
    node2 -> node3 ;    
}

Expected Output:

digraph graph_id {
  node1[label="Label1"shape="Mrecord"];
  node2[label="Label2",shape="Mrecord"];
  node3[label="Label3",shape="Mrecord"];
  node1 -> node2;
  node2 -> node3;    
}

I've implemented a fix for this issue, but I'd prefer to get #29 fixed before I create the a PR for this issue.

Update the docs for the macroses

Up until now, the docs are either absent or misleading.
WE need to update the docs for the macroses to make it more readable.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.