Skip to main content

nargo_metadata/
lib.rs

1#![warn(missing_docs)]
2
3//! API元数据提取和管理模块
4//!
5//! 提供API元数据的提取、解析和管理功能,支持从源代码中提取API端点信息。
6
7use nargo_ir::IRModule;
8use nargo_types::Result;
9use regex::Regex;
10use serde::{Deserialize, Serialize};
11use std::collections::HashMap;
12
13/// API元数据
14///
15/// 包含API的端点列表和类型定义
16#[derive(Debug, Clone, Serialize, Deserialize, Default)]
17pub struct ApiMetadata {
18    /// API端点列表
19    pub endpoints: Vec<ApiEndpoint>,
20    /// 类型定义
21    pub types: HashMap<String, TypeDefinition>,
22}
23
24/// API端点
25///
26/// 表示一个API端点的完整信息
27#[derive(Debug, Clone, Serialize, Deserialize, Default)]
28pub struct ApiEndpoint {
29    /// 控制器名称
30    pub controller: String,
31    /// 函数名称
32    pub name: String,
33    /// HTTP方法
34    pub method: String,
35    /// 路径
36    pub path: String,
37    /// 参数列表
38    pub params: Vec<ApiParam>,
39    /// 返回类型
40    pub return_type: Option<String>,
41    /// 是否异步
42    pub is_async: bool,
43}
44
45/// API参数
46///
47/// 表示API端点的一个参数信息
48#[derive(Debug, Clone, Serialize, Deserialize, Default)]
49pub struct ApiParam {
50    /// 参数名称
51    pub name: String,
52    /// 参数类型
53    pub param_type: ApiParamType,
54    /// 类型名称
55    pub type_name: String,
56    /// 提取器参数
57    pub extractor_param: Option<String>,
58}
59
60/// API参数类型
61///
62/// 表示API参数的提取类型
63#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
64pub enum ApiParamType {
65    /// 请求体
66    Body,
67    /// 路径参数
68    Path,
69    /// 查询参数
70    Query,
71    /// 请求头
72    Header,
73    /// 当前用户
74    CurrentUser,
75    /// 会话
76    Session,
77    /// 国际化
78    I18n,
79    /// 普通参数
80    #[default]
81    Regular,
82}
83
84/// 类型定义
85///
86/// 表示一个自定义类型的定义
87#[derive(Debug, Clone, Serialize, Deserialize, Default)]
88pub struct TypeDefinition {
89    /// 类型名称
90    pub name: String,
91    /// 字段列表
92    pub fields: Vec<TypeField>,
93}
94
95/// 类型字段
96///
97/// 表示类型的一个字段
98#[derive(Debug, Clone, Serialize, Deserialize, Default)]
99pub struct TypeField {
100    /// 字段名称
101    pub name: String,
102    /// 字段类型
103    pub type_name: String,
104    /// 是否可选
105    pub optional: bool,
106}
107
108/// API元数据提取器
109///
110/// 负责从源代码和IR中提取API元数据
111pub struct ApiMetadataExtractor;
112
113impl ApiMetadataExtractor {
114    /// 提取API元数据(从源文本和IR)
115    ///
116    /// # Arguments
117    /// * `source` - 源代码文本
118    /// * `_ir` - IR模块
119    ///
120    /// # Returns
121    /// * `Result<ApiMetadata>` - 提取的API元数据
122    pub fn extract(source: &str, _ir: &IRModule) -> Result<ApiMetadata> {
123        let mut metadata = ApiMetadata::default();
124
125        Self::extract_endpoints_from_source(source, &mut metadata)?;
126        Self::extract_types_from_source(source, &mut metadata)?;
127
128        Ok(metadata)
129    }
130
131    /// 从源文本中提取API端点
132    ///
133    /// # Arguments
134    /// * `source` - 源代码文本
135    /// * `metadata` - 元数据对象
136    ///
137    /// # Returns
138    /// * `Result<()>` - 操作结果
139    fn extract_endpoints_from_source(source: &str, metadata: &mut ApiMetadata) -> Result<()> {
140        let http_decorator_re = Regex::new(r#"@http\s*\(\s*['"]([^'"]+)['"]\s*,\s*['"]([^'"]+)['"]\s*\)"#)?;
141        let export_function_re = Regex::new(r#"(async)?\s*function\s+(\w+)\s*\(([\s\S]*?)\)\s*(?::\s*([^\{]+?))?\s*\{"#)?;
142        let export_async_function_re = Regex::new(r#"export\s+(async)\s+function\s+(\w+)\s*\(([\s\S]*?)\)\s*(?::\s*([^\{]+?))?\s*\{"#)?;
143        let class_method_re = Regex::new(r#"(async)?\s*(\w+)\s*\(([\s\S]*?)\)\s*(?::\s*([^\{]+?))?\s*\{"#)?;
144        let class_def_re = Regex::new(r#"class\s+(\w+)\s*\{"#)?;
145
146        let lines: Vec<&str> = source.lines().collect();
147        let mut i = 0;
148        let mut current_controller = "Default".to_string();
149
150        while i < lines.len() {
151            let line = lines[i];
152
153            if let Some(class_caps) = class_def_re.captures(line) {
154                let class_name = class_caps[1].to_string();
155                if class_name.ends_with("Controller") {
156                    current_controller = class_name;
157                }
158            }
159
160            if let Some(captures) = http_decorator_re.captures(line) {
161                let method = captures[1].to_string().to_uppercase();
162                let path = captures[2].to_string();
163
164                i += 1;
165                while i < lines.len() && lines[i].trim().is_empty() {
166                    i += 1;
167                }
168
169                if i < lines.len() {
170                    let next_line = lines[i];
171
172                    if let Some(func_captures) = export_async_function_re.captures(&format!("{}{}", next_line, Self::collect_until_brace(&lines, i))) {
173                        let is_async = func_captures.get(1).is_some();
174                        let full_name = func_captures[2].to_string();
175                        let (controller, name) = Self::parse_controller_and_name(&full_name);
176                        let params_str = func_captures[3].to_string();
177                        let return_type = func_captures.get(4).map(|m| m.as_str().trim().to_string());
178
179                        let params = Self::parse_params(&params_str);
180
181                        metadata.endpoints.push(ApiEndpoint { controller: if controller.is_empty() { current_controller.clone() } else { controller }, name, method, path, params, return_type, is_async });
182                    }
183                    else if let Some(func_captures) = class_method_re.captures(&format!("{}{}", next_line, Self::collect_until_brace(&lines, i))) {
184                        let is_async = func_captures.get(1).is_some();
185                        let name = func_captures[2].to_string();
186                        let params_str = func_captures[3].to_string();
187                        let return_type = func_captures.get(4).map(|m| m.as_str().trim().to_string());
188
189                        let params = Self::parse_params(&params_str);
190
191                        if !name.starts_with("constructor") && !name.starts_with('_') {
192                            metadata.endpoints.push(ApiEndpoint { controller: current_controller.clone(), name, method, path, params, return_type, is_async });
193                        }
194                    }
195                    else if let Some(func_captures) = export_function_re.captures(&format!("{}{}", next_line, Self::collect_until_brace(&lines, i))) {
196                        let is_async = func_captures.get(1).is_some();
197                        let full_name = func_captures[2].to_string();
198                        let (controller, name) = Self::parse_controller_and_name(&full_name);
199                        let params_str = func_captures[3].to_string();
200                        let return_type = func_captures.get(4).map(|m| m.as_str().trim().to_string());
201
202                        let params = Self::parse_params(&params_str);
203
204                        metadata.endpoints.push(ApiEndpoint { controller: if controller.is_empty() { current_controller.clone() } else { controller }, name, method, path, params, return_type, is_async });
205                    }
206                }
207            }
208
209            i += 1;
210        }
211
212        Ok(())
213    }
214
215    /// 从函数名中解析控制器和方法名
216    ///
217    /// # Arguments
218    /// * `full_name` - 完整的函数名
219    ///
220    /// # Returns
221    /// * `(String, String)` - (控制器名, 方法名)
222    fn parse_controller_and_name(full_name: &str) -> (String, String) {
223        if let Some(idx) = full_name.find('.') { (full_name[..idx].to_string(), full_name[idx + 1..].to_string()) } else { ("Default".to_string(), full_name.to_string()) }
224    }
225
226    /// 收集直到开括号的内容
227    ///
228    /// # Arguments
229    /// * `lines` - 行数组
230    /// * `start` - 起始行索引
231    ///
232    /// # Returns
233    /// * `String` - 收集的内容
234    fn collect_until_brace(lines: &[&str], start: usize) -> String {
235        let mut result = String::new();
236        let mut i = start;
237
238        while i < lines.len() && !lines[i].contains('{') {
239            result.push_str(lines[i]);
240            i += 1;
241        }
242
243        if i < lines.len() {
244            if let Some(idx) = lines[i].find('{') {
245                result.push_str(&lines[i][0..idx]);
246            }
247        }
248
249        result
250    }
251
252    /// 解析参数字符串
253    ///
254    /// # Arguments
255    /// * `params_str` - 参数字符串
256    ///
257    /// # Returns
258    /// * `Vec<ApiParam>` - 解析后的参数列表
259    fn parse_params(params_str: &str) -> Vec<ApiParam> {
260        let mut params = Vec::new();
261
262        let param_re = Regex::new(r#"(?:@(\w+)(?:\s*\(\s*(?:['"]([^'"]*)['"])?\s*\))?\s+)?(\w+)\s*(?::\s*([^,\s]+))?"#).unwrap();
263
264        for caps in param_re.captures_iter(params_str) {
265            let decorator_name = caps.get(1).map(|m| m.as_str());
266            let extractor_param = caps.get(2).map(|m| m.as_str().to_string());
267            let name = caps[3].to_string();
268            let type_name = caps.get(4).map(|m| m.as_str().to_string()).unwrap_or_else(|| "any".to_string());
269
270            let param_type = match decorator_name {
271                Some("Body") => ApiParamType::Body,
272                Some("Path") => ApiParamType::Path,
273                Some("Query") => ApiParamType::Query,
274                Some("Header") => ApiParamType::Header,
275                Some("CurrentUser") => ApiParamType::CurrentUser,
276                Some("Session") => ApiParamType::Session,
277                Some("I18n") => ApiParamType::I18n,
278                _ => ApiParamType::Regular,
279            };
280
281            params.push(ApiParam { name, param_type, type_name, extractor_param });
282        }
283
284        params
285    }
286
287    /// 从源文本中提取类型定义
288    ///
289    /// # Arguments
290    /// * `source` - 源代码文本
291    /// * `metadata` - 元数据对象
292    ///
293    /// # Returns
294    /// * `Result<()>` - 操作结果
295    fn extract_types_from_source(source: &str, metadata: &mut ApiMetadata) -> Result<()> {
296        let class_re = Regex::new(r#"class\s+(\w+)\s*\{([\s\S]*?)\}"#)?;
297        let interface_re = Regex::new(r#"interface\s+(\w+)\s*\{([\s\S]*?)\}"#)?;
298        let _type_alias_re = Regex::new(r#"type\s+(\w+)\s*=\s*([^;]+);"#)?;
299
300        for caps in class_re.captures_iter(source) {
301            let name = caps[1].to_string();
302            if !name.ends_with("Controller") {
303                let body = caps[2].to_string();
304                let fields = Self::parse_fields(&body);
305
306                metadata.types.insert(name.clone(), TypeDefinition { name, fields });
307            }
308        }
309
310        for caps in interface_re.captures_iter(source) {
311            let name = caps[1].to_string();
312            let body = caps[2].to_string();
313            let fields = Self::parse_fields(&body);
314
315            metadata.types.insert(name.clone(), TypeDefinition { name, fields });
316        }
317
318        Ok(())
319    }
320
321    /// 解析类型字段
322    ///
323    /// # Arguments
324    /// * `body` - 类型定义体
325    ///
326    /// # Returns
327    /// * `Vec<TypeField>` - 解析后的字段列表
328    fn parse_fields(body: &str) -> Vec<TypeField> {
329        let mut fields = Vec::new();
330        let field_re = Regex::new(r#"(\w+)([!?])?\s*:\s*([^;!\n]+)(?:[;!])?"#).unwrap();
331
332        for caps in field_re.captures_iter(body) {
333            let name = caps[1].to_string();
334            let optional = caps.get(2).map_or(false, |m| m.as_str() == "?");
335            let type_name = caps[3].to_string().trim().to_string();
336
337            fields.push(TypeField { name, type_name, optional });
338        }
339
340        fields
341    }
342}