UNPKG

autosnippet

Version:

Extract code patterns into a knowledge base for AI coding assistants

229 lines (228 loc) 9.35 kB
/** * Rust Web Enhancement Pack * 条件: { languages: ['rust'], frameworks: ['actix-web', 'axum', 'rocket', 'warp'] } * * 覆盖主流 Rust HTTP 框架: * - Axum (tower-based, 最流行) * - Actix-web (actor 模型) * - Rocket (宏驱动) * - Warp (filter 组合) */ import { EnhancementPack } from './EnhancementPack.js'; class RustWebEnhancement extends EnhancementPack { get id() { return 'rust-web'; } get displayName() { return 'Rust Web (Axum/Actix-web/Rocket/Warp) Enhancement'; } get conditions() { return { languages: ['rust'], frameworks: ['actix-web', 'axum', 'rocket', 'warp'], }; } getExtraDimensions() { return [ { id: 'rust-handler-scan', label: 'HTTP Handler 分析', guide: 'Rust Web Handler 与 Middleware/Layer 分析 — handler 函数签名 (Extractor 模式)、State/AppData 共享、Tower Layer/Service 注册顺序、错误响应统一化 (IntoResponse)', tierHint: 2, knowledgeTypes: ['architecture', 'code-pattern'], skillWorthy: true, dualOutput: true, skillMeta: { name: 'project-rust-handlers', description: 'Rust web handler patterns, extractors, state management and middleware layers (auto-generated by enhancement)', }, }, { id: 'rust-route-scan', label: 'API Route 结构', guide: 'Rust 路由结构分析 — Router::new().route() / web::resource() 路由表、路由嵌套与合并 (nest/merge)、HTTP 方法分布、Path/Query/Json Extractor 使用', tierHint: 2, knowledgeTypes: ['architecture'], skillWorthy: true, dualOutput: true, skillMeta: { name: 'project-rust-routes', description: 'Rust API route structure, nesting/merging and extractor usage (auto-generated by enhancement)', }, }, ]; } getGuardRules() { return [ { ruleId: 'rust-web-unwrap-in-handler', category: 'correctness', dimension: 'file', severity: 'warning', languages: ['rust'], pattern: /\.unwrap\(\)/, message: 'Web handler 中避免 .unwrap(),应使用 ? 运算符配合自定义错误类型实现 IntoResponse,防止 500 panic', }, { ruleId: 'rust-web-blocking-in-async', category: 'performance', dimension: 'file', severity: 'warning', languages: ['rust'], pattern: /std::fs::|std::net::|std::thread::sleep/, message: 'async handler 中不应使用 std 阻塞 I/O,应使用 tokio::fs / tokio::net / tokio::time::sleep 替代', }, { ruleId: 'rust-web-clone-state', category: 'performance', dimension: 'file', severity: 'info', languages: ['rust'], pattern: /\.clone\(\)\s*[;,]\s*(?:\/\/.*)?$/m, message: '频繁 .clone() 共享状态可能有性能问题,考虑使用 Arc<T> 或 Axum State extractor / Actix Data<T>', }, ]; } detectPatterns(astSummary) { const patterns = []; // ── Handler functions (async fn with web extractor params) ── for (const m of astSummary.methods || []) { if (!m.className && m.isAsync) { const nameLower = m.name.toLowerCase(); if (nameLower.startsWith('get_') || nameLower.startsWith('post_') || nameLower.startsWith('put_') || nameLower.startsWith('delete_') || nameLower.startsWith('patch_') || nameLower.startsWith('handle_') || nameLower.startsWith('list_') || nameLower.startsWith('create_') || nameLower.startsWith('update_') || nameLower.startsWith('remove_') || nameLower === 'index' || nameLower === 'health' || nameLower === 'health_check') { patterns.push({ type: 'rust-web-handler', methodName: m.name, line: m.line, confidence: 0.75, }); } } } // ── Structs used as State / AppData ── for (const cls of astSummary.classes || []) { if (cls.kind !== 'struct') { continue; } const nameLower = cls.name.toLowerCase(); if (nameLower.includes('state') || nameLower.includes('appdata') || nameLower.includes('appstate') || nameLower.includes('context') || nameLower.includes('config')) { patterns.push({ type: 'rust-web-state', className: cls.name, line: cls.line, confidence: 0.7, }); } } // ── Error response types (impl IntoResponse / ResponseError) ── for (const cls of astSummary.classes || []) { const nameLower = cls.name.toLowerCase(); if (nameLower.includes('error') || nameLower.includes('apierror') || nameLower.includes('appresponse')) { patterns.push({ type: 'rust-web-error-type', className: cls.name, line: cls.line, confidence: 0.7, }); } } // ── Middleware / Layer structs ── for (const cls of astSummary.classes || []) { if (cls.kind !== 'struct') { continue; } const nameLower = cls.name.toLowerCase(); if (nameLower.includes('middleware') || nameLower.includes('layer') || nameLower.includes('auth') || nameLower.includes('cors') || nameLower.includes('ratelimit') || nameLower.includes('logging')) { patterns.push({ type: 'rust-web-middleware', className: cls.name, line: cls.line, confidence: 0.7, }); } } // ── Extractor structs (used as handler params) ── for (const cls of astSummary.classes || []) { if (cls.kind !== 'struct') { continue; } // Derive-heavy DTOs used as Json<T>, Query<T>, Path<T> if (cls.derives && cls.derives.length >= 2) { const hasSerdeDerive = cls.derives.some((d) => d === 'Deserialize' || d === 'Serialize'); if (hasSerdeDerive) { const nameLower = cls.name.toLowerCase(); if (nameLower.includes('request') || nameLower.includes('payload') || nameLower.includes('params') || nameLower.includes('query') || nameLower.includes('body') || nameLower.includes('form') || nameLower.includes('dto')) { patterns.push({ type: 'rust-web-extractor-dto', className: cls.name, line: cls.line, confidence: 0.85, }); } } } } // ── Response structs ── for (const cls of astSummary.classes || []) { if (cls.kind !== 'struct') { continue; } const nameLower = cls.name.toLowerCase(); if (nameLower.includes('response') || nameLower.endsWith('resp') || nameLower.endsWith('reply')) { patterns.push({ type: 'rust-web-response-dto', className: cls.name, line: cls.line, confidence: 0.75, }); } } // ── Web framework imports ── const webImports = (astSummary.imports || []).filter((imp) => imp.includes('actix_web') || imp.includes('axum') || imp.includes('rocket') || imp.includes('warp') || imp.includes('tower') || imp.includes('tower_http')); if (webImports.length > 0) { patterns.push({ type: 'rust-web-framework-usage', importCount: webImports.length, confidence: 0.9, }); } return patterns; } } export const pack = new RustWebEnhancement();