Skip to main content

summer_lsp/core/
schema.rs

1//! 配置 Schema 管理模块
2
3use serde::{Deserialize, Serialize};
4use serde_json::json;
5use std::collections::HashMap;
6use std::path::Path;
7
8use crate::scanner::config::{ConfigField, ConfigScanner, ConfigurationStruct};
9
10/// 配置 Schema
11///
12/// 包含所有插件的配置定义
13///
14/// 这个结构体用于解析 summer-rs 生成的 JSON Schema,格式为:
15/// ```json
16/// {
17///   "properties": {
18///     "web": { ... },
19///     "redis": { ... }
20///   }
21/// }
22/// ```
23#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct ConfigSchema {
25    /// 插件配置映射,键为配置前缀
26    /// 在 JSON Schema 中对应 "properties" 字段
27    #[serde(rename = "properties")]
28    pub plugins: HashMap<String, serde_json::Value>,
29}
30
31/// 插件 Schema
32///
33/// 单个插件的配置定义
34#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct PluginSchema {
36    /// 配置前缀(如 "web"、"redis" 等)
37    pub prefix: String,
38    /// 配置属性映射,键为属性名
39    pub properties: HashMap<String, PropertySchema>,
40}
41
42/// 配置属性 Schema
43///
44/// 单个配置属性的定义
45#[derive(Debug, Clone, Serialize, Deserialize)]
46pub struct PropertySchema {
47    /// 属性名称
48    pub name: String,
49    /// 类型信息
50    pub type_info: TypeInfo,
51    /// 属性描述
52    pub description: String,
53    /// 默认值(可选)
54    #[serde(skip_serializing_if = "Option::is_none")]
55    pub default: Option<Value>,
56    /// 是否必需
57    #[serde(default)]
58    pub required: bool,
59    /// 废弃信息(可选)
60    #[serde(skip_serializing_if = "Option::is_none")]
61    pub deprecated: Option<String>,
62    /// 示例代码(可选)
63    #[serde(skip_serializing_if = "Option::is_none")]
64    pub example: Option<String>,
65}
66
67/// 类型信息
68#[derive(Debug, Clone, Serialize, Deserialize)]
69#[serde(tag = "type", rename_all = "lowercase")]
70pub enum TypeInfo {
71    /// 字符串类型
72    String {
73        /// 枚举值(可选)
74        #[serde(skip_serializing_if = "Option::is_none")]
75        enum_values: Option<Vec<String>>,
76        /// 最小长度(可选)
77        #[serde(skip_serializing_if = "Option::is_none")]
78        min_length: Option<usize>,
79        /// 最大长度(可选)
80        #[serde(skip_serializing_if = "Option::is_none")]
81        max_length: Option<usize>,
82    },
83    /// 整数类型
84    Integer {
85        /// 最小值(可选)
86        #[serde(skip_serializing_if = "Option::is_none")]
87        min: Option<i64>,
88        /// 最大值(可选)
89        #[serde(skip_serializing_if = "Option::is_none")]
90        max: Option<i64>,
91    },
92    /// 浮点数类型
93    Float {
94        /// 最小值(可选)
95        #[serde(skip_serializing_if = "Option::is_none")]
96        min: Option<f64>,
97        /// 最大值(可选)
98        #[serde(skip_serializing_if = "Option::is_none")]
99        max: Option<f64>,
100    },
101    /// 布尔类型
102    Boolean,
103    /// 数组类型
104    Array {
105        /// 元素类型
106        item_type: Box<TypeInfo>,
107    },
108    /// 对象类型(嵌套配置)
109    Object {
110        /// 嵌套属性
111        properties: HashMap<String, PropertySchema>,
112    },
113}
114
115/// 配置值
116#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
117#[serde(untagged)]
118pub enum Value {
119    /// 字符串值
120    String(String),
121    /// 整数值
122    Integer(i64),
123    /// 浮点数值
124    Float(f64),
125    /// 布尔值
126    Boolean(bool),
127    /// 数组值
128    Array(Vec<Value>),
129    /// 对象值
130    Table(HashMap<String, Value>),
131}
132
133/// Schema 提供者
134///
135/// 管理配置 Schema,提供配置项元数据查询
136#[derive(Clone)]
137pub struct SchemaProvider {
138    /// Schema 数据(加载后不会改变,直接拥有即可)
139    schema: ConfigSchema,
140}
141
142impl SchemaProvider {
143    /// Schema URL
144    const SCHEMA_URL: &'static str = "https://summer-rs.github.io/config-schema.json";
145
146    /// 创建新的 Schema 提供者(使用空 Schema)
147    pub fn new() -> Self {
148        Self {
149            schema: ConfigSchema {
150                plugins: HashMap::new(),
151            },
152        }
153    }
154
155    /// 从 URL 加载 Schema
156    ///
157    /// 如果加载失败,使用内置的备用 Schema
158    pub async fn load() -> anyhow::Result<Self> {
159        // 尝试从 URL 加载 Schema
160        match Self::load_from_url(Self::SCHEMA_URL).await {
161            Ok(schema) => {
162                tracing::info!("Successfully loaded schema from {}", Self::SCHEMA_URL);
163                Ok(Self { schema })
164            }
165            Err(e) => {
166                tracing::warn!("Failed to load schema from URL: {}, using fallback", e);
167                // 使用内置备用 Schema
168                Ok(Self::with_fallback_schema())
169            }
170        }
171    }
172
173    /// 从 URL 加载 Schema 并合并本地 Schema 文件
174    ///
175    /// # Arguments
176    ///
177    /// * `workspace_path` - 工作空间路径,用于查找本地 Schema 文件
178    ///
179    /// # Returns
180    ///
181    /// 返回合并了远程和本地 Schema 的 SchemaProvider
182    ///
183    /// # 本地 Schema 文件
184    ///
185    /// 支持以下文件(按优先级):
186    /// 1. `target/*/summer-lsp.schema.json` - build.rs 生成的 Schema 文件
187    /// 2. `.summer-lsp.schema.json` - 手动生成的 Schema 文件(兼容旧版本)
188    /// 3. 扫描 Rust 代码生成 Schema(fallback)
189    pub async fn load_with_workspace(workspace_path: &Path) -> anyhow::Result<Self> {
190        // 加载远程 Schema
191        let mut provider = Self::load().await?;
192
193        // 1. 尝试从 target 目录加载 Schema(build.rs 生成的)
194        if let Some(schema_path) = Self::find_schema_in_target(workspace_path) {
195            tracing::info!("Loading schema from target directory: {:?}", schema_path);
196            match Self::load_local_schema_file(&schema_path) {
197                Ok(local_schemas) => {
198                    tracing::info!("Loaded {} local schemas from target", local_schemas.len());
199                    for (prefix, schema) in local_schemas {
200                        provider.schema.plugins.insert(prefix, schema);
201                    }
202                    return Ok(provider);
203                }
204                Err(e) => {
205                    tracing::warn!("Failed to load schema from target: {}", e);
206                }
207            }
208        }
209
210        // 2. 尝试从根目录加载 Schema(兼容旧版本)
211        let local_schema_path = workspace_path.join(".summer-lsp.schema.json");
212        if local_schema_path.exists() {
213            tracing::info!("Loading local schema from: {:?}", local_schema_path);
214            match Self::load_local_schema_file(&local_schema_path) {
215                Ok(local_schemas) => {
216                    tracing::info!("Loaded {} local schemas from file", local_schemas.len());
217                    for (prefix, schema) in local_schemas {
218                        provider.schema.plugins.insert(prefix, schema);
219                    }
220                    return Ok(provider);
221                }
222                Err(e) => {
223                    tracing::warn!("Failed to load local schema file: {}", e);
224                }
225            }
226        }
227
228        // 3. Fallback: 扫描 Rust 代码生成 Schema
229        tracing::info!("Scanning local configurations in: {:?}", workspace_path);
230        let scanner = ConfigScanner::new();
231        match scanner.scan_configurations(workspace_path) {
232            Ok(configurations) => {
233                tracing::info!("Found {} local configuration structs", configurations.len());
234
235                // 将本地配置转换为 Schema 并合并
236                for config in configurations {
237                    let schema_json = Self::configuration_to_schema(&config);
238                    provider
239                        .schema
240                        .plugins
241                        .insert(config.prefix.clone(), schema_json);
242                    tracing::debug!("Added local configuration: {}", config.prefix);
243                }
244            }
245            Err(e) => {
246                tracing::warn!("Failed to scan local configurations: {}", e);
247            }
248        }
249
250        Ok(provider)
251    }
252
253    /// 在 target 目录中查找 Schema 文件
254    ///
255    /// build.rs 生成的文件位于 target/{profile}/build/{package}/out/summer-lsp.schema.json
256    /// 在 workspace 中,每个 crate 都可能生成自己的 Schema 文件
257    /// 我们需要找到所有的 Schema 文件并合并
258    fn find_schema_in_target(workspace_path: &Path) -> Option<std::path::PathBuf> {
259        let target_dir = workspace_path.join("target");
260        if !target_dir.exists() {
261            return None;
262        }
263
264        // 收集所有 Schema 文件
265        let mut schema_files = Vec::new();
266
267        if let Ok(entries) = std::fs::read_dir(&target_dir) {
268            for entry in entries.flatten() {
269                let path = entry.path();
270                if path.is_dir() {
271                    // 检查 target/{profile}/build/*/out/summer-lsp.schema.json
272                    let build_dir = path.join("build");
273                    if build_dir.exists() {
274                        if let Ok(build_entries) = std::fs::read_dir(&build_dir) {
275                            for build_entry in build_entries.flatten() {
276                                let out_dir = build_entry.path().join("out");
277                                let schema_path = out_dir.join("summer-lsp.schema.json");
278                                if schema_path.exists() {
279                                    if let Ok(metadata) = std::fs::metadata(&schema_path) {
280                                        if let Ok(modified) = metadata.modified() {
281                                            schema_files.push((schema_path, modified));
282                                        }
283                                    }
284                                }
285                            }
286                        }
287                    }
288                }
289            }
290        }
291
292        if schema_files.is_empty() {
293            return None;
294        }
295
296        // 如果只有一个 Schema 文件,直接返回
297        if schema_files.len() == 1 {
298            return Some(schema_files[0].0.clone());
299        }
300
301        // 多个 Schema 文件:合并它们
302        tracing::info!("Found {} schema files, merging...", schema_files.len());
303
304        // 按修改时间排序,优先使用最新的
305        schema_files.sort_by(|a, b| b.1.cmp(&a.1));
306
307        // 尝试合并所有 Schema 文件
308        match Self::merge_schema_files(&schema_files) {
309            Ok(merged_path) => Some(merged_path),
310            Err(e) => {
311                tracing::warn!("Failed to merge schema files: {}, using latest", e);
312                // 合并失败,返回最新的
313                Some(schema_files[0].0.clone())
314            }
315        }
316    }
317
318    /// 合并多个 Schema 文件
319    ///
320    /// 在 workspace 中,每个 crate 可能生成自己的 Schema
321    /// 我们需要将它们合并为一个完整的 Schema
322    fn merge_schema_files(
323        schema_files: &[(std::path::PathBuf, std::time::SystemTime)],
324    ) -> anyhow::Result<std::path::PathBuf> {
325        use std::fs;
326
327        let mut merged_properties = serde_json::Map::new();
328
329        // 读取并合并所有 Schema 文件
330        for (path, _) in schema_files {
331            tracing::debug!("Merging schema from: {:?}", path);
332            let content = fs::read_to_string(path)?;
333            let schema: serde_json::Value = serde_json::from_str(&content)?;
334
335            if let Some(properties) = schema.get("properties").and_then(|p| p.as_object()) {
336                for (key, value) in properties {
337                    // 如果键已存在,后面的会覆盖前面的(按时间排序,新的优先)
338                    merged_properties.insert(key.clone(), value.clone());
339                }
340            }
341        }
342
343        // 创建合并后的 Schema
344        let merged_schema = serde_json::json!({
345            "type": "object",
346            "properties": merged_properties
347        });
348
349        // 写入临时文件
350        let temp_dir = std::env::temp_dir();
351        let merged_path = temp_dir.join("summer-lsp-merged.schema.json");
352        fs::write(&merged_path, serde_json::to_string_pretty(&merged_schema)?)?;
353
354        tracing::info!(
355            "Merged {} schema files ({} configs) into: {:?}",
356            schema_files.len(),
357            merged_properties.len(),
358            merged_path
359        );
360
361        Ok(merged_path)
362    }
363
364    /// 从本地文件加载 Schema
365    fn load_local_schema_file(path: &Path) -> anyhow::Result<HashMap<String, serde_json::Value>> {
366        let content = std::fs::read_to_string(path)?;
367        let schema: serde_json::Value = serde_json::from_str(&content)?;
368
369        let mut schemas = HashMap::new();
370
371        // 提取 properties 字段
372        if let Some(properties) = schema.get("properties").and_then(|p| p.as_object()) {
373            for (key, value) in properties {
374                schemas.insert(key.clone(), value.clone());
375            }
376        }
377
378        Ok(schemas)
379    }
380
381    /// 将 ConfigurationStruct 转换为 JSON Schema
382    fn configuration_to_schema(config: &ConfigurationStruct) -> serde_json::Value {
383        let mut properties = serde_json::Map::new();
384
385        for field in &config.fields {
386            let field_schema = Self::field_to_schema(field);
387            properties.insert(field.name.clone(), field_schema);
388        }
389
390        json!({
391            "type": "object",
392            "properties": properties,
393            "description": format!("Configuration for {}", config.name)
394        })
395    }
396
397    /// 将 ConfigField 转换为 JSON Schema 属性
398    fn field_to_schema(field: &ConfigField) -> serde_json::Value {
399        let mut schema = serde_json::Map::new();
400
401        // 推断类型
402        let (field_type, is_optional) = if field.optional {
403            // Option<T> 类型,提取内部类型
404            let inner_type = field
405                .type_name
406                .strip_prefix("Option<")
407                .and_then(|s| s.strip_suffix('>'))
408                .unwrap_or(&field.type_name);
409            (inner_type, true)
410        } else {
411            (field.type_name.as_str(), false)
412        };
413
414        // 映射 Rust 类型到 JSON Schema 类型
415        let json_type = match field_type {
416            "String" | "str" | "&str" => "string",
417            "bool" => "boolean",
418            "i8" | "i16" | "i32" | "i64" | "i128" | "u8" | "u16" | "u32" | "u64" | "u128"
419            | "isize" | "usize" => "integer",
420            "f32" | "f64" => "number",
421            t if t.starts_with("Vec<") => "array",
422            t if t.starts_with("HashMap<") || t.starts_with("BTreeMap<") => "object",
423            _ => "string", // 默认为字符串
424        };
425
426        schema.insert("type".to_string(), json!(json_type));
427
428        // 添加描述
429        if let Some(desc) = &field.description {
430            schema.insert("description".to_string(), json!(desc));
431        }
432
433        // 如果是可选的,添加说明
434        if is_optional {
435            let desc = schema
436                .get("description")
437                .and_then(|v| v.as_str())
438                .unwrap_or("")
439                .to_string();
440            schema.insert(
441                "description".to_string(),
442                json!(if desc.is_empty() {
443                    "Optional field".to_string()
444                } else {
445                    format!("{} (optional)", desc)
446                }),
447            );
448        }
449
450        serde_json::Value::Object(schema)
451    }
452
453    /// 从指定 URL 加载 Schema
454    async fn load_from_url(url: &str) -> anyhow::Result<ConfigSchema> {
455        let response = reqwest::get(url).await?;
456        let schema = response.json::<ConfigSchema>().await?;
457        Ok(schema)
458    }
459
460    /// 使用内置备用 Schema
461    fn with_fallback_schema() -> Self {
462        let fallback_schema = Self::create_fallback_schema();
463        Self {
464            schema: fallback_schema,
465        }
466    }
467
468    /// 创建内置备用 Schema
469    ///
470    /// 包含常见的 summer-rs 插件配置
471    fn create_fallback_schema() -> ConfigSchema {
472        let mut plugins = HashMap::new();
473
474        // Web 插件配置
475        let web_schema = json!({
476            "type": "object",
477            "properties": {
478                "host": {
479                    "type": "string",
480                    "description": "Web server host address",
481                    "default": "0.0.0.0"
482                },
483                "port": {
484                    "type": "integer",
485                    "description": "Web server port",
486                    "default": 8080,
487                    "minimum": 1,
488                    "maximum": 65535
489                }
490            }
491        });
492        plugins.insert("web".to_string(), web_schema);
493
494        // Redis 插件配置
495        let redis_schema = json!({
496            "type": "object",
497            "properties": {
498                "url": {
499                    "type": "string",
500                    "description": "Redis connection URL",
501                    "default": "redis://localhost:6379"
502                }
503            }
504        });
505        plugins.insert("redis".to_string(), redis_schema);
506
507        ConfigSchema { plugins }
508    }
509
510    /// 获取插件 Schema
511    ///
512    /// 检查指定的配置前缀是否在 Schema 中定义
513    pub fn has_plugin(&self, prefix: &str) -> bool {
514        self.schema.plugins.contains_key(prefix)
515    }
516
517    /// 获取所有配置前缀
518    ///
519    /// 返回所有已注册插件的配置前缀列表
520    pub fn get_all_prefixes(&self) -> Vec<String> {
521        self.schema.plugins.keys().cloned().collect()
522    }
523
524    /// 获取插件的 Schema
525    ///
526    /// 返回指定插件的 JSON Schema
527    pub fn get_plugin_schema(&self, prefix: &str) -> Option<&serde_json::Value> {
528        self.schema.plugins.get(prefix)
529    }
530
531    /// 检查配置属性是否存在
532    ///
533    /// 查询指定插件的指定属性是否在 Schema 中定义
534    pub fn has_property(&self, prefix: &str, property: &str) -> bool {
535        if let Some(plugin_schema) = self.schema.plugins.get(prefix) {
536            // 尝试解析为 JSON Schema 对象
537            if let Some(properties) = plugin_schema.get("properties") {
538                if let Some(props_obj) = properties.as_object() {
539                    return props_obj.contains_key(property);
540                }
541            }
542        }
543        false
544    }
545
546    /// 获取插件的结构化 Schema
547    ///
548    /// 将 JSON Schema 解析为 PluginSchema 结构
549    pub fn get_plugin(&self, prefix: &str) -> Option<PluginSchema> {
550        let plugin_json = self.schema.plugins.get(prefix)?;
551
552        // 获取 $defs(如果存在)
553        let defs = plugin_json.get("$defs").unwrap_or(&serde_json::Value::Null);
554
555        // 解析 properties
556        let properties_json = plugin_json.get("properties")?.as_object()?;
557        let mut properties = HashMap::new();
558
559        for (key, value) in properties_json {
560            if let Some(property_schema) = Self::parse_property_schema_with_defs(key, value, defs) {
561                properties.insert(key.clone(), property_schema);
562            }
563        }
564
565        Some(PluginSchema {
566            prefix: prefix.to_string(),
567            properties,
568        })
569    }
570
571    /// 解析属性 Schema
572    fn parse_property_schema(name: &str, value: &serde_json::Value) -> Option<PropertySchema> {
573        // 处理 $ref 引用
574        if value.get("$ref").is_some() {
575            // 暂时跳过 $ref,因为需要上下文来解析
576            // 我们将在 get_plugin 中处理这个
577            return None;
578        }
579
580        let type_info = Self::parse_type_info(value)?;
581        let description = value
582            .get("description")
583            .and_then(|d| d.as_str())
584            .unwrap_or("")
585            .to_string();
586
587        let default = value.get("default").and_then(Self::parse_value);
588        let required = value
589            .get("required")
590            .and_then(|r| r.as_bool())
591            .unwrap_or(false);
592        let deprecated = value
593            .get("deprecated")
594            .and_then(|d| d.as_str())
595            .map(|s| s.to_string());
596        let example = value
597            .get("example")
598            .and_then(|e| e.as_str())
599            .map(|s| s.to_string());
600
601        Some(PropertySchema {
602            name: name.to_string(),
603            type_info,
604            description,
605            default,
606            required,
607            deprecated,
608            example,
609        })
610    }
611
612    /// 解析属性 Schema(带 $defs 上下文)
613    fn parse_property_schema_with_defs(
614        name: &str,
615        value: &serde_json::Value,
616        defs: &serde_json::Value,
617    ) -> Option<PropertySchema> {
618        // 处理 $ref 引用
619        if let Some(ref_path) = value.get("$ref").and_then(|r| r.as_str()) {
620            // 解析引用路径,例如 "#/$defs/LogLevel"
621            if let Some(def_name) = ref_path.strip_prefix("#/$defs/") {
622                if let Some(def_value) = defs.get(def_name) {
623                    // 递归解析引用的定义
624                    return Self::parse_property_schema_with_defs(name, def_value, defs);
625                }
626            }
627            // 如果无法解析引用,返回 None
628            return None;
629        }
630
631        let type_info = Self::parse_type_info_with_defs(value, defs)?;
632        let description = value
633            .get("description")
634            .and_then(|d| d.as_str())
635            .unwrap_or("")
636            .to_string();
637
638        let default = value.get("default").and_then(Self::parse_value);
639        let required = value
640            .get("required")
641            .and_then(|r| r.as_bool())
642            .unwrap_or(false);
643        let deprecated = value
644            .get("deprecated")
645            .and_then(|d| d.as_str())
646            .map(|s| s.to_string());
647        let example = value
648            .get("example")
649            .and_then(|e| e.as_str())
650            .map(|s| s.to_string());
651
652        Some(PropertySchema {
653            name: name.to_string(),
654            type_info,
655            description,
656            default,
657            required,
658            deprecated,
659            example,
660        })
661    }
662
663    /// 解析类型信息
664    fn parse_type_info(value: &serde_json::Value) -> Option<TypeInfo> {
665        Self::parse_type_info_with_defs(value, &serde_json::Value::Null)
666    }
667
668    /// 解析类型信息(带 $defs 上下文)
669    fn parse_type_info_with_defs(
670        value: &serde_json::Value,
671        _defs: &serde_json::Value,
672    ) -> Option<TypeInfo> {
673        // 处理 oneOf(通常用于枚举)
674        if let Some(one_of) = value.get("oneOf").and_then(|o| o.as_array()) {
675            // 提取所有 const 值作为枚举
676            let enum_values: Vec<String> = one_of
677                .iter()
678                .filter_map(|item| item.get("const").and_then(|c| c.as_str()))
679                .map(|s| s.to_string())
680                .collect();
681
682            if !enum_values.is_empty() {
683                return Some(TypeInfo::String {
684                    enum_values: Some(enum_values),
685                    min_length: None,
686                    max_length: None,
687                });
688            }
689        }
690
691        // 处理顶层 enum
692        if let Some(enum_array) = value.get("enum").and_then(|e| e.as_array()) {
693            let enum_values: Vec<String> = enum_array
694                .iter()
695                .filter_map(|v| v.as_str())
696                .map(|s| s.to_string())
697                .collect();
698
699            if !enum_values.is_empty() {
700                return Some(TypeInfo::String {
701                    enum_values: Some(enum_values),
702                    min_length: None,
703                    max_length: None,
704                });
705            }
706        }
707
708        let type_str = value.get("type")?.as_str()?;
709
710        match type_str {
711            "string" => {
712                let enum_values = value.get("enum").and_then(|e| e.as_array()).map(|arr| {
713                    arr.iter()
714                        .filter_map(|v| v.as_str().map(|s| s.to_string()))
715                        .collect()
716                });
717                let min_length = value
718                    .get("minLength")
719                    .and_then(|m| m.as_u64())
720                    .map(|n| n as usize);
721                let max_length = value
722                    .get("maxLength")
723                    .and_then(|m| m.as_u64())
724                    .map(|n| n as usize);
725
726                Some(TypeInfo::String {
727                    enum_values,
728                    min_length,
729                    max_length,
730                })
731            }
732            "integer" => {
733                let min = value.get("minimum").and_then(|m| m.as_i64());
734                let max = value.get("maximum").and_then(|m| m.as_i64());
735
736                Some(TypeInfo::Integer { min, max })
737            }
738            "number" => {
739                let min = value.get("minimum").and_then(|m| m.as_f64());
740                let max = value.get("maximum").and_then(|m| m.as_f64());
741
742                Some(TypeInfo::Float { min, max })
743            }
744            "boolean" => Some(TypeInfo::Boolean),
745            "array" => {
746                let items = value.get("items")?;
747                let item_type = Self::parse_type_info(items)?;
748
749                Some(TypeInfo::Array {
750                    item_type: Box::new(item_type),
751                })
752            }
753            "object" => {
754                let properties_json = value.get("properties")?.as_object()?;
755                let mut properties = HashMap::new();
756
757                for (key, val) in properties_json {
758                    if let Some(prop_schema) = Self::parse_property_schema(key, val) {
759                        properties.insert(key.clone(), prop_schema);
760                    }
761                }
762
763                Some(TypeInfo::Object { properties })
764            }
765            _ => None,
766        }
767    }
768
769    /// 解析值
770    fn parse_value(value: &serde_json::Value) -> Option<Value> {
771        match value {
772            serde_json::Value::String(s) => Some(Value::String(s.clone())),
773            serde_json::Value::Number(n) => {
774                if let Some(i) = n.as_i64() {
775                    Some(Value::Integer(i))
776                } else {
777                    n.as_f64().map(Value::Float)
778                }
779            }
780            serde_json::Value::Bool(b) => Some(Value::Boolean(*b)),
781            serde_json::Value::Array(arr) => {
782                let values: Option<Vec<Value>> = arr.iter().map(Self::parse_value).collect();
783                values.map(Value::Array)
784            }
785            serde_json::Value::Object(obj) => {
786                let mut table = HashMap::new();
787                for (k, v) in obj {
788                    if let Some(val) = Self::parse_value(v) {
789                        table.insert(k.clone(), val);
790                    }
791                }
792                Some(Value::Table(table))
793            }
794            serde_json::Value::Null => None,
795        }
796    }
797}
798
799impl Default for SchemaProvider {
800    fn default() -> Self {
801        Self::with_fallback_schema()
802    }
803}
804
805impl SchemaProvider {
806    /// 从给定的 ConfigSchema 创建 SchemaProvider(用于测试)
807    ///
808    /// 这个方法主要用于属性测试,允许使用自定义的 Schema 创建提供者
809    pub fn from_schema(schema: ConfigSchema) -> Self {
810        Self { schema }
811    }
812}
813
814#[cfg(test)]
815mod tests {
816    use super::*;
817    use serial_test::serial;
818
819    #[tokio::test]
820    async fn test_load_real_schema() {
821        // 测试加载真实的 Schema
822        let provider = SchemaProvider::load().await.unwrap();
823
824        // 验证常见的插件存在
825        assert!(provider.has_plugin("logger"), "Should have logger plugin");
826        assert!(provider.has_plugin("grpc"), "Should have grpc plugin");
827        assert!(provider.has_plugin("web"), "Should have web plugin");
828        assert!(provider.has_plugin("redis"), "Should have redis plugin");
829
830        // 验证可以获取插件 Schema
831        let grpc_schema = provider.get_plugin("grpc");
832        assert!(grpc_schema.is_some(), "Should be able to get grpc schema");
833
834        if let Some(grpc) = grpc_schema {
835            // 验证 graceful 属性存在
836            assert!(
837                grpc.properties.contains_key("graceful"),
838                "GRPC should have 'graceful' property"
839            );
840
841            // 验证 port 属性存在
842            assert!(
843                grpc.properties.contains_key("port"),
844                "GRPC should have 'port' property"
845            );
846        }
847
848        // 验证 logger 插件
849        let logger_schema = provider.get_plugin("logger");
850        assert!(
851            logger_schema.is_some(),
852            "Should be able to get logger schema"
853        );
854
855        if let Some(logger) = logger_schema {
856            // 打印调试信息
857            println!(
858                "Logger properties found: {:?}",
859                logger.properties.keys().collect::<Vec<_>>()
860            );
861
862            // 验证常见属性
863            assert!(
864                logger.properties.contains_key("level"),
865                "Logger should have 'level' property. Found: {:?}",
866                logger.properties.keys().collect::<Vec<_>>()
867            );
868            assert!(
869                logger.properties.contains_key("format"),
870                "Logger should have 'format' property"
871            );
872        }
873    }
874
875    #[test]
876    fn test_parse_property_schema() {
877        let json = serde_json::json!({
878            "type": "string",
879            "description": "Test property",
880            "default": "test_value",
881            "enum": ["value1", "value2", "value3"]
882        });
883
884        let property = SchemaProvider::parse_property_schema("test_prop", &json);
885        assert!(property.is_some());
886
887        let prop = property.unwrap();
888        assert_eq!(prop.name, "test_prop");
889        assert_eq!(prop.description, "Test property");
890
891        // 验证枚举值
892        if let TypeInfo::String { enum_values, .. } = &prop.type_info {
893            assert!(enum_values.is_some());
894            let enums = enum_values.as_ref().unwrap();
895            assert_eq!(enums.len(), 3);
896            assert!(enums.contains(&"value1".to_string()));
897        } else {
898            panic!("Expected String type");
899        }
900
901        // 验证默认值
902        assert!(prop.default.is_some());
903        if let Some(Value::String(s)) = &prop.default {
904            assert_eq!(s, "test_value");
905        } else {
906            panic!("Expected String default value");
907        }
908    }
909
910    #[test]
911    fn test_parse_integer_type() {
912        let json = serde_json::json!({
913            "type": "integer",
914            "minimum": 1,
915            "maximum": 65535
916        });
917
918        let type_info = SchemaProvider::parse_type_info(&json);
919        assert!(type_info.is_some());
920
921        if let Some(TypeInfo::Integer { min, max }) = type_info {
922            assert_eq!(min, Some(1));
923            assert_eq!(max, Some(65535));
924        } else {
925            panic!("Expected Integer type");
926        }
927    }
928
929    #[test]
930    fn test_parse_boolean_type() {
931        let json = serde_json::json!({
932            "type": "boolean"
933        });
934
935        let type_info = SchemaProvider::parse_type_info(&json);
936        assert!(type_info.is_some());
937        assert!(matches!(type_info.unwrap(), TypeInfo::Boolean));
938    }
939
940    #[test]
941    fn test_find_schema_in_target() {
942        use std::fs;
943        use tempfile::TempDir;
944
945        // 创建临时目录结构
946        let temp_dir = TempDir::new().unwrap();
947        let workspace_path = temp_dir.path();
948
949        // 创建 target/debug/build/my-package/out/summer-lsp.schema.json
950        let target_dir = workspace_path.join("target/debug/build/my-package/out");
951        fs::create_dir_all(&target_dir).unwrap();
952
953        let schema_path = target_dir.join("summer-lsp.schema.json");
954        let schema_content = serde_json::json!({
955            "properties": {
956                "test-config": {
957                    "type": "object",
958                    "properties": {
959                        "field1": {
960                            "type": "string",
961                            "description": "Test field"
962                        }
963                    }
964                }
965            }
966        });
967        fs::write(
968            &schema_path,
969            serde_json::to_string_pretty(&schema_content).unwrap(),
970        )
971        .unwrap();
972
973        // 测试查找功能
974        let found = SchemaProvider::find_schema_in_target(workspace_path);
975        assert!(found.is_some());
976        assert_eq!(found.unwrap(), schema_path);
977    }
978
979    #[test]
980    #[serial]
981    fn test_find_schema_in_target_multiple_profiles() {
982        use std::fs;
983        use std::thread;
984        use std::time::Duration;
985        use tempfile::TempDir;
986
987        // 创建临时目录结构
988        let temp_dir = TempDir::new().unwrap();
989        let workspace_path = temp_dir.path();
990
991        // 创建多个 profile 的 Schema 文件
992        let debug_dir = workspace_path.join("target/debug/build/my-package/out");
993        fs::create_dir_all(&debug_dir).unwrap();
994        let debug_schema = debug_dir.join("summer-lsp.schema.json");
995        fs::write(
996            &debug_schema,
997            serde_json::json!({
998                "properties": {
999                    "debug-config": {
1000                        "type": "object"
1001                    }
1002                }
1003            })
1004            .to_string(),
1005        )
1006        .unwrap();
1007
1008        // 等待一小段时间确保文件时间戳不同
1009        thread::sleep(Duration::from_millis(10));
1010
1011        let release_dir = workspace_path.join("target/release/build/my-package/out");
1012        fs::create_dir_all(&release_dir).unwrap();
1013        let release_schema = release_dir.join("summer-lsp.schema.json");
1014        fs::write(
1015            &release_schema,
1016            serde_json::json!({
1017                "properties": {
1018                    "release-config": {
1019                        "type": "object"
1020                    }
1021                }
1022            })
1023            .to_string(),
1024        )
1025        .unwrap();
1026
1027        // 应该合并两个 profile 的 Schema
1028        let found = SchemaProvider::find_schema_in_target(workspace_path);
1029        assert!(found.is_some());
1030
1031        let merged_path = found.unwrap();
1032        let content = fs::read_to_string(&merged_path).unwrap();
1033        let schema: serde_json::Value = serde_json::from_str(&content).unwrap();
1034
1035        let properties = schema
1036            .get("properties")
1037            .and_then(|p| p.as_object())
1038            .unwrap();
1039
1040        // 应该包含两个 profile 的配置
1041        assert_eq!(properties.len(), 2);
1042        assert!(properties.contains_key("debug-config"));
1043        assert!(properties.contains_key("release-config"));
1044    }
1045
1046    #[test]
1047    fn test_find_schema_in_target_not_exists() {
1048        use tempfile::TempDir;
1049
1050        let temp_dir = TempDir::new().unwrap();
1051        let workspace_path = temp_dir.path();
1052
1053        // target 目录不存在
1054        let found = SchemaProvider::find_schema_in_target(workspace_path);
1055        assert!(found.is_none());
1056    }
1057
1058    #[test]
1059    fn test_load_local_schema_file() {
1060        use std::fs;
1061        use tempfile::TempDir;
1062
1063        let temp_dir = TempDir::new().unwrap();
1064        let schema_path = temp_dir.path().join("test-schema.json");
1065
1066        let schema_content = serde_json::json!({
1067            "properties": {
1068                "web": {
1069                    "type": "object",
1070                    "properties": {
1071                        "port": {
1072                            "type": "integer",
1073                            "default": 8080
1074                        }
1075                    }
1076                },
1077                "database": {
1078                    "type": "object",
1079                    "properties": {
1080                        "url": {
1081                            "type": "string"
1082                        }
1083                    }
1084                }
1085            }
1086        });
1087
1088        fs::write(
1089            &schema_path,
1090            serde_json::to_string_pretty(&schema_content).unwrap(),
1091        )
1092        .unwrap();
1093
1094        let schemas = SchemaProvider::load_local_schema_file(&schema_path).unwrap();
1095        assert_eq!(schemas.len(), 2);
1096        assert!(schemas.contains_key("web"));
1097        assert!(schemas.contains_key("database"));
1098    }
1099
1100    #[test]
1101    #[serial]
1102    fn test_merge_schema_files() {
1103        use std::fs;
1104        use tempfile::TempDir;
1105
1106        let temp_dir = TempDir::new().unwrap();
1107
1108        // 创建第一个 Schema 文件(crate1)
1109        let schema1_path = temp_dir.path().join("schema1.json");
1110        let schema1_content = serde_json::json!({
1111            "properties": {
1112                "service-a": {
1113                    "type": "object",
1114                    "properties": {
1115                        "endpoint": {
1116                            "type": "string"
1117                        }
1118                    }
1119                }
1120            }
1121        });
1122        fs::write(
1123            &schema1_path,
1124            serde_json::to_string_pretty(&schema1_content).unwrap(),
1125        )
1126        .unwrap();
1127
1128        // 创建第二个 Schema 文件(crate2)
1129        let schema2_path = temp_dir.path().join("schema2.json");
1130        let schema2_content = serde_json::json!({
1131            "properties": {
1132                "service-b": {
1133                    "type": "object",
1134                    "properties": {
1135                        "port": {
1136                            "type": "integer"
1137                        }
1138                    }
1139                }
1140            }
1141        });
1142        fs::write(
1143            &schema2_path,
1144            serde_json::to_string_pretty(&schema2_content).unwrap(),
1145        )
1146        .unwrap();
1147
1148        // 模拟文件时间戳
1149        let time1 = std::time::SystemTime::now();
1150        let time2 = std::time::SystemTime::now();
1151
1152        let schema_files = vec![(schema1_path, time1), (schema2_path, time2)];
1153
1154        // 合并 Schema 文件
1155        let merged_path = SchemaProvider::merge_schema_files(&schema_files).unwrap();
1156        assert!(merged_path.exists());
1157
1158        // 验证合并结果
1159        let merged_content = fs::read_to_string(&merged_path).unwrap();
1160        let merged_schema: serde_json::Value = serde_json::from_str(&merged_content).unwrap();
1161
1162        let properties = merged_schema
1163            .get("properties")
1164            .and_then(|p| p.as_object())
1165            .unwrap();
1166
1167        // 应该包含两个 crate 的配置
1168        assert_eq!(properties.len(), 2);
1169        assert!(properties.contains_key("service-a"));
1170        assert!(properties.contains_key("service-b"));
1171    }
1172
1173    #[test]
1174    #[serial]
1175    fn test_find_schema_in_target_multiple_crates() {
1176        use std::fs;
1177        use tempfile::TempDir;
1178
1179        let temp_dir = TempDir::new().unwrap();
1180        let workspace_path = temp_dir.path();
1181
1182        // 创建多个 crate 的 Schema 文件
1183        let crate1_dir = workspace_path.join("target/debug/build/crate1/out");
1184        fs::create_dir_all(&crate1_dir).unwrap();
1185        let schema1_path = crate1_dir.join("summer-lsp.schema.json");
1186        fs::write(
1187            &schema1_path,
1188            serde_json::json!({
1189                "properties": {
1190                    "service-a": {
1191                        "type": "object"
1192                    }
1193                }
1194            })
1195            .to_string(),
1196        )
1197        .unwrap();
1198
1199        let crate2_dir = workspace_path.join("target/debug/build/crate2/out");
1200        fs::create_dir_all(&crate2_dir).unwrap();
1201        let schema2_path = crate2_dir.join("summer-lsp.schema.json");
1202        fs::write(
1203            &schema2_path,
1204            serde_json::json!({
1205                "properties": {
1206                    "service-b": {
1207                        "type": "object"
1208                    }
1209                }
1210            })
1211            .to_string(),
1212        )
1213        .unwrap();
1214
1215        // 查找并合并
1216        let found = SchemaProvider::find_schema_in_target(workspace_path);
1217        assert!(found.is_some());
1218
1219        let merged_path = found.unwrap();
1220        let content = fs::read_to_string(&merged_path).unwrap();
1221        let schema: serde_json::Value = serde_json::from_str(&content).unwrap();
1222
1223        let properties = schema
1224            .get("properties")
1225            .and_then(|p| p.as_object())
1226            .unwrap();
1227
1228        // 应该包含两个 crate 的配置
1229        assert_eq!(properties.len(), 2);
1230        assert!(properties.contains_key("service-a"));
1231        assert!(properties.contains_key("service-b"));
1232    }
1233}