UNPKG

vibe-coder-mcp

Version:

Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.

443 lines (442 loc) 18.3 kB
import { BaseLanguageHandler } from './base.js'; import { getNodeText } from '../astAnalyzer.js'; import logger from '../../../logger.js'; export class RustHandler extends BaseLanguageHandler { getFunctionQueryPatterns() { return [ 'function_item', 'function_signature_item', 'closure_expression', 'impl_method', 'trait_method' ]; } getClassQueryPatterns() { return [ 'struct_item', 'enum_item', 'trait_item', 'impl_item' ]; } getImportQueryPatterns() { return [ 'use_declaration', 'extern_crate_declaration' ]; } extractFunctionName(node, sourceCode, _options) { try { if (node.type === 'function_item' || node.type === 'function_signature_item') { const nameNode = node.childForFieldName('name'); if (nameNode) { const name = getNodeText(nameNode, sourceCode); if (name === 'main') { return 'main_entrypoint'; } if (this.hasAttribute(node, 'test')) { return `test_${name}`; } if (this.hasAttribute(node, 'bench')) { return `benchmark_${name}`; } return name; } } if (node.type === 'impl_method') { const nameNode = node.childForFieldName('name'); if (nameNode) { const name = getNodeText(nameNode, sourceCode); if (this.hasSelfParameter(node, sourceCode)) { return `self_${name}`; } return name; } } if (node.type === 'trait_method') { const nameNode = node.childForFieldName('name'); if (nameNode) { return getNodeText(nameNode, sourceCode); } } if (node.type === 'closure_expression') { if (node.parent?.type === 'let_declaration') { const patternNode = node.parent.childForFieldName('pattern'); if (patternNode) { return getNodeText(patternNode, sourceCode); } } if (node.parent?.type === 'arguments' && node.parent.parent?.type === 'call_expression') { const funcNode = node.parent.parent.childForFieldName('function'); if (funcNode) { const funcName = getNodeText(funcNode, sourceCode); if (['map', 'filter', 'for_each', 'fold'].includes(funcName)) { return `${funcName}_closure`; } } } return 'closure'; } return 'anonymous'; } catch (error) { logger.warn({ err: error, nodeType: node.type }, 'Error extracting Rust function name'); return 'anonymous'; } } hasAttribute(node, attributeName) { try { const attributesNode = node.childForFieldName('attributes'); if (!attributesNode) return false; for (let i = 0; i < attributesNode.childCount; i++) { const attribute = attributesNode.child(i); if (attribute?.text.includes(`#[${attributeName}]`)) { return true; } } return false; } catch (error) { logger.warn({ err: error, nodeType: node.type }, 'Error checking Rust attribute'); return false; } } hasSelfParameter(node, sourceCode) { try { const parametersNode = node.childForFieldName('parameters'); if (!parametersNode) return false; const firstParam = parametersNode.firstChild; if (firstParam) { const paramText = getNodeText(firstParam, sourceCode); return paramText.includes('self'); } return false; } catch (error) { logger.warn({ err: error, nodeType: node.type }, 'Error checking Rust self parameter'); return false; } } extractClassName(node, sourceCode) { try { if (node.type === 'struct_item' || node.type === 'enum_item' || node.type === 'trait_item') { const nameNode = node.childForFieldName('name'); if (nameNode) { return getNodeText(nameNode, sourceCode); } } else if (node.type === 'impl_item') { const typeNode = node.childForFieldName('type'); if (typeNode) { return `Impl_${getNodeText(typeNode, sourceCode)}`; } } return 'AnonymousType'; } catch (error) { logger.warn({ err: error, nodeType: node.type }, 'Error extracting Rust class name'); return 'AnonymousType'; } } extractImplementedInterfaces(node, sourceCode) { try { if (node.type === 'impl_item') { const traitNode = node.childForFieldName('trait'); if (traitNode) { return [getNodeText(traitNode, sourceCode)]; } } else if (node.type === 'struct_item') { const attributesNode = node.childForFieldName('attributes'); if (attributesNode) { const traits = []; for (let i = 0; i < attributesNode.childCount; i++) { const attribute = attributesNode.child(i); if (attribute?.text.includes('#[derive(')) { const deriveText = attribute.text; const match = deriveText.match(/#\[derive\((.*?)\)\]/); if (match && match[1]) { const derivedTraits = match[1].split(',').map(t => t.trim()); traits.push(...derivedTraits); } } } return traits.length > 0 ? traits : undefined; } } return undefined; } catch (error) { logger.warn({ err: error, nodeType: node.type }, 'Error extracting Rust implemented interfaces'); return undefined; } } extractImportPath(node, sourceCode) { try { if (node.type === 'use_declaration') { const treeNode = node.childForFieldName('tree'); if (treeNode) { return getNodeText(treeNode, sourceCode); } } else if (node.type === 'extern_crate_declaration') { const nameNode = node.childForFieldName('name'); if (nameNode) { return getNodeText(nameNode, sourceCode); } } return 'unknown'; } catch (error) { logger.warn({ err: error, nodeType: node.type }, 'Error extracting Rust import path'); return 'unknown'; } } extractImportedItems(node, sourceCode) { try { if (node.type === 'use_declaration') { const items = []; const treeNode = node.childForFieldName('tree'); if (treeNode) { const fullPath = getNodeText(treeNode, sourceCode); if (!fullPath.includes('{') && !fullPath.includes('::*')) { const parts = fullPath.split('::'); const name = parts[parts.length - 1]; items.push({ name, path: fullPath, isDefault: false, isNamespace: false, nodeText: node.text }); } else if (fullPath.endsWith('::*')) { const basePath = fullPath.substring(0, fullPath.length - 3); items.push({ name: '*', path: basePath, isDefault: false, isNamespace: true, nodeText: node.text }); } else if (fullPath.includes('{')) { const basePath = fullPath.substring(0, fullPath.indexOf('{')); const groupContent = fullPath.substring(fullPath.indexOf('{') + 1, fullPath.lastIndexOf('}')); const importItems = this.splitGroupedImports(groupContent); for (const item of importItems) { const trimmedItem = item.trim(); if (trimmedItem.includes(' as ')) { const [originalName, alias] = trimmedItem.split(' as ').map(s => s.trim()); items.push({ name: originalName, alias, path: basePath + originalName, isDefault: false, isNamespace: false, nodeText: trimmedItem }); } else if (trimmedItem.includes('{')) { const nestedBase = trimmedItem.substring(0, trimmedItem.indexOf('{')); const nestedContent = trimmedItem.substring(trimmedItem.indexOf('{') + 1, trimmedItem.lastIndexOf('}')); const nestedItems = this.splitGroupedImports(nestedContent); for (const nestedItem of nestedItems) { const trimmedNestedItem = nestedItem.trim(); if (trimmedNestedItem === 'self') { items.push({ name: nestedBase, path: basePath + nestedBase, isDefault: false, isNamespace: false, nodeText: trimmedNestedItem }); } else { items.push({ name: trimmedNestedItem, path: basePath + nestedBase + '::' + trimmedNestedItem, isDefault: false, isNamespace: false, nodeText: trimmedNestedItem }); } } } else if (trimmedItem === 'self') { const lastPart = basePath.split('::').filter(Boolean).pop() || ''; items.push({ name: lastPart, path: basePath.substring(0, basePath.length - 2), isDefault: false, isNamespace: false, nodeText: 'self' }); } else { items.push({ name: trimmedItem, path: basePath + trimmedItem, isDefault: false, isNamespace: false, nodeText: trimmedItem }); } } } } return items.length > 0 ? items : undefined; } else if (node.type === 'extern_crate_declaration') { const nameNode = node.childForFieldName('name'); if (nameNode) { const name = getNodeText(nameNode, sourceCode); const aliasNode = node.childForFieldName('alias'); if (aliasNode) { const alias = getNodeText(aliasNode, sourceCode); return [{ name, alias, path: name, isDefault: false, isNamespace: false, nodeText: node.text }]; } return [{ name, path: name, isDefault: false, isNamespace: false, nodeText: node.text }]; } } return undefined; } catch (error) { logger.warn({ err: error, nodeType: node.type }, 'Error extracting Rust imported items'); return undefined; } } splitGroupedImports(groupContent) { const result = []; let currentItem = ''; let braceDepth = 0; for (let i = 0; i < groupContent.length; i++) { const char = groupContent[i]; if (char === '{') { braceDepth++; currentItem += char; } else if (char === '}') { braceDepth--; currentItem += char; } else if (char === ',' && braceDepth === 0) { result.push(currentItem); currentItem = ''; } else { currentItem += char; } } if (currentItem.trim()) { result.push(currentItem); } return result; } extractFunctionComment(node, _sourceCode) { try { const attributesNode = node.childForFieldName('attributes'); if (attributesNode) { for (let i = 0; i < attributesNode.childCount; i++) { const attribute = attributesNode.child(i); if (attribute?.text.startsWith('///') || attribute?.text.startsWith('//!') || attribute?.text.includes('#[doc =')) { return this.parseRustDocComment(attribute.text); } } } return undefined; } catch (error) { logger.warn({ err: error, nodeType: node.type }, 'Error extracting Rust function comment'); return undefined; } } extractClassComment(node, _sourceCode) { try { const attributesNode = node.childForFieldName('attributes'); if (attributesNode) { for (let i = 0; i < attributesNode.childCount; i++) { const attribute = attributesNode.child(i); if (attribute?.text.startsWith('///') || attribute?.text.startsWith('//!') || attribute?.text.includes('#[doc =')) { return this.parseRustDocComment(attribute.text); } } } return undefined; } catch (error) { logger.warn({ err: error, nodeType: node.type }, 'Error extracting Rust class comment'); return undefined; } } parseRustDocComment(comment) { try { if (comment.startsWith('///')) { return comment.replace(/^\/\/\/\s*/mg, '').trim(); } else if (comment.startsWith('//!')) { return comment.replace(/^\/\/!\s*/mg, '').trim(); } else if (comment.includes('#[doc =')) { const match = comment.match(/#\[doc\s*=\s*"(.*?)"\]/); if (match && match[1]) { return match[1].replace(/\\"/g, '"').trim(); } } return comment; } catch (error) { logger.warn({ err: error }, 'Error parsing Rust doc comment'); return comment; } } detectFramework(sourceCode) { try { if (sourceCode.includes('actix_web') || sourceCode.includes('HttpServer') || sourceCode.includes('App::new()')) { return 'actix'; } if (sourceCode.includes('rocket') || sourceCode.includes('#[get(') || sourceCode.includes('#[post(')) { return 'rocket'; } if (sourceCode.includes('tokio') || sourceCode.includes('#[tokio::main]') || sourceCode.includes('async fn')) { return 'tokio'; } if (sourceCode.includes('diesel') || sourceCode.includes('table!') || sourceCode.includes('QueryDsl')) { return 'diesel'; } return null; } catch (error) { logger.warn({ err: error }, 'Error detecting Rust framework'); return null; } } }