1pub mod detection;
7pub mod structure;
8
9use crate::cache::CacheManager;
10use anyhow::Result;
11
12#[derive(Debug, Clone)]
14pub struct ContextOptions {
15 pub structure: bool,
17
18 pub path: Option<String>,
20
21 pub file_types: bool,
23
24 pub project_type: bool,
26
27 pub framework: bool,
29
30 pub entry_points: bool,
32
33 pub test_layout: bool,
35
36 pub config_files: bool,
38
39 pub depth: usize,
41
42 pub json: bool,
44}
45
46impl Default for ContextOptions {
47 fn default() -> Self {
48 Self {
49 structure: true,
50 path: None,
51 file_types: true,
52 project_type: true,
53 framework: true,
54 entry_points: true,
55 test_layout: true,
56 config_files: true,
57 depth: 1,
58 json: false,
59 }
60 }
61}
62
63impl ContextOptions {
64 pub fn is_empty(&self) -> bool {
68 !self.structure
69 && !self.file_types
70 && !self.project_type
71 && !self.framework
72 && !self.entry_points
73 && !self.test_layout
74 && !self.config_files
75 }
76}
77
78pub fn generate_context(cache: &CacheManager, opts: &ContextOptions) -> Result<String> {
82 let workspace_root = cache.workspace_root();
83 let target_path = opts
84 .path
85 .as_ref()
86 .map(|p| workspace_root.join(p))
87 .unwrap_or_else(|| workspace_root.clone());
88
89 if !target_path.exists() {
91 anyhow::bail!(
92 "Path '{}' does not exist in workspace",
93 opts.path.as_deref().unwrap_or(".")
94 );
95 }
96
97 let mut effective_opts = opts.clone();
99 if effective_opts.is_empty() {
100 effective_opts.structure = true;
102 effective_opts.file_types = true;
103 effective_opts.project_type = true;
104 effective_opts.framework = true;
105 effective_opts.entry_points = true;
106 effective_opts.test_layout = true;
107 effective_opts.config_files = true;
108 }
109
110 if opts.json {
111 generate_json_context(cache, &effective_opts, &target_path)
112 } else {
113 generate_text_context(cache, &effective_opts, &target_path)
114 }
115}
116
117fn generate_text_context(
119 cache: &CacheManager,
120 opts: &ContextOptions,
121 target_path: &std::path::Path,
122) -> Result<String> {
123 let mut sections = Vec::new();
124
125 let path_display = target_path
127 .strip_prefix(cache.workspace_root())
128 .unwrap_or(target_path)
129 .display();
130 sections.push(format!("# Project Context: {}\n", path_display));
131
132 if opts.project_type
134 && let Ok(project_info) = detection::detect_project_type(cache, target_path)
135 {
136 sections.push(format!("## Project Type\n{}\n", project_info));
137 }
138
139 if opts.entry_points
141 && let Ok(entry_points) = detection::find_entry_points(target_path)
142 && !entry_points.is_empty()
143 {
144 sections.push(format!("## Entry Points\n{}\n", entry_points.join("\n")));
145 }
146
147 if opts.structure
149 && let Ok(tree) = structure::generate_tree(target_path, opts.depth)
150 {
151 sections.push(format!("## Directory Structure\n{}\n", tree));
152 }
153
154 if opts.file_types
156 && let Ok(distribution) = detection::get_file_distribution(cache)
157 {
158 sections.push(format!("## File Distribution\n{}\n", distribution));
159 }
160
161 if opts.test_layout
163 && let Ok(test_info) = detection::detect_test_layout(target_path)
164 {
165 sections.push(format!("## Test Organization\n{}\n", test_info));
166 }
167
168 if opts.framework
170 && let Ok(frameworks) = detection::detect_frameworks(target_path)
171 && !frameworks.is_empty()
172 {
173 sections.push(format!("## Framework Detection\n{}\n", frameworks));
174 }
175
176 if opts.config_files
178 && let Ok(configs) = detection::find_config_files(target_path)
179 && !configs.is_empty()
180 {
181 sections.push(format!("## Configuration Files\n{}\n", configs));
182 }
183
184 Ok(sections.join("\n"))
185}
186
187fn generate_json_context(
189 cache: &CacheManager,
190 opts: &ContextOptions,
191 target_path: &std::path::Path,
192) -> Result<String> {
193 use serde_json::json;
194
195 let mut context = json!({});
196
197 if opts.project_type
198 && let Ok(project_type) = detection::detect_project_type_json(cache, target_path)
199 {
200 context["project_type"] = project_type;
201 }
202
203 if opts.entry_points
204 && let Ok(entry_points) = detection::find_entry_points_json(target_path)
205 {
206 context["entry_points"] = entry_points;
207 }
208
209 if opts.structure
210 && let Ok(tree) = structure::generate_tree_json(target_path, opts.depth)
211 {
212 context["structure"] = tree;
213 }
214
215 if opts.file_types
216 && let Ok(distribution) = detection::get_file_distribution_json(cache)
217 {
218 context["file_distribution"] = distribution;
219 }
220
221 if opts.test_layout
222 && let Ok(test_layout) = detection::detect_test_layout_json(target_path)
223 {
224 context["test_layout"] = test_layout;
225 }
226
227 if opts.framework
228 && let Ok(frameworks) = detection::detect_frameworks_json(target_path)
229 {
230 context["frameworks"] = frameworks;
231 }
232
233 if opts.config_files
234 && let Ok(configs) = detection::find_config_files_json(target_path)
235 {
236 context["config_files"] = configs;
237 }
238
239 serde_json::to_string_pretty(&context).map_err(Into::into)
240}