pub mod detection;
pub mod structure;
use anyhow::Result;
use crate::cache::CacheManager;
#[derive(Debug, Clone)]
pub struct ContextOptions {
pub structure: bool,
pub path: Option<String>,
pub file_types: bool,
pub project_type: bool,
pub framework: bool,
pub entry_points: bool,
pub test_layout: bool,
pub config_files: bool,
pub depth: usize,
pub json: bool,
}
impl Default for ContextOptions {
fn default() -> Self {
Self {
structure: true,
path: None,
file_types: true,
project_type: true,
framework: true,
entry_points: true,
test_layout: true,
config_files: true,
depth: 1,
json: false,
}
}
}
impl ContextOptions {
pub fn is_empty(&self) -> bool {
!self.structure
&& !self.file_types
&& !self.project_type
&& !self.framework
&& !self.entry_points
&& !self.test_layout
&& !self.config_files
}
}
pub fn generate_context(cache: &CacheManager, opts: &ContextOptions) -> Result<String> {
let workspace_root = cache.workspace_root();
let target_path = opts.path.as_ref()
.map(|p| workspace_root.join(p))
.unwrap_or_else(|| workspace_root.clone());
if !target_path.exists() {
anyhow::bail!("Path '{}' does not exist in workspace",
opts.path.as_deref().unwrap_or("."));
}
let mut effective_opts = opts.clone();
if effective_opts.is_empty() {
effective_opts.structure = true;
effective_opts.file_types = true;
effective_opts.project_type = true;
effective_opts.framework = true;
effective_opts.entry_points = true;
effective_opts.test_layout = true;
effective_opts.config_files = true;
}
if opts.json {
generate_json_context(cache, &effective_opts, &target_path)
} else {
generate_text_context(cache, &effective_opts, &target_path)
}
}
fn generate_text_context(
cache: &CacheManager,
opts: &ContextOptions,
target_path: &std::path::Path,
) -> Result<String> {
let mut sections = Vec::new();
let path_display = target_path.strip_prefix(cache.workspace_root())
.unwrap_or(target_path)
.display();
sections.push(format!("# Project Context: {}\n", path_display));
if opts.project_type {
if let Ok(project_info) = detection::detect_project_type(cache, target_path) {
sections.push(format!("## Project Type\n{}\n", project_info));
}
}
if opts.entry_points {
if let Ok(entry_points) = detection::find_entry_points(target_path) {
if !entry_points.is_empty() {
sections.push(format!("## Entry Points\n{}\n", entry_points.join("\n")));
}
}
}
if opts.structure {
if let Ok(tree) = structure::generate_tree(target_path, opts.depth) {
sections.push(format!("## Directory Structure\n{}\n", tree));
}
}
if opts.file_types {
if let Ok(distribution) = detection::get_file_distribution(cache) {
sections.push(format!("## File Distribution\n{}\n", distribution));
}
}
if opts.test_layout {
if let Ok(test_info) = detection::detect_test_layout(target_path) {
sections.push(format!("## Test Organization\n{}\n", test_info));
}
}
if opts.framework {
if let Ok(frameworks) = detection::detect_frameworks(target_path) {
if !frameworks.is_empty() {
sections.push(format!("## Framework Detection\n{}\n", frameworks));
}
}
}
if opts.config_files {
if let Ok(configs) = detection::find_config_files(target_path) {
if !configs.is_empty() {
sections.push(format!("## Configuration Files\n{}\n", configs));
}
}
}
Ok(sections.join("\n"))
}
fn generate_json_context(
cache: &CacheManager,
opts: &ContextOptions,
target_path: &std::path::Path,
) -> Result<String> {
use serde_json::json;
let mut context = json!({});
if opts.project_type {
if let Ok(project_type) = detection::detect_project_type_json(cache, target_path) {
context["project_type"] = project_type;
}
}
if opts.entry_points {
if let Ok(entry_points) = detection::find_entry_points_json(target_path) {
context["entry_points"] = entry_points;
}
}
if opts.structure {
if let Ok(tree) = structure::generate_tree_json(target_path, opts.depth) {
context["structure"] = tree;
}
}
if opts.file_types {
if let Ok(distribution) = detection::get_file_distribution_json(cache) {
context["file_distribution"] = distribution;
}
}
if opts.test_layout {
if let Ok(test_layout) = detection::detect_test_layout_json(target_path) {
context["test_layout"] = test_layout;
}
}
if opts.framework {
if let Ok(frameworks) = detection::detect_frameworks_json(target_path) {
context["frameworks"] = frameworks;
}
}
if opts.config_files {
if let Ok(configs) = detection::find_config_files_json(target_path) {
context["config_files"] = configs;
}
}
serde_json::to_string_pretty(&context).map_err(Into::into)
}