1use std::path::{Path, PathBuf};
2use std::collections::{HashMap, HashSet, VecDeque};
3use anyhow::{Result, Context};
4use crate::dna::atp::ast::{HelixAst, Declaration};
5use crate::dna::hel::error::{HlxError, CompilationError, CompilationStage};
6pub struct ModuleSystem {
7 modules: HashMap<PathBuf, Module>,
8 dependencies: HashMap<PathBuf, HashSet<PathBuf>>,
9 dependents: HashMap<PathBuf, HashSet<PathBuf>>,
10 asts: HashMap<PathBuf, HelixAst>,
11 resolution_order: Vec<PathBuf>,
12 #[allow(dead_code)]
13 resolver: ModuleResolver,
14}
15#[derive(Debug, Clone)]
16pub struct Module {
17 pub path: PathBuf,
18 pub ast: HelixAst,
19 pub exports: ModuleExports,
20 pub imports: ModuleImports,
21 pub metadata: ModuleMetadata,
22}
23#[derive(Debug, Clone, Default)]
24pub struct ModuleExports {
25 pub agents: HashMap<String, AgentExport>,
26 pub workflows: HashMap<String, WorkflowExport>,
27 pub contexts: HashMap<String, ContextExport>,
28 pub crews: HashMap<String, CrewExport>,
29 pub types: HashMap<String, TypeExport>,
30}
31#[derive(Debug, Clone, Default)]
32pub struct ModuleImports {
33 pub imports: Vec<ImportStatement>,
34}
35#[derive(Debug, Clone)]
36pub struct ImportStatement {
37 pub path: String,
38 pub items: ImportItems,
39 pub alias: Option<String>,
40}
41#[derive(Debug, Clone)]
42pub enum ImportItems {
43 All,
44 Specific(Vec<ImportItem>),
45 Module,
46}
47#[derive(Debug, Clone)]
48pub struct ImportItem {
49 pub name: String,
50 pub alias: Option<String>,
51 pub item_type: ImportType,
52}
53#[derive(Debug, Clone)]
54pub enum ImportType {
55 Agent,
56 Workflow,
57 Context,
58 Crew,
59 Type,
60 Any,
61}
62#[derive(Debug, Clone)]
63pub struct AgentExport {
64 pub name: String,
65 pub public: bool,
66 pub location: usize,
67}
68#[derive(Debug, Clone)]
69pub struct WorkflowExport {
70 pub name: String,
71 pub public: bool,
72 pub location: usize,
73}
74#[derive(Debug, Clone)]
75pub struct ContextExport {
76 pub name: String,
77 pub public: bool,
78 pub location: usize,
79}
80#[derive(Debug, Clone)]
81pub struct CrewExport {
82 pub name: String,
83 pub public: bool,
84 pub location: usize,
85}
86#[derive(Debug, Clone)]
87pub struct TypeExport {
88 pub name: String,
89 pub public: bool,
90 pub type_def: String,
91}
92#[derive(Debug, Clone)]
93pub struct ModuleMetadata {
94 pub version: String,
95 pub description: Option<String>,
96 pub dependencies: Vec<String>,
97 pub main: bool,
98}
99pub struct ModuleResolver {
100 search_paths: Vec<PathBuf>,
101 cache: HashMap<String, PathBuf>,
102}
103impl ModuleResolver {
104 pub fn new() -> Self {
105 Self {
106 search_paths: vec![
107 PathBuf::from("."), PathBuf::from("./configs"),
108 PathBuf::from("./hlx_modules"),
109 ],
110 cache: HashMap::new(),
111 }
112 }
113 pub fn add_search_path<P: AsRef<Path>>(&mut self, path: P) {
114 self.search_paths.push(path.as_ref().to_path_buf());
115 }
116 pub fn resolve(&mut self, module_name: &str) -> Result<PathBuf> {
117 if let Some(path) = self.cache.get(module_name) {
118 return Ok(path.clone());
119 }
120 let patterns = vec![
121 format!("{}.hlx", module_name), format!("{}/mod.hlx", module_name),
122 format!("hlx/{}.hlx", module_name),
123 ];
124 for search_path in &self.search_paths {
125 for pattern in &patterns {
126 let full_path = search_path.join(pattern);
127 if full_path.exists() {
128 self.cache.insert(module_name.to_string(), full_path.clone());
129 return Ok(full_path);
130 }
131 }
132 }
133 Err(anyhow::anyhow!("Module not found: {}", module_name))
134 }
135 pub fn clear_cache(&mut self) {
136 self.cache.clear();
137 }
138}
139impl ModuleSystem {
140 pub fn new() -> Self {
141 Self {
142 modules: HashMap::new(),
143 dependencies: HashMap::new(),
144 dependents: HashMap::new(),
145 asts: HashMap::new(),
146 resolution_order: Vec::new(),
147 resolver: ModuleResolver::new(),
148 }
149 }
150 pub fn load_module(&mut self, path: &Path) -> Result<()> {
151 let path = path.to_path_buf();
152 let content = std::fs::read_to_string(&path).context("Failed to read file")?;
153 let ast = crate::parse(&content)
154 .map_err(|e| HlxError::compilation_error(
155 format!("Parsing error: {}", e),
156 "Check syntax and file format",
157 ))?;
158 let deps = self.extract_dependencies(&ast);
159 let imports = self.extract_imports(&ast)?;
160 let exports = self.extract_exports(&ast)?;
161 let module = Module {
162 path: path.clone(),
163 ast: ast.clone(),
164 exports,
165 imports,
166 metadata: ModuleMetadata {
167 version: "1.0.0".to_string(),
168 description: None,
169 dependencies: Vec::new(),
170 main: false,
171 },
172 };
173 self.dependencies.insert(path.clone(), deps.clone());
174 for dep in deps {
175 self.dependents.entry(dep).or_insert_with(HashSet::new).insert(path.clone());
176 }
177 self.modules.insert(path.clone(), module);
178 self.asts.insert(path, ast);
179 Ok(())
180 }
181 fn extract_dependencies(&self, _ast: &HelixAst) -> HashSet<PathBuf> {
182 let deps = HashSet::new();
183 for decl in &_ast.declarations {
184 match decl {
185 _ => {}
186 }
187 }
188 deps
189 }
190 pub fn resolve_dependencies(&mut self) -> Result<()> {
191 if let Some(cycle) = self.find_circular_dependency() {
192 return Err(
193 HlxError::compilation_error(
194 format!("Circular dependency detected: {:?}", cycle),
195 "Check module dependencies",
196 )
197 .into(),
198 );
199 }
200 self.resolution_order = self.topological_sort()?;
201 Ok(())
202 }
203 pub fn compilation_order(&self) -> &[PathBuf] {
204 &self.resolution_order
205 }
206 #[allow(dead_code)]
207 fn resolve_import_path(
208 &self,
209 import_path: &str,
210 from_module: &Path,
211 ) -> Result<PathBuf, HlxError> {
212 if import_path.starts_with("./") || import_path.starts_with("../") {
213 let base_dir = from_module.parent().unwrap_or(Path::new("."));
214 return Ok(base_dir.join(import_path).with_extension("hlx"));
215 }
216 if import_path.starts_with("/") {
217 return Ok(PathBuf::from(import_path).with_extension("hlx"));
218 }
219 Ok(PathBuf::from(format!("{}.hlx", import_path)))
220 }
221 fn extract_imports(&self, _ast: &HelixAst) -> Result<ModuleImports, HlxError> {
222 let imports = Vec::new();
223 Ok(ModuleImports { imports })
224 }
225 fn extract_exports(&self, ast: &HelixAst) -> Result<ModuleExports, HlxError> {
226 let mut exports = ModuleExports::default();
227 for (index, declaration) in ast.declarations.iter().enumerate() {
228 match declaration {
229 Declaration::Agent(agent) => {
230 exports
231 .agents
232 .insert(
233 agent.name.clone(),
234 AgentExport {
235 name: agent.name.clone(),
236 public: true,
237 location: index,
238 },
239 );
240 }
241 Declaration::Workflow(workflow) => {
242 exports
243 .workflows
244 .insert(
245 workflow.name.clone(),
246 WorkflowExport {
247 name: workflow.name.clone(),
248 public: true,
249 location: index,
250 },
251 );
252 }
253 Declaration::Context(context) => {
254 exports
255 .contexts
256 .insert(
257 context.name.clone(),
258 ContextExport {
259 name: context.name.clone(),
260 public: true,
261 location: index,
262 },
263 );
264 }
265 Declaration::Crew(crew) => {
266 exports
267 .crews
268 .insert(
269 crew.name.clone(),
270 CrewExport {
271 name: crew.name.clone(),
272 public: true,
273 location: index,
274 },
275 );
276 }
277 _ => {}
278 }
279 }
280 Ok(exports)
281 }
282 fn find_circular_dependency(&self) -> Option<Vec<PathBuf>> {
283 for (start, _) in &self.dependencies {
284 let mut visited = HashSet::new();
285 let mut rec_stack = HashSet::new();
286 let mut path = Vec::new();
287 if self.has_cycle_util(start, &mut visited, &mut rec_stack, &mut path) {
288 return Some(path);
289 }
290 }
291 None
292 }
293 fn has_cycle_util(
294 &self,
295 node: &PathBuf,
296 visited: &mut HashSet<PathBuf>,
297 rec_stack: &mut HashSet<PathBuf>,
298 path: &mut Vec<PathBuf>,
299 ) -> bool {
300 if rec_stack.contains(node) {
301 path.push(node.clone());
302 return true;
303 }
304 if visited.contains(node) {
305 return false;
306 }
307 visited.insert(node.clone());
308 rec_stack.insert(node.clone());
309 path.push(node.clone());
310 if let Some(deps) = self.dependencies.get(node) {
311 for dep in deps {
312 if self.has_cycle_util(dep, visited, rec_stack, path) {
313 return true;
314 }
315 }
316 }
317 rec_stack.remove(node);
318 path.pop();
319 false
320 }
321 fn topological_sort(&self) -> Result<Vec<PathBuf>, HlxError> {
322 let mut in_degree: HashMap<PathBuf, usize> = HashMap::new();
323 let mut result = Vec::new();
324 let mut queue = VecDeque::new();
325 for path in self.dependencies.keys() {
326 in_degree.insert(path.clone(), 0);
327 }
328 for (_, deps) in &self.dependencies {
329 for dep in deps {
330 *in_degree.entry(dep.clone()).or_insert(0) += 1;
331 }
332 }
333 for (path, °ree) in &in_degree {
334 if degree == 0 {
335 queue.push_back(path.clone());
336 }
337 }
338 while let Some(node) = queue.pop_front() {
339 result.push(node.clone());
340 if let Some(deps) = self.dependencies.get(&node) {
341 for dep in deps {
342 if let Some(degree) = in_degree.get_mut(dep) {
343 *degree -= 1;
344 if *degree == 0 {
345 queue.push_back(dep.clone());
346 }
347 }
348 }
349 }
350 }
351 if result.len() != self.dependencies.len() {
352 return Err(
353 HlxError::compilation_error(
354 "Failed to resolve module dependencies",
355 "Check module dependency resolution",
356 ),
357 );
358 }
359 Ok(result)
360 }
361 pub fn merge_modules(&self) -> Result<HelixAst, HlxError> {
362 let mut merged_declarations = Vec::new();
363 for path in &self.resolution_order {
364 if let Some(module) = self.modules.get(path) {
365 for decl in &module.ast.declarations {
366 if !Self::declaration_exists(&merged_declarations, decl) {
367 merged_declarations.push(decl.clone());
368 }
369 }
370 }
371 }
372 Ok(HelixAst {
373 declarations: merged_declarations,
374 })
375 }
376 fn declaration_exists(declarations: &[Declaration], decl: &Declaration) -> bool {
377 for existing in declarations {
378 match (existing, decl) {
379 (Declaration::Agent(a1), Declaration::Agent(a2)) => {
380 if a1.name == a2.name {
381 return true;
382 }
383 }
384 (Declaration::Workflow(w1), Declaration::Workflow(w2)) => {
385 if w1.name == w2.name {
386 return true;
387 }
388 }
389 (Declaration::Context(c1), Declaration::Context(c2)) => {
390 if c1.name == c2.name {
391 return true;
392 }
393 }
394 (Declaration::Crew(c1), Declaration::Crew(c2)) => {
395 if c1.name == c2.name {
396 return true;
397 }
398 }
399 _ => {}
400 }
401 }
402 false
403 }
404 pub fn modules(&self) -> &HashMap<PathBuf, Module> {
405 &self.modules
406 }
407 pub fn dependency_graph(&self) -> &HashMap<PathBuf, HashSet<PathBuf>> {
408 &self.dependencies
409 }
410 pub fn get_dependencies<P: AsRef<Path>>(&self, path: P) -> HashSet<PathBuf> {
411 self.dependencies.get(path.as_ref()).cloned().unwrap_or_default()
412 }
413 pub fn get_dependents<P: AsRef<Path>>(&self, path: P) -> HashSet<PathBuf> {
414 self.dependents.get(path.as_ref()).cloned().unwrap_or_default()
415 }
416}
417pub struct DependencyBundler {
418 module_system: ModuleSystem,
419}
420impl DependencyBundler {
421 pub fn new() -> Self {
422 Self {
423 module_system: ModuleSystem::new(),
424 }
425 }
426 pub fn add_root<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
427 let mut queue = VecDeque::new();
428 let mut processed = HashSet::new();
429 queue.push_back(path.as_ref().to_path_buf());
430 while let Some(current) = queue.pop_front() {
431 if processed.contains(¤t) {
432 continue;
433 }
434 self.module_system.load_module(¤t)?;
435 processed.insert(current.clone());
436 let deps = self.module_system.get_dependencies(¤t);
437 for dep in deps {
438 if !processed.contains(&dep) {
439 queue.push_back(dep);
440 }
441 }
442 }
443 Ok(())
444 }
445 pub fn build_bundle(&mut self) -> Result<HelixAst> {
446 self.module_system.resolve_dependencies()?;
447 self.module_system
448 .merge_modules()
449 .map_err(|e| anyhow::anyhow!("Failed to merge modules: {}", e))
450 }
451 pub fn get_compilation_order(&self) -> &[PathBuf] {
452 self.module_system.compilation_order()
453 }
454}
455impl Default for ModuleSystem {
456 fn default() -> Self {
457 Self::new()
458 }
459}
460impl Default for DependencyBundler {
461 fn default() -> Self {
462 Self::new()
463 }
464}
465pub struct DependencyGraph;
466impl DependencyGraph {
467 pub fn new() -> Self {
468 Self
469 }
470 pub fn check_circular(&self) -> Result<(), String> {
471 Ok(())
472 }
473}
474#[cfg(test)]
475mod tests {
476 use super::*;
477 use tempfile::TempDir;
478 #[test]
479 fn test_module_system_creation() {
480 let module_system = ModuleSystem::new();
481 assert!(module_system.modules.is_empty());
482 assert!(module_system.dependencies.is_empty());
483 }
484 #[test]
485 fn test_module_loading() {
486 let temp_dir = TempDir::new().unwrap();
487 let module_path = temp_dir.path().join("test.hlx");
488 std::fs::write(
489 &module_path,
490 r#"
491 agent "test" {
492 model = "gpt-4"
493 }
494 "#,
495 )
496 .unwrap();
497 let mut module_system = ModuleSystem::new();
498 module_system.load_module(&module_path).unwrap();
499 assert_eq!(module_system.modules.len(), 1);
500 assert!(module_system.modules.contains_key(& module_path));
501 }
502 #[test]
503 fn test_export_extraction() {
504 let temp_dir = TempDir::new().unwrap();
505 let module_path = temp_dir.path().join("test.hlx");
506 std::fs::write(
507 &module_path,
508 r#"
509 agent "analyzer" {
510 model = "gpt-4"
511 }
512
513 workflow "review" {
514 trigger = "manual"
515 }
516 "#,
517 )
518 .unwrap();
519 let mut module_system = ModuleSystem::new();
520 module_system.load_module(&module_path).unwrap();
521 let module = module_system.modules.get(&module_path).unwrap();
522 assert_eq!(module.exports.agents.len(), 1);
523 assert_eq!(module.exports.workflows.len(), 1);
524 assert!(module.exports.agents.contains_key("analyzer"));
525 assert!(module.exports.workflows.contains_key("review"));
526 }
527 #[test]
528 fn test_dependency_bundler() {
529 let mut bundler = DependencyBundler::new();
530 assert!(bundler.module_system.modules.is_empty());
531 }
532 #[test]
533 fn test_module_resolver() {
534 let mut resolver = ModuleResolver::new();
535 resolver.add_search_path("./test");
536 assert_eq!(resolver.search_paths.len(), 4);
537 }
538}