Rusty Refactor
A powerful VSCode extension for extracting Rust code into modules with intelligent type detection, impl block preservation, and RustyRoad-aware file navigation.
✨ Features
🤖 AI-Powered Documentation
- Automatic Documentation Generation: Uses GPT-4o-mini to generate comprehensive Rust documentation
- Smart Summaries: AI-generated extraction summaries in the original file
- Copilot Integration: Leverages GitHub Copilot's language models
- Configurable: Can be set to always ask, always generate, or never generate
- Intelligent Import Analysis: Only copies imports that are actually used in the extracted code
- Rust-Analyzer Cleanup: Automatically removes unused imports after extraction using LSP diagnostics
- Implementation Block Preservation: Automatically wraps extracted methods in implblocks when extracting from implementations
- Type Detection: Identifies types, traits, and dependencies in your selected code
- Automatic Module Registration: Finds parent module and properly registers with pub modandpub use
📁 RustyRoad Integration
Built specifically for RustyRoad projects with Rails-like conventions:
- Auto-suggests conventional directories: controllers,models,views,services,middleware, etc.
- Browse project structure: Intuitive directory navigation with QuickPick
- Create directories on-the-fly: Automatically creates suggested RustyRoad directories
- Extract with File Browser ⭐ (Recommended) - 
- Visual directory browsing
- RustyRoad directory suggestions
- Create new directories as needed
 
- Quick Extract - 
- Uses default path from settings
- Fastest for standard workflows
 
- Custom Path Extract - 
- Type exact file path manually
- For advanced users
 
📖 Usage Examples
Before - Original file src/models/email.rs:
use super::Email;
use rustyroad::database::{Database, PoolConnection};
impl Email {
    pub fn new(
        to_field: String,
        from_field: String,
        subject: String,
        body: String,
    ) -> Self {
        Self {
            id: 0,
            to_field,
            from_field,
            subject,
            body,
            status: "pending".to_string(),
        }
    }
    pub async fn create(email: Email) -> Result<Self, sqlx::Error> {
        let sql = r#"
            INSERT INTO emails (to_field, from_field, status)
            VALUES ($1, $2, $3)
            RETURNING *
        "#;
        
        let database = Database::get_database_from_rustyroad_toml().unwrap();
        let pool = Database::get_db_pool(database).await.unwrap();
        
        sqlx::query_as::<_, Self>(sql)
            .bind(&email.to_field)
            .bind(&email.from_field)
            .bind(&email.status)
            .fetch_one(&pool)
            .await
    }
}
Steps:
- Select the createmethod (just the method, not the entire impl block)
- Right-click → "Rusty Refactor: Extract to Module (Browse Files)"
- Enter module name: email_repository
- Browse to: src/models/repositories/or create it
- Confirm
After - New file src/models/repositories/email_repository.rs:
//! Email repository module
//!
//! This module was automatically extracted by Rusty Refactor.
// Imports from original file
use super::Email;
use rustyroad::database::{Database, PoolConnection};
impl Email {
    pub async fn create(email: Email) -> Result<Self, sqlx::Error> {
        let sql = r#"
            INSERT INTO emails (to_field, from_field, status)
            VALUES ($1, $2, $3)
            RETURNING *
        "#;
        
        let database = Database::get_database_from_rustyroad_toml().unwrap();
        let pool = Database::get_db_pool(database).await.unwrap();
        
        sqlx::query_as::<_, Self>(sql)
            .bind(&email.to_field)
            .bind(&email.from_field)
            .bind(&email.status)
            .fetch_one(&pool)
            .await
    }
}
Updated - Original file src/models/email.rs:
use super::Email;
use rustyroad::database::{Database, PoolConnection};
pub mod repositories;
pub use repositories::email_repository::*;
impl Email {
    pub fn new(
        to_field: String,
        from_field: String,
        subject: String,
        body: String,
    ) -> Self {
        Self {
            id: 0,
            to_field,
            from_field,
            subject,
            body,
            status: "pending".to_string(),
        }
    }
    // Code extracted to src/models/repositories/email_repository.rs
    // Available as: email_repository::*
}
Updated - Parent module file src/models/mod.rs or src/lib.rs:
pub mod repositories;
pub use repositories::*;
Example 2: Extracting Standalone Functions
Before:
use std::collections::HashMap;
pub fn calculate_total(items: &[Item]) -> f64 {
    items.iter().map(|i| i.price).sum()
}
pub fn apply_discount(total: f64, discount: f64) -> f64 {
    total * (1.0 - discount)
}
Steps:
- Select both functions
- Extract to src/utils/pricing.rs
After - src/utils/pricing.rs:
//! Pricing module
// Imports from original file
use std::collections::HashMap;
pub fn calculate_total(items: &[Item]) -> f64 {
    items.iter().map(|i| i.price).sum()
}
pub fn apply_discount(total: f64, discount: f64) -> f64 {
    total * (1.0 - discount)
}
🎨 How It Works
Import Handling
Rusty Refactor intelligently copies all imports from your original file to the new module. This means:
- ✅ External crate imports are preserved
- ✅ Local module imports are maintained
- ✅ No more compiler errors from missing imports
- ✅ Only adds use super::*for types defined in the parent module (like struct definitions for impl blocks)
Implementation Block Detection
When you select code inside an impl block:
- Detects you're inside an impl Emailorimpl Trait for Type
- Automatically wraps extracted code in the same impl structure
- Imports the target type from parent module if needed
- Preserves trait implementations correctly
Module Registration
The extension automatically:
- Finds the correct parent module file (mod.rs,lib.rs, ormain.rs)
- Adds pub mod module_name;with proper path attributes if needed
- Adds pub use module_name::*;to re-export public items
- Handles nested module structures
⚙️ Configuration
{
  // Enable RustyRoad conventions (controllers, models, etc.)
  "rustyRefactor.rustyRoadMode": true,
  
  // Default path for quick extract
  "rustyRefactor.defaultModulePath": "src",
  
  // Auto-format after refactoring
  "rustyRefactor.autoFormatAfterRefactor": true,
  
  // Add doc comments to extracted modules
  "rustyRefactor.addModuleDocComments": true,
  
  // Integrate with rust-analyzer
  "rustyRefactor.integrationWithRustAnalyzer": true,
  
  // AI Documentation (requires GitHub Copilot)
  "rustyRefactor.aiAutoDocumentation": false,  // Set to true to always generate
  "rustyRefactor.aiAskEachTime": true,         // Ask before generating each time
  
  // Maximum directory depth for file search
  "rustyRefactor.searchDepth": 5
}
AI Documentation Setup
- Install GitHub Copilot: The AI documentation feature requires an active GitHub Copilot subscription
- Enable in Settings: Set rustyRefactor.aiAutoDocumentationtotruefor automatic generation
- Or Choose Each Time: Keep rustyRefactor.aiAskEachTimeastrueto be prompted each extraction
AI Documentation Example:
Before extraction, your code might look like this:
pub async fn create(email: Email) -> Result<Self, sqlx::Error> {
    let sql = r#"INSERT INTO emails..."#;
    // ... implementation
}
After AI documentation generation:
/// Creates a new email record in the database
///
/// This function inserts a new email entry into the emails table with the provided
/// email data and returns the created record with all database-generated fields.
///
/// # Arguments
///
/// * `email` - The email data to be inserted
///
/// # Returns
///
/// Returns `Result<Self, sqlx::Error>` containing the created email record with
/// database-generated fields populated, or an error if the insertion fails.
///
/// # Errors
///
/// This function will return an error if:
/// * Database connection fails
/// * SQL query execution fails
/// * Email data validation fails
///
/// # Example
///
/// ```rust
/// let email = Email::new(...);
/// let created = Email::create(email).await?;
/// ```
pub async fn create(email: Email) -> Result<Self, sqlx::Error> {
    let sql = r#"INSERT INTO emails..."#;
    // ... implementation
}
🚦 Commands
| Command | Description | Shortcut | 
| Rusty Refactor: Extract to Module (Browse Files) | Browse and select destination directory | Right-click menu | 
| Rusty Refactor: Extract to Module | Quick extract to default path | Right-click menu | 
| Rusty Refactor: Extract to Module (Custom Path) | Manually type file path | Right-click menu | 
📋 Requirements
- VSCode 1.80.0 or higher
- Rust project with Cargo.toml
- rust-analyzer extension (recommended for best results)
🔧 Installation
- Install from VSCode Marketplace: Search for "Rusty Refactor"
- Or install from VSIX: code --install-extension rusty-refactor-0.2.0.vsix
🎯 RustyRoad Project Structure
This extension works great with RustyRoad's Rails-inspired structure:
src/
├── controllers/       # HTTP request handlers
├── models/           # Data models and business logic
│   └── repositories/ # Database access layer
├── views/            # Template rendering
├── services/         # Business logic services
├── middleware/       # Request/response middleware
├── helpers/          # Helper functions
├── utils/            # Utility functions
├── config/           # Configuration
├── routes/           # Route definitions
└── lib.rs or main.rs
When you use "Extract with File Browser", these directories are suggested automatically!
🐛 Troubleshooting
Imports not working?
- The extension copies all imports from your original file
- If you still get errors, check that the types are accessible from the new module location
- You may need to adjust paths if moving between different module hierarchies
Module not registered in parent?
- Check that your project has a proper module structure (mod.rs,lib.rs, ormain.rs)
- The extension looks for parent modules automatically
- You can manually add pub mod module_name;if needed
Impl block not preserved?
- Make sure you're selecting code from inside an impl block
- The extension detects the impl context automatically
- If issues persist, try selecting the entire impl block
🤝 Contributing
Contributions welcome! Please open issues or PRs on GitHub.
📄 License
MIT License - See LICENSE file for details
🙏 Acknowledgments
- Built for the RustyRoad framework
- Inspired by Rails conventions and Ruby refactoring tools
- Powered by rust-analyzer LSP integration
Happy Refactoring! 🦀✨