Skip to content

Scanner — Rust

Rust

Classes

RustScanner

class RustScanner implements Scanner
TypeScript

Use RustScanner to extract public API elements from Rust source files so skrypt can generate documentation for your Rust codebase.

Reach for this when you're integrating skrypt into a pipeline that processes Rust code — it's the scanner that powers skrypt generate for .rs files. It fits into the broader scanner pattern alongside TypeScript, Python, and Go scanners, all of which share the same Scanner interface.

RustScanner reads each .rs file and extracts pub functions, structs, enums, impl blocks, and traits. It automatically skips test files (anything under a /tests/ directory) so your generated docs stay focused on your public API surface.

Parameters — canHandle(filePath)

NameTypeRequiredDescription
filePathstringYesAbsolute or relative path to the file — returns true only for .rs files outside /tests/ directories

Parameters — scanFile(filePath)

NameTypeRequiredDescription
filePathstringYesPath to the .rs file to scan — must be readable by the process running skrypt

Returns

scanFile returns a Promise<ScanResult> containing the extracted APIElement[] — each element carries the name, signature, parameters, return type, and source context needed for AI doc generation. Pass the result directly to skrypt's doc generation step, or collect results across multiple files before generating.

Heads up

  • RustScanner only surfaces pub items — private functions, structs, and enums are intentionally excluded since they're not part of your public API.
  • Files anywhere in a path containing /tests/ are skipped entirely, including integration test files that happen to define public helpers.

Example:

// Inline types matching the Scanner interface shape
interface Parameter {
  name: string
  type: string
  required: boolean
  description: string
}

interface APIElement {
  name: string
  signature: string
  parameters: Parameter[]
  returnType: string
  sourceContext: string
  language: string
}

interface ScanResult {
  elements: APIElement[]
  filePath: string
  language: string
}

interface Scanner {
  languages: string[]
  canHandle(filePath: string): boolean
  scanFile(filePath: string): Promise<ScanResult>
}

// Self-contained mock of RustScanner behavior
class RustScanner implements Scanner {
  languages = ['rust']

  canHandle(filePath: string): boolean {
    return /\.rs$/.test(filePath) && !filePath.includes('/tests/')
  }

  async scanFile(filePath: string): Promise<ScanResult> {
    // In production, reads the actual file — here we simulate a parsed result
    const mockElements: APIElement[] = [
      {
        name: 'connect',
        signature: 'pub fn connect(url: &str, timeout_ms: u64) -> Result<Connection, DbError>',
        parameters: [
          { name: 'url', type: '&str', required: true, description: 'Database connection string' },
          { name: 'timeout_ms', type: 'u64', required: true, description: 'Connection timeout in milliseconds' },
        ],
        returnType: 'Result<Connection, DbError>',
        sourceContext: 'pub fn connect(url: &str, timeout_ms: u64) -> Result<Connection, DbError> {',
        language: 'rust',
      },
      {
        name: 'QueryBuilder',
        signature: 'pub struct QueryBuilder',
        parameters: [],
        returnType: '',
        sourceContext: 'pub struct QueryBuilder {',
        language: 'rust',
      },
    ]

    return {
      elements: mockElements,
      filePath,
      language: 'rust',
    }
  }
}

async function main() {
  const scanner = new RustScanner()

  const filesToProcess = [
    'src/db/connection.rs',
    'src/db/query.rs',
    'src/db/tests/connection_test.rs', // will be skipped
  ]

  const eligibleFiles = filesToProcess.filter(f => scanner.canHandle(f))
  console.log('Files to scan:', eligibleFiles)
  // → ['src/db/connection.rs', 'src/db/query.rs']

  try {
    const results = await Promise.all(
      eligibleFiles.map(f => scanner.scanFile(f))
    )

    const allElements = results.flatMap(r => r.elements)
    console.log(`Extracted ${allElements.length} public API elements`)
    console.log('First element:', JSON.stringify(allElements[0], null, 2))
    // → { name: 'connect', signature: 'pub fn connect(...)', ... }
  } catch (err) {
    console.error('Scan failed:', err)
  }
}

main()
TypeScript

Methods

canHandle

canHandle(filePath: string): boolean
TypeScript

Use canHandle to determine whether RustScanner is the right scanner for a given file before attempting to extract API elements from it.

Call this before invoking scanFile to route files to the correct scanner — especially when building a pipeline that processes mixed-language codebases where TypeScript, Python, Go, and Rust files coexist in the same directory tree.

canHandle returns true only for files with a .rs extension that are not inside a /tests/ directory. Test files are excluded because they typically contain private, non-public-facing code that shouldn't appear in generated documentation.

NameTypeRequiredDescription
filePathstringYesAbsolute or relative path to the file being evaluated. Paths containing /tests/ are automatically excluded, even if they end in .rs.

Returns true if the file is a Rust source file that RustScanner can process, false otherwise. Use the result to gate calls to scanFile — only pass files where canHandle returns true to avoid unnecessary processing or errors.

Heads up:

  • The /tests/ check matches anywhere in the path, so src/tests/helpers.rs, tests/mod.rs, and lib/tests/fixtures.rs are all excluded. If your project uses a different test directory convention (e.g., __tests__), those files will pass through.

Example:

// Inline types to keep this self-contained
interface Scanner {
  languages: string[];
  canHandle(filePath: string): boolean;
}

// Minimal RustScanner implementation matching the real behavior
class RustScanner implements Scanner {
  languages = ['rust'];

  canHandle(filePath: string): boolean {
    return /\.rs$/.test(filePath) && !filePath.includes('/tests/');
  }
}

const scanner = new RustScanner();

const files = [
  'src/lib.rs',
  'src/models/user.rs',
  'src/tests/user_test.rs',
  'tests/integration.rs',
  'src/main.py',
  'README.md',
];

const scannable = files.filter(f => scanner.canHandle(f));

console.log('Files RustScanner will process:');
scannable.forEach(f => console.log(`  ✓ ${f}`));

console.log('\nFiles skipped:');
files.filter(f => !scanner.canHandle(f)).forEach(f => console.log(`  ✗ ${f}`));

// Expected output:
// Files RustScanner will process:
//   ✓ src/lib.rs
//   ✓ src/models/user.rs
//
// Files skipped:
//   ✗ src/tests/user_test.rs
//   ✗ tests/integration.rs
//   ✗ src/main.py
//   ✗ README.md
TypeScript

scanFile

async scanFile(filePath: string): Promise<ScanResult>
TypeScript

Use scanFile to extract all public API elements from a Rust source file and prepare them for documentation generation.

Reach for this when you're building a documentation pipeline and need to parse a .rs file into structured API data — functions, structs, enums, and their signatures — before passing that data to an AI generation step.

RustScanner reads the file synchronously, walks its lines, and extracts API elements along with any parse errors encountered. It skips test files (paths containing /tests/) automatically, so you can safely pass glob results without filtering first.

Parameters

NameTypeRequiredDescription
filePathstringYesAbsolute or relative path to the .rs file to scan. Must not be a test file — paths containing /tests/ are rejected by canHandle and will produce an empty result.

Returns

Returns a Promise<ScanResult> containing an elements array of discovered API items and an errors array of any lines that failed to parse. Pass elements directly to your AI documentation generator, or inspect errors to surface files that may need manual review.

Heads up

  • scanFile does not call canHandle internally — if you pass a non-.rs file or a test file path, it will attempt to scan it anyway. Call canHandle(filePath) first to guard against this.
  • Parse errors are collected into result.errors rather than thrown, so a non-empty errors array doesn't reject the promise. Always check errors.length if completeness matters.

Example:

import { readFileSync, writeFileSync } from "fs";
import { join } from "path";

// Inline types (do not import from autodocs)
interface Parameter {
  name: string;
  type: string;
  description: string;
}

interface APIElement {
  name: string;
  kind: "function" | "struct" | "enum" | "trait";
  signature: string;
  parameters: Parameter[];
  returnType?: string;
  sourceContext: string;
  lineNumber: number;
}

interface ScanResult {
  elements: APIElement[];
  errors: string[];
}

// Minimal RustScanner mock — mirrors real behavior
class RustScanner {
  canHandle(filePath: string): boolean {
    return /\.rs$/.test(filePath) && !filePath.includes("/tests/");
  }

  async scanFile(filePath: string): Promise<ScanResult> {
    const source = readFileSync(filePath, "utf-8");
    const elements: APIElement[] = [];
    const errors: string[] = [];
    const lines = source.split("\n");

    lines.forEach((line, index) => {
      const fnMatch = line.match(/^pub fn (\w+)\(([^)]*)\)(?:\s*->\s*(.+))?/);
      if (fnMatch) {
        elements.push({
          name: fnMatch[1],
          kind: "function",
          signature: line.trim(),
          parameters: [],
          returnType: fnMatch[3]?.trim(),
          sourceContext: lines.slice(Math.max(0, index - 1), index + 3).join("\n"),
          lineNumber: index + 1,
        });
      }
    });

    return { elements, errors };
  }
}

// Create a temporary Rust file to scan
const tmpPath = join(process.cwd(), "example_lib.rs");
writeFileSync(
  tmpPath,
  `
pub fn create_user(name: &str, email: &str) -> Result<User, Error> {
    // implementation
}

pub fn delete_user(id: u64) -> bool {
    // implementation
}
`.trim()
);

async function main() {
  const scanner = new RustScanner();

  if (!scanner.canHandle(tmpPath)) {
    console.error("File is not a scannable Rust source file.");
    process.exit(1);
  }

  try {
    const result = await scanner.scanFile(tmpPath);

    console.log(`Scanned ${result.elements.length} API elements:`);
    result.elements.forEach((el) => {
      console.log(`  [${el.kind}] ${el.name} — line ${el.lineNumber}`);
      if (el.returnType) console.log(`    returns: ${el.returnType}`);
    });

    if (result.errors.length > 0) {
      console.warn(`Parse errors (${result.errors.length}):`);
      result.errors.forEach((e) => console.warn(" ", e));
    }

    // Expected output:
    // Scanned 2 API elements:
    //   [function] create_user — line 1
    //     returns: Result<User, Error>
    //   [function] delete_user — line 5
    //     returns: bool
  } catch (err) {
    console.error("Failed to scan file:", err);
  }
}

main();
TypeScript
Was this helpful?