elif_introspect/
lib.rs

1use elif_core::{ElifError, ResourceSpec};
2use serde::{Deserialize, Serialize};
3use std::path::PathBuf;
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
6pub struct ProjectMap {
7    pub routes: Vec<RouteInfo>,
8    pub models: Vec<ModelInfo>,
9    pub specs: Vec<SpecInfo>,
10}
11
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct RouteInfo {
14    pub op_id: String,
15    pub method: String,
16    pub path: String,
17    pub file: String,
18    pub marker: Option<String>,
19}
20
21#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct ModelInfo {
23    pub name: String,
24    pub file: String,
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct SpecInfo {
29    pub name: String,
30    pub file: String,
31}
32
33pub struct MapGenerator {
34    project_root: PathBuf,
35}
36
37impl MapGenerator {
38    pub fn new(project_root: PathBuf) -> Self {
39        Self { project_root }
40    }
41    
42    pub fn generate(&self) -> Result<ProjectMap, ElifError> {
43        let mut routes = Vec::new();
44        let mut models = Vec::new();
45        let mut specs = Vec::new();
46        
47        // Collect resource specifications
48        let resources_dir = self.project_root.join("resources");
49        if resources_dir.exists() {
50            for entry in std::fs::read_dir(&resources_dir)? {
51                let entry = entry?;
52                let path = entry.path();
53                
54                if path.extension().map_or(false, |ext| ext == "yaml") &&
55                   path.file_stem().and_then(|s| s.to_str())
56                       .map_or(false, |s| s.ends_with(".resource")) {
57                    
58                    let content = std::fs::read_to_string(&path)?;
59                    let spec = ResourceSpec::from_yaml(&content)?;
60                    
61                    // Add spec info
62                    specs.push(SpecInfo {
63                        name: spec.name.clone(),
64                        file: path.to_string_lossy().to_string(),
65                    });
66                    
67                    // Add model info
68                    models.push(ModelInfo {
69                        name: spec.name.clone(),
70                        file: format!("crates/orm/src/models/{}.rs", spec.name.to_lowercase()),
71                    });
72                    
73                    // Add route info for each operation
74                    let handler_file = format!("apps/api/src/routes/{}.rs", spec.name.to_lowercase());
75                    for op in &spec.api.operations {
76                        routes.push(RouteInfo {
77                            op_id: format!("{}.{}", spec.name, op.op),
78                            method: op.method.clone(),
79                            path: format!("{}{}", spec.route.trim_end_matches('/'), op.path),
80                            file: handler_file.clone(),
81                            marker: Some(format!("{}_{}", op.op, spec.name)),
82                        });
83                    }
84                }
85            }
86        }
87        
88        Ok(ProjectMap {
89            routes,
90            models,
91            specs,
92        })
93    }
94}