use anyhow::Result;
use std::path::PathBuf;
use crate::bin_cli::args::Commands;
use crate::bin_cli::dispatch::DispatchContext;
use crate::bin_cli::helpers::{format_languages, load_config, resolve_doc_languages};
use crate::cli::{cache, dispatch, pipeline};
pub(crate) fn handle(
config_path: &std::path::Path,
context: &DispatchContext,
lang: Option<Vec<String>>,
output: Option<String>,
skip_snippet_validation: bool,
) -> Result<Option<Commands>> {
let (_workspace, resolved) = load_config(config_path)?;
let crates_to_process = dispatch::select_crates(&resolved, &context.crate_filter)?;
let multi = dispatch::is_multi_crate(&crates_to_process);
let base_dir = std::env::current_dir()?;
let config_toml = std::fs::read_to_string(config_path)?;
let mut grand_total: usize = 0;
for resolved_cfg in &crates_to_process {
let languages = resolve_doc_languages(resolved_cfg, lang.as_deref())?;
let selector = languages.iter().map(ToString::to_string).collect::<Vec<_>>().join("-");
let output_key = output.as_deref().unwrap_or("default");
let docs_stage_key = format!("docs-{}", cache::hash_content(&format!("{selector}:{output_key}")));
let api = pipeline::extract(resolved_cfg, config_path, false)?;
let ir_json = serde_json::to_string(&api)?;
let stage_hash = cache::compute_stage_hash(&ir_json, &docs_stage_key, &config_toml, &[]);
let use_stage_cache = resolved_cfg.docs.is_none();
let sources_hash = cache::sources_hash(&resolved_cfg.sources)?;
let alef_toml_bytes = cache::read_alef_toml_bytes(config_path);
let inputs_hash = crate::core::hash::compute_inputs_hash(&sources_hash, &alef_toml_bytes);
if use_stage_cache && cache::is_stage_cached(&resolved_cfg.name, &docs_stage_key, &stage_hash, &inputs_hash) {
if multi {
tracing::info!("[{}] Docs up to date (cached)", resolved_cfg.name);
} else {
tracing::info!("Docs up to date (cached)");
}
continue;
}
if multi {
tracing::info!(
"[{}] Generating docs for: {}",
resolved_cfg.name,
format_languages(&languages)
);
} else {
tracing::info!("Generating docs for: {}", format_languages(&languages));
}
let (files, docs_result) = if skip_snippet_validation {
crate::docs::generate_docs_stage_without_snippet_compile_validation(
&api,
resolved_cfg,
&languages,
output.as_deref(),
&base_dir,
)
} else {
crate::docs::generate_docs_stage(&api, resolved_cfg, &languages, output.as_deref(), &base_dir)
};
let report = pipeline::write_scaffold_files_report(&files, &base_dir, true)?;
pipeline::report_refused_writes(&report);
docs_result?;
let count = report.changed_count();
let output_paths: Vec<PathBuf> = files
.iter()
.filter(|file| file.carries_alef_marker())
.map(|file| base_dir.join(&file.path))
.collect();
let doc_paths = pipeline::stampable_output_paths(&files, &base_dir);
pipeline::finalize_hashes(&doc_paths, &sources_hash, &alef_toml_bytes)?;
if use_stage_cache {
cache::write_stage_hash(&resolved_cfg.name, &docs_stage_key, stage_hash.as_str(), &output_paths)?;
}
grand_total += count;
}
tracing::info!("Generated {grand_total} doc files");
Ok(None)
}