acp/commands/
bridge.rs

1//! @acp:module "Bridge Command"
2//! @acp:summary "RFC-0006: Documentation bridging status and management"
3//! @acp:domain cli
4//! @acp:layer handler
5//!
6//! Implements `acp bridge` command for managing documentation bridging.
7
8use anyhow::Result;
9use console::style;
10use serde::Serialize;
11
12use crate::cache::Cache;
13use crate::config::Config;
14
15/// Subcommands for the bridge command
16#[derive(Debug, Clone)]
17pub enum BridgeSubcommand {
18    /// Show bridging configuration and statistics
19    Status { json: bool },
20}
21
22/// Options for the bridge command
23#[derive(Debug, Clone)]
24pub struct BridgeOptions {
25    /// Cache file path
26    pub cache: std::path::PathBuf,
27    /// Subcommand to execute
28    pub subcommand: BridgeSubcommand,
29}
30
31/// Status output for JSON format
32#[derive(Debug, Serialize)]
33#[serde(rename_all = "camelCase")]
34struct BridgeStatusJson {
35    enabled: bool,
36    precedence: String,
37    summary: BridgeSummaryJson,
38    by_format: std::collections::HashMap<String, u64>,
39}
40
41#[derive(Debug, Serialize)]
42#[serde(rename_all = "camelCase")]
43struct BridgeSummaryJson {
44    total_annotations: u64,
45    explicit_count: u64,
46    converted_count: u64,
47    merged_count: u64,
48}
49
50/// Execute the bridge command
51pub fn execute_bridge(options: BridgeOptions, config: Config) -> Result<()> {
52    match options.subcommand {
53        BridgeSubcommand::Status { json } => execute_status(&options.cache, &config, json),
54    }
55}
56
57/// Execute the bridge status subcommand
58fn execute_status(cache_path: &std::path::Path, config: &Config, json_output: bool) -> Result<()> {
59    // Load cache if it exists
60    let cache = if cache_path.exists() {
61        Some(Cache::from_json(cache_path)?)
62    } else {
63        None
64    };
65
66    if json_output {
67        output_status_json(config, cache.as_ref())
68    } else {
69        output_status_text(config, cache.as_ref())
70    }
71}
72
73/// Output status in JSON format
74fn output_status_json(config: &Config, cache: Option<&Cache>) -> Result<()> {
75    let status = if let Some(cache) = cache {
76        BridgeStatusJson {
77            enabled: config.bridge.enabled,
78            precedence: config.bridge.precedence.to_string(),
79            summary: BridgeSummaryJson {
80                total_annotations: cache.bridge.summary.total_annotations,
81                explicit_count: cache.bridge.summary.explicit_count,
82                converted_count: cache.bridge.summary.converted_count,
83                merged_count: cache.bridge.summary.merged_count,
84            },
85            by_format: cache.bridge.by_format.clone(),
86        }
87    } else {
88        BridgeStatusJson {
89            enabled: config.bridge.enabled,
90            precedence: config.bridge.precedence.to_string(),
91            summary: BridgeSummaryJson {
92                total_annotations: 0,
93                explicit_count: 0,
94                converted_count: 0,
95                merged_count: 0,
96            },
97            by_format: std::collections::HashMap::new(),
98        }
99    };
100
101    println!("{}", serde_json::to_string_pretty(&status)?);
102    Ok(())
103}
104
105/// Output status in human-readable format
106fn output_status_text(config: &Config, cache: Option<&Cache>) -> Result<()> {
107    println!("{}", style("Bridge Configuration:").bold());
108    println!(
109        "  Enabled:    {}",
110        if config.bridge.enabled {
111            style("yes").green()
112        } else {
113            style("no").yellow()
114        }
115    );
116    println!("  Precedence: {}", config.bridge.precedence);
117    println!("  Strictness: {:?}", config.bridge.strictness);
118    println!();
119
120    println!("{}", style("Language Support:").bold());
121    println!(
122        "  JSDoc/TSDoc: {}",
123        if config.bridge.jsdoc.enabled {
124            style("enabled").green()
125        } else {
126            style("disabled").dim()
127        }
128    );
129    println!(
130        "  Python:      {}",
131        if config.bridge.python.enabled {
132            style("enabled").green()
133        } else {
134            style("disabled").dim()
135        }
136    );
137    if config.bridge.python.enabled {
138        println!("    Style: {:?}", config.bridge.python.docstring_style);
139    }
140    println!(
141        "  Rust:        {}",
142        if config.bridge.rust.enabled {
143            style("enabled").green()
144        } else {
145            style("disabled").dim()
146        }
147    );
148    println!();
149
150    if let Some(cache) = cache {
151        let total = cache.bridge.summary.total_annotations;
152        if total > 0 {
153            println!("{}", style("Statistics:").bold());
154            println!("  Total annotations: {}", total);
155
156            let explicit = cache.bridge.summary.explicit_count;
157            let converted = cache.bridge.summary.converted_count;
158            let merged = cache.bridge.summary.merged_count;
159
160            println!(
161                "    Explicit (ACP only):     {:>5} ({:>5.1}%)",
162                explicit,
163                (explicit as f64 / total as f64) * 100.0
164            );
165            println!(
166                "    Converted (from native): {:>5} ({:>5.1}%)",
167                converted,
168                (converted as f64 / total as f64) * 100.0
169            );
170            println!(
171                "    Merged (ACP + native):   {:>5} ({:>5.1}%)",
172                merged,
173                (merged as f64 / total as f64) * 100.0
174            );
175            println!();
176
177            if !cache.bridge.by_format.is_empty() {
178                println!("{}", style("By Format:").bold());
179                for (format, count) in &cache.bridge.by_format {
180                    println!("  {}: {}", format, count);
181                }
182            }
183        } else {
184            println!("{}", style("Statistics:").bold());
185            println!("  No bridged annotations found.");
186            if !config.bridge.enabled {
187                println!();
188                println!(
189                    "  {} Run `acp index --bridge` to enable bridging",
190                    style("Hint:").cyan()
191                );
192            }
193        }
194    } else {
195        println!("{}", style("Statistics:").bold());
196        println!("  Cache not found. Run `acp index` first.");
197    }
198
199    Ok(())
200}