pub mod dot;
pub mod evidence_formatter;
pub mod formatters;
pub mod json;
pub mod llm_markdown;
pub mod markdown;
pub mod pattern_analysis;
pub mod pattern_formatter;
pub mod terminal;
pub mod unified;
use crate::io::view_formatters;
use crate::priority::tiers::TierConfig;
use crate::priority::view::{PreparedDebtView, SortCriteria, ViewConfig};
use crate::priority::view_pipeline;
use crate::{core::AnalysisResults, formatting::FormattingConfig, io, priority, risk};
use anyhow::Result;
use std::path::PathBuf;
pub struct OutputConfig {
pub top: Option<usize>,
pub tail: Option<usize>,
pub summary: bool,
pub verbosity: u8,
pub output_file: Option<PathBuf>,
pub output_format: Option<crate::cli::OutputFormat>,
pub formatting_config: FormattingConfig,
pub show_filter_stats: bool,
}
pub fn output_with_prepared_view(
analysis: &priority::UnifiedAnalysis,
config: &OutputConfig,
) -> Result<()> {
let view_config = build_view_config(config);
let tier_config = TierConfig::default();
let view = view_pipeline::prepare_view(analysis, &view_config, &tier_config);
output_prepared_view(&view, config)
}
fn output_prepared_view(view: &PreparedDebtView, config: &OutputConfig) -> Result<()> {
match &config.output_format {
Some(crate::cli::OutputFormat::Json) => {
let include_scoring_details = config.verbosity >= 2;
let json = view_formatters::format_json(view, include_scoring_details);
write_output(&json, &config.output_file)
}
Some(crate::cli::OutputFormat::Markdown) => {
let md_config = view_formatters::MarkdownConfig {
verbosity: config.verbosity,
show_filter_stats: config.show_filter_stats,
};
let markdown = view_formatters::format_markdown(view, &md_config);
write_output(&markdown, &config.output_file)
}
_ => {
if is_markdown_file(&config.output_file) {
let md_config = view_formatters::MarkdownConfig {
verbosity: config.verbosity,
show_filter_stats: config.show_filter_stats,
};
let markdown = view_formatters::format_markdown(view, &md_config);
write_output(&markdown, &config.output_file)
} else {
let term_config = view_formatters::TerminalConfig {
verbosity: config.verbosity,
use_color: config.formatting_config.color.should_use_color(),
summary_mode: config.summary,
};
let terminal = view_formatters::format_terminal(view, &term_config);
write_output(&terminal, &config.output_file)
}
}
}
}
fn build_view_config(config: &OutputConfig) -> ViewConfig {
let limit = match (config.top, config.tail) {
(Some(n), _) => Some(n),
(_, Some(n)) => Some(n),
_ => None,
};
let min_score_threshold = std::env::var("DEBTMAP_MIN_SCORE_THRESHOLD")
.ok()
.and_then(|s| s.parse::<f64>().ok())
.unwrap_or(3.0);
ViewConfig {
min_score_threshold,
exclude_t4_maintenance: true, limit,
sort_by: SortCriteria::Score, compute_groups: false, }
}
fn write_output(content: &str, output_file: &Option<PathBuf>) -> Result<()> {
use crate::progress::ProgressManager;
if let Some(pm) = ProgressManager::global() {
let _ = pm.clear();
}
if let Some(path) = output_file {
if let Some(parent) = path.parent() {
io::ensure_dir(parent)?;
}
std::fs::write(path, content)?;
} else {
println!("{content}");
}
Ok(())
}
pub use dot::*;
pub use evidence_formatter::*;
pub use formatters::*;
pub use json::*;
pub use llm_markdown::*;
pub use markdown::*;
pub use pattern_analysis::*;
pub use pattern_formatter::*;
pub use terminal::*;
pub use unified::*;
pub fn output_results_with_risk(
results: AnalysisResults,
risk_insights: Option<risk::RiskInsight>,
format: io::output::OutputFormat,
output_file: Option<PathBuf>,
) -> Result<()> {
match output_file {
Some(path) => {
let content = format_results_to_string(&results, &risk_insights, format)?;
io::write_file(&path, &content)?;
}
None => {
let mut writer = io::output::create_writer(format);
writer.write_results(&results)?;
if let Some(insights) = risk_insights {
writer.write_risk_insights(&insights)?;
}
}
}
Ok(())
}
pub fn output_unified_priorities_with_config(
analysis: priority::UnifiedAnalysis,
config: OutputConfig,
results: &AnalysisResults,
_coverage_file: Option<&PathBuf>,
) -> Result<()> {
output_unified_priorities_with_summary(
analysis,
config.top,
config.tail,
config.summary,
config.verbosity,
config.output_file,
config.output_format,
config.formatting_config,
results,
config.show_filter_stats,
)
}
#[allow(clippy::too_many_arguments)]
pub fn output_unified_priorities(
analysis: priority::UnifiedAnalysis,
top: Option<usize>,
tail: Option<usize>,
verbosity: u8,
output_file: Option<PathBuf>,
output_format: Option<crate::cli::OutputFormat>,
formatting_config: FormattingConfig,
results: &AnalysisResults,
) -> Result<()> {
output_unified_priorities_with_summary(
analysis,
top,
tail,
false, verbosity,
output_file,
output_format,
formatting_config,
results,
false, )
}
#[allow(clippy::too_many_arguments)]
pub fn output_unified_priorities_with_summary(
analysis: priority::UnifiedAnalysis,
top: Option<usize>,
tail: Option<usize>,
summary: bool,
verbosity: u8,
output_file: Option<PathBuf>,
output_format: Option<crate::cli::OutputFormat>,
formatting_config: FormattingConfig,
_results: &AnalysisResults,
show_filter_stats: bool,
) -> Result<()> {
match output_format {
Some(crate::cli::OutputFormat::Json) => {
let include_scoring_details = verbosity >= 2;
json::output_json_with_format(
&analysis,
top,
tail,
output_file,
include_scoring_details,
)
}
Some(crate::cli::OutputFormat::Markdown) => {
let include_scoring_details = verbosity >= 2;
llm_markdown::output_llm_markdown_with_format(
&analysis,
top,
tail,
output_file,
include_scoring_details,
)
}
Some(crate::cli::OutputFormat::Dot) => {
dot::output_dot_default(&analysis, output_file)
}
_ => {
if is_markdown_file(&output_file) {
markdown::output_markdown(
&analysis,
top,
tail,
verbosity,
output_file,
formatting_config,
show_filter_stats,
)
} else {
terminal::output_terminal_with_mode(
&analysis,
top,
tail,
verbosity,
output_file,
formatting_config,
summary,
)
}
}
}
}
fn is_markdown_file(output_file: &Option<PathBuf>) -> bool {
output_file
.as_ref()
.and_then(|p| p.extension())
.map(|ext| ext == "md")
.unwrap_or(false)
}