autosnippet
Version:
Extract code patterns into a knowledge base for AI coding assistants
229 lines (228 loc) • 9.35 kB
JavaScript
/**
* 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();