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 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().is_some_and(|ext| ext == "yaml")
55 && path
56 .file_stem()
57 .and_then(|s| s.to_str())
58 .is_some_and(|s| s.ends_with(".resource"))
59 {
60 let content = std::fs::read_to_string(&path)?;
61 let spec = ResourceSpec::from_yaml(&content)?;
62
63 specs.push(SpecInfo {
65 name: spec.name.clone(),
66 file: path.to_string_lossy().to_string(),
67 });
68
69 models.push(ModelInfo {
71 name: spec.name.clone(),
72 file: format!("crates/orm/src/models/{}.rs", spec.name.to_lowercase()),
73 });
74
75 let handler_file =
77 format!("apps/api/src/routes/{}.rs", spec.name.to_lowercase());
78 for op in &spec.api.operations {
79 routes.push(RouteInfo {
80 op_id: format!("{}.{}", spec.name, op.op),
81 method: op.method.clone(),
82 path: format!("{}{}", spec.route.trim_end_matches('/'), op.path),
83 file: handler_file.clone(),
84 marker: Some(format!("{}_{}", op.op, spec.name)),
85 });
86 }
87 }
88 }
89 }
90
91 Ok(ProjectMap {
92 routes,
93 models,
94 specs,
95 })
96 }
97}