Rust图形生成库dot-generator的使用,dot-generator可高效生成DOT语言描述的图形结构

Rust图形生成库dot-generator的使用

dot-generator是一个高效的Rust库,用于生成DOT语言描述的图形结构。DOT语言是Graphviz工具使用的图形描述语言,广泛用于可视化各种图形结构。

安装

在项目目录中运行以下Cargo命令:

cargo add dot-generator

或者在Cargo.toml中添加:

dot-generator = "0.2.0"

使用示例

简单图形示例

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

fn main() {
    // 创建一个有向图
    let graph = graph!(strict di id!("example");
        node!("a"; attr!("shape", "circle")),
        node!("b"; attr!("shape", "square")),
        edge!(node_id!("a") => node_id!("b")),
        stmt_list!(
            attr!("rankdir", "LR")
        )
    );
    
    // 将图转换为DOT语言字符串
    let dot_string = graph.to_string();
    println!("{}", dot_string);
}

输出结果:

strict digraph example {
    rankdir=LR;
    a [shape=circle];
    b [shape=square];
    a -> b;
}

复杂图形示例

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

fn main() {
    let graph = graph!(strict di id!("network");
        node!("router"; 
            attr!("shape", "box"),
            attr!("color", "red"),
            attr!("label", "Main Router")
        ),
        node!("server1"; 
            attr!("shape", "ellipse"),
            attr!("color", "blue"),
            attr!("label", "Web Server")
        ),
        node!("server2"; 
            attr!("shape", "ellipse"),
            attr!("color", "green"),
            attr!("label", "DB Server")
        ),
        edge!(node_id!("router") => node_id!("server1"); 
            attr!("label", "100Mbps"),
            attr!("color", "gray")
        ),
        edge!(node_id!("router") => node_id!("server2"); 
            attr!("label", "1Gbps"),
            attr!("color", "black")
        ),
        stmt_list!(
            attr!("rankdir", "TB"),
            attr!("label", "Network Diagram"),
            attr!("labelloc", "t")
        )
    );
    
    println!("{}", graph.to_string());
}

主要特性

  1. 类型安全:所有图形元素都使用Rust类型系统进行验证
  2. 流畅的API:使用宏提供简洁的构建语法
  3. 高效:生成DOT语言描述非常高效
  4. 灵活:支持Graphviz的所有主要功能

完整示例Demo

状态机示例

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

fn main() {
    // 创建一个状态机图
    let state_machine = graph!(di id!("state_machine");
        // 定义状态节点
        node!("Idle"; 
            attr!("shape", "ellipse"),
            attr!("color", "blue"),
            attr!("label", "Idle State")
        ),
        node!("Processing"; 
            attr!("shape", "ellipse"),
            attr!("color", "green"),
            attr!("label", "Processing State")
        ),
        node!("Error"; 
            attr!("shape", "doublecircle"),
            attr!("color", "red"),
            attr!("label", "Error State")
        ),
        
        // 定义状态转移边
        edge!(node_id!("Idle") => node_id!("Processing");
            attr!("label", "on_start"),
            attr!("color", "black")
        ),
        edge!(node_id!("Processing") => node_id!("Idle");
            attr!("label", "on_complete"),
            attr!("color", "green")
        ),
        edge!(node_id!("Processing") => node_id!("Error");
            attr!("label", "on_error"),
            attr!("color", "red"),
            attr!("style", "dashed")
        ),
        edge!(node_id!("Error") => node_id!("Idle");
            attr!("label", "on_reset"),
            attr!("color", "blue")
        ),
        
        // 图属性
        stmt_list!(
            attr!("rankdir", "LR"),
            attr!("label", "State Machine Diagram"),
            attr!("labelloc", "t")
        )
    );
    
    println!("{}", state_machine.to_string());
}

这个完整示例展示了如何使用dot-generator创建状态机图,包含三种状态和四种状态转移条件,并添加了丰富的属性设置。


1 回复

Rust图形生成库dot-generator使用指南

介绍

dot-generator是一个Rust库,用于高效生成DOT语言描述的图形结构。DOT是一种图形描述语言,常用于可视化图形结构,可以被Graphviz等工具渲染成图片。这个库提供了类型安全的方式来构建图形,避免了手动拼接字符串的麻烦。

安装

在Cargo.toml中添加依赖:

[dependencies]
dot-generator = "0.1"
dot = "0.1"  # 可选,用于输出DOT格式

基本用法

创建简单图形

use dot_generator::*;
use dot::*;

fn main() {
    // 创建一个有向图
    let graph = graph::digraph()
        .set(graph::id("G"))  // 设置图形ID
        .edge(node_id("a"), node_id("b"))  // 添加边a->b
        .edge(node_id("b"), node_id("c"))  // 添加边b->c
        .edge(node_id("c"), node_id("a")); // 添加边c->a
    
    println!("{}", graph.to_dot());  // 输出DOT格式
}

输出DOT格式:

digraph G {
    a -> b;
    b -> c;
    c -> a;
}

添加属性

use dot_generator::*;
use dot::*;

fn main() {
    // 创建带有属性的图形
    let graph = graph::digraph()
        .set(graph::id("example"))
        .set(graph::attr("rankdir", "LR"))  // 设置图形属性:从左到右布局
        .node(
            node_id("start")
                .attr("shape", "box")    // 设置节点形状
                .attr("color", "red")    // 设置节点颜色
        )
        .node(
            node_id("end")
                .attr("shape", "ellipse")
                .attr("color", "blue")
        )
        .edge(
            edge(node_id("start"), node_id("end"))
                .attr("label", "process")  // 设置边标签
                .attr("style", "dashed")   // 设置边样式
        );
    
    println!("{}", graph.to_dot());
}

创建子图

use dot_generator::*;
use dot::*;

fn main() {
    // 创建包含子图的图形
    let graph = graph::digraph()
        .set(graph::id("cluster_example"))
        .subgraph(
            subgraph("cluster_a")
                .set(graph::attr("label", "Process A"))  // 设置子图标签
                .node(node_id("a1"))
                .node(node_id("a2"))
                .edge(node_id("a1"), node_id("a2"))  // 子图内部边
        )
        .subgraph(
            subgraph("cluster_b")
                .set(graph::attr("label", "Process B"))
                .node(node_id("b1"))
                .node(node_id("b2"))
                .edge(node_id("b1"), node_id("b2"))
        )
        .edge(node_id("a1"), node_id("b2"));  // 子图之间的边
    
    println!("{}", graph.to_dot());
}

高级特性

使用宏简化创建

use dot_generator::*;
use dot::*;

fn main() {
    // 使用宏简化图形创建
    let graph = digraph!("G" => {
        rankdir = "LR";
        node!("start" => {
            shape = "box";
            color = "red";
        });
        node!("end" => {
            shape = "ellipse";
            color = "blue";
        });
        edge!("start" -> "end" => {
            label = "process";
            style = "dashed";
        });
    });
    
    println!("{}", graph.to_dot());
}

从数据结构生成图形

use dot_generator::*;
use dot::*;

// 定义节点结构体
struct Node {
    id: String,
    label: String,
}

// 定义边结构体
struct Edge {
    from: String,
    to: String,
    relation: String,
}

// 从数据结构生成DOT图形
fn generate_graph(nodes: Vec<Node>, edges: Vec<Edge>) -> String {
    let mut graph = graph::digraph().set(graph::id("data_structure"));
    
    // 添加所有节点
    for node in nodes {
        graph = graph.node(
            node_id(&node.id)
                .attr("label", &node.label)
        );
    }
    
    // 添加所有边
    for edge in edges {
        graph = graph.edge(
            edge(node_id(&edge.from), node_id(&edge.to))
                .attr("label", &edge.relation)
        );
    }
    
    graph.to_dot()
}

fn main() {
    let nodes = vec![
        Node { id: "a".to_string(), label: "Node A".to_string() },
        Node { id: "b".to_string(), label: "Node B".to_string() },
    ];
    
    let edges = vec![
        Edge { from: "a".to_string(), to: "b".to_string(), relation: "connects".to_string() },
    ];
    
    println!("{}", generate_graph(nodes, edges));
}

输出到文件

use std::fs::File;
use std::io::Write;
use dot_generator::*;
use dot::*;

fn main() -> std::io::Result<()> {
    // 创建简单图形
    let graph = graph::digraph()
        .set(graph::id("file_example"))
        .edge(node_id("a"), node_id("b"))
        .edge(node_id("b"), node_id("c"));
    
    // 将图形写入文件
    let mut file = File::create("example.dot")?;
    file.write_all(graph.to_dot().as_bytes())?;
    
    Ok(())
}

完整示例

下面是一个结合了多种特性的完整示例:

use dot_generator::*;
use dot::*;
use std::fs::File;
use std::io::Write;

// 定义项目结构
struct Project {
    name: String,
    modules: Vec<Module>,
}

struct Module {
    name: String,
    functions: Vec<String>,
}

fn generate_project_graph(project: Project) -> String {
    let mut graph = graph::digraph()
        .set(graph::id(&project.name))
        .set(graph::attr("rankdir", "TB"))
        .set(graph::attr("compound", "true"));
    
    // 为每个模块创建子图
    for module in project.modules {
        let cluster_name = format!("cluster_{}", module.name);
        
        graph = graph.subgraph(
            subgraph(&cluster_name)
                .set(graph::attr("label", &module.name))
                .set(graph::attr("style", "filled"))
                .set(graph::attr("color", "lightgrey"))
        );
        
        // 添加模块中的函数节点
        for func in module.functions {
            let node_id = format!("{}_{}", module.name, func);
            graph = graph.node(
                node_id(&node_id)
                    .attr("label", &func)
                    .attr("shape", "box")
            );
        }
    }
    
    // 添加模块间的依赖关系
    graph = graph.edge(
        edge(node_id("core_initialize"), node_id("network_connect"))
            .attr("label", "uses")
            .attr("style", "dashed")
    );
    
    graph.to_dot()
}

fn main() -> std::io::Result<()> {
    // 创建项目结构
    let project = Project {
        name: "RustProject".to_string(),
        modules: vec![
            Module {
                name: "core".to_string(),
                functions: vec!["initialize".to_string(), "shutdown".to_string()],
            },
            Module {
                name: "network".to_string(),
                functions: vec!["connect".to_string(), "disconnect".to_string()],
            },
        ],
    };
    
    // 生成图形
    let dot_output = generate_project_graph(project);
    
    // 输出到控制台
    println!("{}", dot_output);
    
    // 写入文件
    let mut file = File::create("project.dot")?;
    file.write_all(dot_output.as_bytes())?;
    
    Ok(())
}

注意事项

  1. 生成的DOT文件可以使用Graphviz工具渲染成图片:

    dot -Tpng example.dot -o example.png
    
  2. 对于大型图形,考虑使用graph::strict()创建严格图形,可以避免重复边

  3. 节点ID必须是有效的DOT标识符,如果包含特殊字符,需要使用引号包裹

  4. 使用子图(cluster)时,子图名称必须以"cluster_"开头才能被Graphviz识别为集群

dot-generator提供了类型安全的方式来构建图形结构,避免了手动拼接字符串可能带来的错误,是生成DOT图形的理想选择。

回到顶部