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
JavaScript
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;
}
}
}