graphviz-rust icon indicating copy to clipboard operation
graphviz-rust copied to clipboard

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

Open phtrivier opened this issue 1 year ago • 3 comments

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 ?

phtrivier avatar Mar 23 '24 13:03 phtrivier

Hi there!

The given code should be working fine but with two small changes.

  1. The macros graph! expects a collection of statements and Node is only one subtype of the statement thus the node in nodes needs to be wrapped with stmt! like from the aforementioned example:
 stmt!(node!(id; attr!("label",label)))

Another change regards the graph! macros itself. It can accept either a collection of statements or varargs of statements:

graph!(strict di id!("t"), stmts);  //<- collection
graph!(strict di id!("t"); stmt1, stmt2,stmt3); // <- varargs

As you can see, the first example has the sep as a comma and the second is a semicolon. The complete example:

       let nodes:Vec<_> = vec![(1,"a"),(2,"b")].iter().map(|(id,label)| {
            stmt!(node!(id; attr!("label",label))) // wrapped with stmt!
        }).collect();

        let g = graph!(strict di id!("t"), nodes);  // changed to comma 
        let res = g.print(&mut PrinterContext::default());
        print!("{}",res)

which constructs the following graph:

strict digraph t {
  1[label=a]
  2[label=b]
}

I will update the documentation for the macros to make it more explicit.

besok avatar Mar 23 '24 15:03 besok

Thanks, this work ! However, I keep getting into issues where my labels seem to be invalid, and breaks the generation. So, two follow-up:

  • is there a way to print the generated .dot to check where are errors ?
  • are there guidelines for generating / escaping node labels ?

For example, this works:

node!("x"; attr!("label", "add"))

But this breaks:

node!("x"; attr!("label", "+"))
called `Result::unwrap()` on an `Err` value: Custom { kind: Other, error: "Error: /tmp/.tmp0bKQlI: syntax error in line 2 near '+'\n" }
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

I generate svg with :

let mut context = PrinterContext::default();
    exec(
        g,
        &mut context,
        vec![
            Format::Svg.into(),
            CommandArg::Output("graph.svg".to_string()),
        ],
    ).unwrap();

phtrivier avatar Mar 23 '24 19:03 phtrivier

Yeah, that is a valid point. I have created an issue to provide the docs for the macroses but for now, you can find some examples in the sources here

Speaking about questions:

is there a way to print the generated .dot to check where are errors ?

Yeah, sure. As I showed in the example previously you can just invoke print from the graph before the dot gets generated:

let g = graph!(strict di id!("t"), nodes);
let res = g.print(&mut PrinterContext::default());
print!("{}",res)

are there guidelines for generating / escaping node labels ?

Again, unfortunately, I did not add the docs for macroses and therefore the only way is to scrutinize the the file

Also, You always can ask me here and I can try to look into.

For example, if you want to escape something you can use the prefix esc in macros like that:

    #[test]
    fn issue_with_node_test() {
        let nodes:Vec<_> = vec![(1,"a"),(2,"+")].iter().map(|(id,label)| {
            stmt!(node!(id; attr!("label", esc label))) // <- use esc for escaping strings or html for add html tags 
        }).collect();

        let g = graph!(strict di id!("t"), nodes);
        let res = g.print(&mut PrinterContext::default());
        print!("{}",res)
    }

that will give :

strict digraph t {
  1[label="-"]
  2[label="+"]
}

besok avatar Mar 23 '24 20:03 besok