nornir 0.4.22

Companion to cargo: dependency tracking, release gating, deploy, benchmarks, and documentation assembly. Project-agnostic.
Documentation
//! Code introspection — derive structural docs from the *built* code.
//!
//! All extraction targets the compiled / parsed artifact, never
//! hand-maintained sources. The published documentation is then a
//! pure function of the code:
//!
//! ```text
//!     source code  ──cargo──►  rustdoc JSON ┐
//!     workspace    ──cargo──►  metadata     ├─► nornir::introspect ─► graphs
//!     src/**/*.rs  ──syn ───►  AST          ┘                       │
//!//!                                                             nornir::docs
//!//!//!                                                    README / CLAUDE / docs/*
//! ```

pub mod api;
pub mod artifact;
pub mod callgraph;
pub mod callgraph_dwarf;
pub mod callgraph_llvm;
pub mod depgraph;
pub mod persist;

use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Edge {
    pub from: String,
    pub to: String,
    pub kind: EdgeKind,
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
pub enum EdgeKind {
    Calls,
    DependsOn,
    Reexports,
    Implements,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Graph {
    pub nodes: Vec<String>,
    pub edges: Vec<Edge>,
}

impl Graph {
    pub fn to_mermaid(&self) -> String {
        let mut s = String::from("```mermaid\ngraph LR\n");
        for n in &self.nodes {
            s.push_str(&format!("  {}\n", sanitize(n)));
        }
        for e in &self.edges {
            let arrow = match e.kind {
                EdgeKind::Calls => "-->",
                EdgeKind::DependsOn => "-.->",
                EdgeKind::Reexports => "==>",
                EdgeKind::Implements => "--o",
            };
            s.push_str(&format!("  {} {} {}\n", sanitize(&e.from), arrow, sanitize(&e.to)));
        }
        s.push_str("```\n");
        s
    }
}

fn sanitize(s: &str) -> String {
    s.replace([':', '-', '.', '/'], "_")
}