cloudreve-api 0.9.0

A Rust library for interacting with Cloudreve API
Documentation
//! OpenAPI 规范验证器
//!
//! 此模块通过 MCP 工具读取 V3/V4 OpenAPI 规范,并验证代码实现是否与规范一致

use serde_json::Value;
use std::collections::HashMap;

/// OpenAPI 验证器
pub struct OpenApiValidator {
    v3_spec: Option<Value>,
    v4_spec: Option<Value>,
    #[allow(dead_code)]
    strict_mode: bool,
    #[allow(dead_code)]
    allow_extra_fields: bool,
}

/// 验证报告
#[derive(Debug, Default)]
pub struct ValidationReport {
    pub version: String,
    pub compliant: Vec<String>,
    pub warnings: HashMap<String, Vec<String>>,
    pub errors: HashMap<String, Vec<String>>,
    pub missing_implementations: Vec<String>,
    pub extra_implementations: Vec<String>,
}

impl OpenApiValidator {
    /// 创建新的验证器
    pub async fn new(
        strict_mode: bool,
        allow_extra_fields: bool,
    ) -> Result<Self, Box<dyn std::error::Error>> {
        Ok(Self {
            v3_spec: None, // 将通过 MCP 工具加载
            v4_spec: None,
            strict_mode,
            allow_extra_fields,
        })
    }

    /// 检查 V3 规范是否已加载
    pub fn has_v3_spec(&self) -> bool {
        self.v3_spec.is_some()
    }

    /// 检查 V4 规范是否已加载
    pub fn has_v4_spec(&self) -> bool {
        self.v4_spec.is_some()
    }

    /// 从 MCP 加载 V3 规范
    pub async fn load_v3_from_mcp(&mut self) -> Result<(), Box<dyn std::error::Error>> {
        // 这里将通过 MCP 工具读取 OpenAPI 规范
        // 在实际实现中,会调用 mcp__cloudrevev3-api__read_project_oas 工具
        println!("  [OpenAPI] 从 MCP 加载 V3 规范...");
        // 暂时设置为 None,实际使用时需要通过 MCP 工具获取
        self.v3_spec = Some(serde_json::json!({}));
        Ok(())
    }

    /// 从 MCP 加载 V4 规范
    pub async fn load_v4_from_mcp(&mut self) -> Result<(), Box<dyn std::error::Error>> {
        // 这里将通过 MCP 工具读取 OpenAPI 规范
        // 在实际实现中,会调用 mcp__cloudrevev4-api__read_project_oas 工具
        println!("  [OpenAPI] 从 MCP 加载 V4 规范...");
        // 暂时设置为 None,实际使用时需要通过 MCP 工具获取
        self.v4_spec = Some(serde_json::json!({}));
        Ok(())
    }

    /// 从缓存文件加载 V3 规范
    pub async fn load_v3_from_cache(&mut self) -> Result<(), Box<dyn std::error::Error>> {
        let cache_path = "tests/openapi/v3_spec_cache.json";
        match tokio::fs::read_to_string(cache_path).await {
            Ok(content) => {
                self.v3_spec = Some(serde_json::from_str(&content)?);
                println!("  [OpenAPI] 从缓存加载 V3 规范");
                Ok(())
            }
            Err(e) => {
                println!("  [OpenAPI] V3 缓存不存在: {}", e);
                Ok(())
            }
        }
    }

    /// 从缓存文件加载 V4 规范
    pub async fn load_v4_from_cache(&mut self) -> Result<(), Box<dyn std::error::Error>> {
        let cache_path = "tests/openapi/v4_spec_cache.json";
        match tokio::fs::read_to_string(cache_path).await {
            Ok(content) => {
                self.v4_spec = Some(serde_json::from_str(&content)?);
                println!("  [OpenAPI] 从缓存加载 V4 规范");
                Ok(())
            }
            Err(e) => {
                println!("  [OpenAPI] V4 缓存不存在: {}", e);
                Ok(())
            }
        }
    }

    /// 验证 V3 API 实现
    pub fn validate_v3(&self) -> ValidationReport {
        let mut report = ValidationReport {
            version: "V3".to_string(),
            ..Default::default()
        };

        // 如果没有加载规范,跳过验证
        if self.v3_spec.is_none() {
            println!("  [OpenAPI] V3 规范未加载,跳过验证");
            return report;
        }

        // 提取所有端点
        let spec_endpoints = self.extract_endpoints(self.v3_spec.as_ref().unwrap());

        // 获取已实现的端点(这里简化处理,实际应该扫描代码)
        let impl_endpoints = self.get_v3_implemented_endpoints();

        // 比对验证
        for endpoint in &spec_endpoints {
            if impl_endpoints.contains(endpoint) {
                report.compliant.push(endpoint.clone());
            } else {
                report.missing_implementations.push(endpoint.clone());
            }
        }

        // 检查额外实现
        for endpoint in &impl_endpoints {
            if !spec_endpoints.contains(endpoint) {
                report.extra_implementations.push(endpoint.clone());
            }
        }

        report
    }

    /// 验证 V4 API 实现
    pub fn validate_v4(&self) -> ValidationReport {
        let mut report = ValidationReport {
            version: "V4".to_string(),
            ..Default::default()
        };

        // 如果没有加载规范,跳过验证
        if self.v4_spec.is_none() {
            println!("  [OpenAPI] V4 规范未加载,跳过验证");
            return report;
        }

        // 提取所有端点
        let spec_endpoints = self.extract_endpoints(self.v4_spec.as_ref().unwrap());

        // 获取已实现的端点
        let impl_endpoints = self.get_v4_implemented_endpoints();

        // 比对验证
        for endpoint in &spec_endpoints {
            if impl_endpoints.contains(endpoint) {
                report.compliant.push(endpoint.clone());
            } else {
                report.missing_implementations.push(endpoint.clone());
            }
        }

        // 检查额外实现
        for endpoint in &impl_endpoints {
            if !spec_endpoints.contains(endpoint) {
                report.extra_implementations.push(endpoint.clone());
            }
        }

        report
    }

    /// 从 OpenAPI 规范中提取所有端点
    fn extract_endpoints(&self, spec: &Value) -> Vec<String> {
        let mut endpoints = Vec::new();

        if let Some(paths) = spec.get("paths").and_then(|p| p.as_object()) {
            for (path, path_spec) in paths {
                if let Some(methods) = path_spec.as_object() {
                    for (method, _) in methods {
                        if method != "$ref" {
                            endpoints.push(format!("{} {}", method.to_uppercase(), path));
                        }
                    }
                }
            }
        }

        endpoints
    }

    /// 获取 V3 已实现的端点列表
    fn get_v3_implemented_endpoints(&self) -> Vec<String> {
        // V3 已实现的端点列表(根据代码分析)
        vec![
            "POST /user/session".to_string(),
            "POST /user/2fa".to_string(),
            "DELETE /user/session".to_string(),
            "PUT /file/upload".to_string(),
            "POST /file/upload/{sessionID}/{index}".to_string(),
            "PUT /file/download/{id}".to_string(),
            "POST /file/source".to_string(),
            "GET /file/preview/{id}".to_string(),
            "GET /file/thumb/{id}".to_string(),
            "POST /file/create".to_string(),
            "GET /directory{path}".to_string(),
            "PUT /directory".to_string(),
            "GET /object/property/{id}".to_string(),
            "POST /object/rename".to_string(),
            "PATCH /object".to_string(),
            "DELETE /object".to_string(),
            "POST /object/copy".to_string(),
            "POST /share".to_string(),
            "GET /site/config".to_string(),
            "GET /site/ping".to_string(),
            "GET /user/setting".to_string(),
            "GET /user/storage".to_string(),
            "GET /user/avatar/{id}/l".to_string(),
            "GET /webdav/accounts".to_string(),
            "POST /aria2/url".to_string(),
            "GET /aria2/downloading".to_string(),
            "GET /aria2/finished".to_string(),
            "DELETE /aria2/task/{gid}".to_string(),
            "GET /user/setting/tasks".to_string(),
        ]
    }

    /// 获取 V4 已实现的端点列表
    fn get_v4_implemented_endpoints(&self) -> Vec<String> {
        // V4 已实现的端点列表(根据代码分析)
        vec![
            "GET /session/prepare".to_string(),
            "POST /session/token".to_string(),
            "POST /session/token/2fa".to_string(),
            "POST /session/token/refresh".to_string(),
            "DELETE /session/token".to_string(),
            "PUT /session/openid".to_string(),
            "POST /session/openid".to_string(),
            "PUT /session/authn".to_string(),
            "POST /session/authn".to_string(),
            "GET /file".to_string(),
            "DELETE /file/{uri}".to_string(),
            "POST /file/create".to_string(),
            "PUT /file/rename/{uri}".to_string(),
            "POST /file/move".to_string(),
            "POST /file/copy".to_string(),
            "POST /file/url".to_string(),
            "POST /file/restore".to_string(),
            "GET /file/{uri}".to_string(),
            "GET /file/stat/{uri}".to_string(),
            "GET /file/thumb".to_string(),
            "PUT /file/content".to_string(),
            "PUT /file/upload".to_string(),
            "POST /file/upload/{sessionId}/{index}".to_string(),
            "DELETE /file/upload".to_string(),
            "POST /file/permission".to_string(),
            "DELETE /file/permission".to_string(),
            "PATCH /file/metadata".to_string(),
            "PATCH /file/policy".to_string(),
            "PATCH /file/view".to_string(),
            "GET /file/activities".to_string(),
            "GET /file/archive".to_string(),
            "PUT /file/viewerSession".to_string(),
            "POST /user".to_string(),
            "GET /user/capacity".to_string(),
            "GET /user/search".to_string(),
            "GET /user/info/{user_id}".to_string(),
            "GET /user/avatar/{user_id}".to_string(),
            "GET /user/shares/{user_id}".to_string(),
            "GET /user/setting".to_string(),
            "PATCH /user/setting".to_string(),
            "GET /user/setting/policies".to_string(),
            "GET /user/setting/nodes".to_string(),
            "GET /user/setting/2fa".to_string(),
            "PUT /user/setting/avatar".to_string(),
            "PUT /user/authn".to_string(),
            "POST /user/authn".to_string(),
            "DELETE /user/authn".to_string(),
            "PUT /share".to_string(),
            "GET /share".to_string(),
            "POST /share/{id}".to_string(),
            "DELETE /share/{id}".to_string(),
            "GET /share/info/{id}".to_string(),
            "GET /devices/dav".to_string(),
            "PUT /devices/dav".to_string(),
            "PATCH /devices/dav/{id}".to_string(),
            "DELETE /devices/dav/{id}".to_string(),
            "POST /workflow/download".to_string(),
            "PATCH /workflow/download/{task_id}".to_string(),
            "DELETE /workflow/download/{task_id}".to_string(),
            "GET /workflow".to_string(),
            "GET /workflow/progress/{id}".to_string(),
            "POST /workflow/archive".to_string(),
            "POST /workflow/extract".to_string(),
            "GET /site/ping".to_string(),
            "GET /site/version".to_string(),
            "GET /site/config/{section}".to_string(),
            "POST /site/abuse".to_string(),
            "GET /site/captcha".to_string(),
        ]
    }
}

impl ValidationReport {
    /// 打印验证报告
    pub fn print(&self) {
        println!(
            "\n┌─ {} OpenAPI 验证报告 ───────────────────────────",
            self.version
        );
        println!("│  符合规范: {}", self.compliant.len());
        println!("│  警告: {}", self.warnings.len());
        println!("│  错误: {}", self.errors.len());
        println!("│  缺失实现: {}", self.missing_implementations.len());
        println!("│  额外实现: {}", self.extra_implementations.len());

        if !self.missing_implementations.is_empty() {
            println!("");
            println!("│  缺失的端点实现:");
            for endpoint in &self.missing_implementations {
                println!("│    - {}", endpoint);
            }
        }

        if !self.extra_implementations.is_empty() {
            println!("");
            println!("│  额外的端点实现:");
            for endpoint in &self.extra_implementations {
                println!("│    - {}", endpoint);
            }
        }

        println!("└─────────────────────────────────────────────────");
    }

    /// 是否有错误
    pub fn has_errors(&self) -> bool {
        !self.errors.is_empty() || !self.missing_implementations.is_empty()
    }

    /// 是否有警告
    #[allow(dead_code)]
    pub fn has_warnings(&self) -> bool {
        !self.warnings.is_empty() || !self.extra_implementations.is_empty()
    }
}