gobby-code 1.3.3

Fast Rust CLI for Gobby's code index — AST-aware search, symbol navigation, and dependency graph
Documentation
use std::collections::BTreeMap;

use super::types::{
    BridgeReportSummary, GraphHotspot, GraphReportHotspots, GraphReportSummary, ReportDegradation,
    TargetFrequency,
};

pub(super) struct RenderMarkdownInput<'a> {
    pub(super) project_id: &'a str,
    pub(super) generated_at: &'a str,
    pub(super) summary: &'a GraphReportSummary,
    pub(super) hotspots: &'a GraphReportHotspots,
    pub(super) unresolved_targets: &'a [TargetFrequency],
    pub(super) external_targets: &'a [TargetFrequency],
    pub(super) bridge_summary: Option<&'a BridgeReportSummary>,
    pub(super) degradation_details: &'a [ReportDegradation],
    pub(super) top_n: usize,
}

pub(super) fn render_markdown(input: RenderMarkdownInput<'_>) -> String {
    let mut lines = vec![
        "# Project Graph Report".to_string(),
        String::new(),
        format!("- Project: {}", inline_code(input.project_id)),
        format!("- Generated: {}", inline_code(input.generated_at)),
        format!("- Nodes: {}", input.summary.node_count),
        format!("- Edges: {}", input.summary.edge_count),
    ];

    if !input.summary.code_edge_counts.is_empty() {
        lines.push(format!(
            "- Code edges: {}",
            named_counts_inline(&input.summary.code_edge_counts)
        ));
    }

    append_hotspot_section(
        &mut lines,
        "High-degree files",
        &input.hotspots.high_degree_files,
        input.top_n,
    );
    append_hotspot_section(
        &mut lines,
        "High-degree modules",
        &input.hotspots.high_degree_modules,
        input.top_n,
    );
    append_hotspot_section(
        &mut lines,
        "High-degree symbols",
        &input.hotspots.high_degree_symbols,
        input.top_n,
    );
    append_hotspot_section(
        &mut lines,
        "Incoming-call hotspots",
        &input.hotspots.incoming_call_hotspots,
        input.top_n,
    );
    append_target_section(
        &mut lines,
        "Unresolved call targets",
        input.unresolved_targets,
        input.top_n,
    );
    append_target_section(
        &mut lines,
        "External call targets",
        input.external_targets,
        input.top_n,
    );

    if let Some(summary) = input.bridge_summary {
        lines.push(String::new());
        lines.push("## RELATES_TO_CODE bridges".to_string());
        lines.push(format!(
            "- {} inferred read-only edge(s)",
            summary.edge_count
        ));
        if let Some(range) = &summary.confidence_range {
            lines.push(format!("- Confidence: {:.3}..{:.3}", range.min, range.max));
        }
    }

    if !input.degradation_details.is_empty() {
        lines.push(String::new());
        lines.push("## Degradation".to_string());
        for detail in input.degradation_details {
            lines.push(format!(
                "- {}: {}",
                inline_code(&detail.input),
                markdown_text(&detail.detail)
            ));
        }
    }

    lines.join("\n")
}

fn append_hotspot_section(
    lines: &mut Vec<String>,
    title: &str,
    hotspots: &[GraphHotspot],
    top_n: usize,
) {
    if top_n == 0 || hotspots.is_empty() {
        return;
    }
    lines.push(String::new());
    lines.push(format!("## {title}"));
    for hotspot in hotspots.iter().take(top_n) {
        lines.push(format!(
            "- {} (degree {}, in {}, out {})",
            inline_code(&hotspot.name),
            hotspot.degree,
            hotspot.incoming,
            hotspot.outgoing
        ));
    }
}

fn append_target_section(
    lines: &mut Vec<String>,
    title: &str,
    targets: &[TargetFrequency],
    top_n: usize,
) {
    if top_n == 0 || targets.is_empty() {
        return;
    }
    lines.push(String::new());
    lines.push(format!("## {title}"));
    for target in targets.iter().take(top_n) {
        lines.push(format!(
            "- {} ({})",
            inline_code(&target.name),
            target.count
        ));
    }
}

fn inline_code(value: &str) -> String {
    let delimiter = "`".repeat(max_backtick_run(value).saturating_add(1));
    if value.starts_with('`') || value.ends_with('`') {
        format!("{delimiter} {value} {delimiter}")
    } else {
        format!("{delimiter}{value}{delimiter}")
    }
}

fn max_backtick_run(value: &str) -> usize {
    let mut max_run = 0usize;
    let mut current_run = 0usize;
    for ch in value.chars() {
        if ch == '`' {
            current_run += 1;
            max_run = max_run.max(current_run);
        } else {
            current_run = 0;
        }
    }
    max_run
}

fn markdown_text(value: &str) -> String {
    value
        .replace('\\', "\\\\")
        .replace('`', "\\`")
        .replace('*', "\\*")
        .replace('_', "\\_")
        .replace('[', "\\[")
        .replace(']', "\\]")
        .replace('<', "\\<")
        .replace('>', "\\>")
        .replace('\n', " ")
}

fn named_counts_inline(counts: &BTreeMap<String, usize>) -> String {
    counts
        .iter()
        .map(|(name, count)| format!("{name}={count}"))
        .collect::<Vec<_>>()
        .join(", ")
}