Initial commit
This commit is contained in:
429
commands/tokio-migrate.md
Normal file
429
commands/tokio-migrate.md
Normal file
@@ -0,0 +1,429 @@
|
||||
---
|
||||
name: tokio-migrate
|
||||
description: Migrate synchronous code to async Tokio or upgrade between Tokio versions
|
||||
---
|
||||
|
||||
# Tokio Migrate Command
|
||||
|
||||
This command assists with migrating synchronous code to async Tokio, upgrading between Tokio versions, or converting from other async runtimes.
|
||||
|
||||
## Arguments
|
||||
|
||||
- `$1` - Migration type: `sync-to-async`, `tokio-upgrade`, `runtime-switch` (required)
|
||||
- `$2` - Target file or directory (optional, defaults to current directory)
|
||||
- `$3` - Additional context: Tokio version for upgrades, or source runtime for switches (optional)
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
/rust-tokio-expert:tokio-migrate sync-to-async src/handlers/
|
||||
/rust-tokio-expert:tokio-migrate tokio-upgrade src/ 1.0
|
||||
/rust-tokio-expert:tokio-migrate runtime-switch src/ async-std
|
||||
```
|
||||
|
||||
## Workflow
|
||||
|
||||
### 1. Sync to Async Migration
|
||||
|
||||
When migrating synchronous code to async Tokio:
|
||||
|
||||
#### Analysis Phase
|
||||
|
||||
1. **Scan Target Files**
|
||||
- Use Glob to find all Rust files in target
|
||||
- Read files and identify synchronous operations
|
||||
- Detect blocking I/O operations
|
||||
- Find CPU-intensive operations
|
||||
- Identify thread spawning
|
||||
|
||||
2. **Identify Conversion Candidates**
|
||||
- Functions with I/O operations (network, file, database)
|
||||
- Functions that spawn threads
|
||||
- Functions with sleep/delays
|
||||
- Functions with synchronous HTTP clients
|
||||
- Functions with blocking mutex operations
|
||||
|
||||
3. **Analyze Dependencies**
|
||||
- Check `Cargo.toml` for sync crates
|
||||
- Identify replacements (e.g., `reqwest` blocking → async)
|
||||
- Find database drivers needing async versions
|
||||
|
||||
#### Migration Phase
|
||||
|
||||
4. **Invoke Agent**
|
||||
- Use Task tool with `subagent_type="rust-tokio-expert:tokio-pro"`
|
||||
- Provide code context and migration plan
|
||||
|
||||
5. **Convert Functions to Async**
|
||||
|
||||
The agent should transform:
|
||||
|
||||
**Synchronous Function:**
|
||||
```rust
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
|
||||
fn read_config(path: &str) -> Result<String, Error> {
|
||||
let mut file = File::open(path)?;
|
||||
let mut contents = String::new();
|
||||
file.read_to_string(&mut contents)?;
|
||||
Ok(contents)
|
||||
}
|
||||
```
|
||||
|
||||
**To Async:**
|
||||
```rust
|
||||
use tokio::fs::File;
|
||||
use tokio::io::AsyncReadExt;
|
||||
|
||||
async fn read_config(path: &str) -> Result<String, Error> {
|
||||
let mut file = File::open(path).await?;
|
||||
let mut contents = String::new();
|
||||
file.read_to_string(&mut contents).await?;
|
||||
Ok(contents)
|
||||
}
|
||||
```
|
||||
|
||||
6. **Replace Blocking Operations**
|
||||
|
||||
Convert common patterns:
|
||||
|
||||
**Thread Sleep → Async Sleep:**
|
||||
```rust
|
||||
// Before
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
fn wait() {
|
||||
thread::sleep(Duration::from_secs(1));
|
||||
}
|
||||
|
||||
// After
|
||||
use tokio::time::{sleep, Duration};
|
||||
|
||||
async fn wait() {
|
||||
sleep(Duration::from_secs(1)).await;
|
||||
}
|
||||
```
|
||||
|
||||
**Std Mutex → Tokio Mutex:**
|
||||
```rust
|
||||
// Before
|
||||
use std::sync::Mutex;
|
||||
|
||||
fn update_state(mutex: &Mutex<State>) {
|
||||
let mut state = mutex.lock().unwrap();
|
||||
state.update();
|
||||
}
|
||||
|
||||
// After
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
async fn update_state(mutex: &Mutex<State>) {
|
||||
let mut state = mutex.lock().await;
|
||||
state.update();
|
||||
}
|
||||
```
|
||||
|
||||
**Thread Spawning → Task Spawning:**
|
||||
```rust
|
||||
// Before
|
||||
use std::thread;
|
||||
|
||||
fn spawn_worker() {
|
||||
thread::spawn(|| {
|
||||
do_work();
|
||||
});
|
||||
}
|
||||
|
||||
// After
|
||||
use tokio::task;
|
||||
|
||||
async fn spawn_worker() {
|
||||
task::spawn(async {
|
||||
do_work().await;
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
7. **Update Dependencies in Cargo.toml**
|
||||
|
||||
Replace sync crates:
|
||||
```toml
|
||||
# Before
|
||||
[dependencies]
|
||||
reqwest = { version = "0.11", features = ["blocking"] }
|
||||
|
||||
# After
|
||||
[dependencies]
|
||||
reqwest = "0.11"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
```
|
||||
|
||||
8. **Add Runtime Setup**
|
||||
|
||||
Add to main.rs:
|
||||
```rust
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Your async code
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
9. **Handle CPU-Intensive Operations**
|
||||
|
||||
Wrap in `spawn_blocking`:
|
||||
```rust
|
||||
async fn process_data(data: Vec<u8>) -> Result<Vec<u8>, Error> {
|
||||
// CPU-intensive work
|
||||
let result = tokio::task::spawn_blocking(move || {
|
||||
expensive_computation(data)
|
||||
}).await?;
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Tokio Version Upgrade
|
||||
|
||||
When upgrading between Tokio versions (e.g., 0.2 → 1.x):
|
||||
|
||||
#### Analysis Phase
|
||||
|
||||
1. **Detect Current Version**
|
||||
- Read `Cargo.toml`
|
||||
- Identify current Tokio version
|
||||
- Check dependent crates versions
|
||||
|
||||
2. **Identify Breaking Changes**
|
||||
- Scan for deprecated APIs
|
||||
- Find removed features
|
||||
- Detect renamed functions
|
||||
|
||||
#### Migration Phase
|
||||
|
||||
3. **Update Cargo.toml**
|
||||
|
||||
```toml
|
||||
# From Tokio 0.2
|
||||
[dependencies]
|
||||
tokio = { version = "0.2", features = ["macros", "rt-threaded"] }
|
||||
|
||||
# To Tokio 1.x
|
||||
[dependencies]
|
||||
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
|
||||
```
|
||||
|
||||
4. **Update Runtime Setup**
|
||||
|
||||
```rust
|
||||
// Tokio 0.2
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
// ...
|
||||
}
|
||||
|
||||
// Tokio 1.x (same, but verify features)
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
5. **Fix API Changes**
|
||||
|
||||
Common migrations:
|
||||
|
||||
**Timer API:**
|
||||
```rust
|
||||
// Tokio 0.2
|
||||
use tokio::time::delay_for;
|
||||
delay_for(Duration::from_secs(1)).await;
|
||||
|
||||
// Tokio 1.x
|
||||
use tokio::time::sleep;
|
||||
sleep(Duration::from_secs(1)).await;
|
||||
```
|
||||
|
||||
**Timeout API:**
|
||||
```rust
|
||||
// Tokio 0.2
|
||||
use tokio::time::timeout_at;
|
||||
|
||||
// Tokio 1.x
|
||||
use tokio::time::timeout;
|
||||
```
|
||||
|
||||
**Signal Handling:**
|
||||
```rust
|
||||
// Tokio 0.2
|
||||
use tokio::signal::ctrl_c;
|
||||
|
||||
// Tokio 1.x (same, but improved)
|
||||
use tokio::signal::ctrl_c;
|
||||
```
|
||||
|
||||
6. **Update Feature Flags**
|
||||
|
||||
Map old features to new:
|
||||
- `rt-threaded` → `rt-multi-thread`
|
||||
- `rt-core` → `rt`
|
||||
- `tcp` → `net`
|
||||
- `dns` → removed (use async DNS crates)
|
||||
|
||||
### 3. Runtime Switch
|
||||
|
||||
When switching from other runtimes (async-std, smol) to Tokio:
|
||||
|
||||
#### Analysis Phase
|
||||
|
||||
1. **Identify Runtime-Specific Code**
|
||||
- Find runtime initialization
|
||||
- Detect runtime-specific APIs
|
||||
- Identify spawning patterns
|
||||
|
||||
#### Migration Phase
|
||||
|
||||
2. **Replace Runtime Setup**
|
||||
|
||||
**From async-std:**
|
||||
```rust
|
||||
// Before
|
||||
#[async_std::main]
|
||||
async fn main() {
|
||||
// ...
|
||||
}
|
||||
|
||||
// After
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
3. **Update Spawning**
|
||||
|
||||
```rust
|
||||
// async-std
|
||||
use async_std::task;
|
||||
task::spawn(async { /* ... */ });
|
||||
|
||||
// Tokio
|
||||
use tokio::task;
|
||||
task::spawn(async { /* ... */ });
|
||||
```
|
||||
|
||||
4. **Replace I/O Types**
|
||||
|
||||
```rust
|
||||
// async-std
|
||||
use async_std::net::TcpListener;
|
||||
|
||||
// Tokio
|
||||
use tokio::net::TcpListener;
|
||||
```
|
||||
|
||||
5. **Update Dependencies**
|
||||
|
||||
Replace runtime-specific crates:
|
||||
```toml
|
||||
# Remove
|
||||
async-std = "1"
|
||||
|
||||
# Add
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
```
|
||||
|
||||
### Common Migration Tasks
|
||||
|
||||
For all migration types:
|
||||
|
||||
1. **Update Tests**
|
||||
```rust
|
||||
// Before
|
||||
#[async_std::test]
|
||||
async fn test_something() { }
|
||||
|
||||
// After
|
||||
#[tokio::test]
|
||||
async fn test_something() { }
|
||||
```
|
||||
|
||||
2. **Update Error Handling**
|
||||
- Ensure error types work with async
|
||||
- Add proper error context
|
||||
- Use `?` operator appropriately
|
||||
|
||||
3. **Add Tracing**
|
||||
- Instrument key functions
|
||||
- Add structured logging
|
||||
- Set up tracing subscriber
|
||||
|
||||
4. **Verification**
|
||||
- Run `cargo check`
|
||||
- Run `cargo test`
|
||||
- Run `cargo clippy`
|
||||
- Verify no blocking operations remain
|
||||
|
||||
## Migration Checklist
|
||||
|
||||
- [ ] All I/O operations are async
|
||||
- [ ] No `std::thread::sleep` usage
|
||||
- [ ] No `std::sync::Mutex` in async code
|
||||
- [ ] CPU-intensive work uses `spawn_blocking`
|
||||
- [ ] Runtime properly configured
|
||||
- [ ] Tests updated to use `#[tokio::test]`
|
||||
- [ ] Dependencies updated in Cargo.toml
|
||||
- [ ] Error handling verified
|
||||
- [ ] Documentation updated
|
||||
- [ ] Performance tested
|
||||
|
||||
## Incremental Migration Strategy
|
||||
|
||||
For large codebases:
|
||||
|
||||
1. **Identify Migration Boundaries**
|
||||
- Start with leaf functions (no callers)
|
||||
- Move up the call graph gradually
|
||||
- Create async versions alongside sync
|
||||
|
||||
2. **Bridge Sync and Async**
|
||||
```rust
|
||||
// Call async from sync
|
||||
fn sync_wrapper() -> Result<T, Error> {
|
||||
let rt = tokio::runtime::Runtime::new()?;
|
||||
rt.block_on(async_function())
|
||||
}
|
||||
|
||||
// Call sync from async (CPU-intensive)
|
||||
async fn async_wrapper() -> Result<T, Error> {
|
||||
tokio::task::spawn_blocking(|| {
|
||||
sync_function()
|
||||
}).await?
|
||||
}
|
||||
```
|
||||
|
||||
3. **Migration Order**
|
||||
- I/O layer first
|
||||
- Business logic second
|
||||
- API/handlers last
|
||||
- Tests continuously
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Don't Mix Sync and Async I/O**: Choose one model
|
||||
2. **Use spawn_blocking**: For blocking operations you can't convert
|
||||
3. **Test Thoroughly**: Async bugs can be subtle
|
||||
4. **Profile Performance**: Measure before and after
|
||||
5. **Update Documentation**: Note async requirements
|
||||
6. **Handle Cancellation**: Implement proper cleanup
|
||||
7. **Consider Backpressure**: Add flow control
|
||||
|
||||
## Notes
|
||||
|
||||
- Migration is often incremental - don't try to do everything at once
|
||||
- Test each migration step thoroughly
|
||||
- Consider performance implications of async
|
||||
- Some operations may not benefit from async
|
||||
- Document breaking changes for API consumers
|
||||
248
commands/tokio-review.md
Normal file
248
commands/tokio-review.md
Normal file
@@ -0,0 +1,248 @@
|
||||
---
|
||||
name: tokio-review
|
||||
description: Review Tokio code for async anti-patterns, performance issues, and best practices
|
||||
---
|
||||
|
||||
# Tokio Review Command
|
||||
|
||||
This command performs comprehensive code review of Tokio-based applications, identifying async/await anti-patterns, performance issues, blocking operations, and suggesting improvements.
|
||||
|
||||
## Arguments
|
||||
|
||||
- `$1` - File path or directory to review (optional, defaults to current directory)
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
/rust-tokio-expert:tokio-review
|
||||
/rust-tokio-expert:tokio-review src/handlers/
|
||||
/rust-tokio-expert:tokio-review src/main.rs
|
||||
```
|
||||
|
||||
## Workflow
|
||||
|
||||
1. **Analyze Target**
|
||||
- If no argument provided, scan current directory for Rust files
|
||||
- If directory provided, scan all `.rs` files recursively
|
||||
- If file provided, review that specific file
|
||||
|
||||
2. **Read Relevant Files**
|
||||
- Use Glob tool to find Rust source files
|
||||
- Read all identified files using Read tool
|
||||
- Prioritize files in: src/main.rs, src/lib.rs, src/**/*.rs
|
||||
|
||||
3. **Invoke Agent**
|
||||
- Use Task tool with `subagent_type="rust-tokio-expert:tokio-performance"`
|
||||
- Provide all source code context to the agent
|
||||
- Request comprehensive analysis
|
||||
|
||||
4. **Review Checklist**
|
||||
|
||||
The agent should analyze for:
|
||||
|
||||
### Async/Await Anti-Patterns
|
||||
|
||||
- [ ] **Blocking Operations in Async Context**
|
||||
- Detect `std::thread::sleep` instead of `tokio::time::sleep`
|
||||
- Identify blocking I/O operations
|
||||
- Find CPU-intensive operations not wrapped in `spawn_blocking`
|
||||
|
||||
- [ ] **Holding Locks Across Await Points**
|
||||
- Detect `std::sync::Mutex` or `tokio::sync::Mutex` held across `.await`
|
||||
- Suggest lock scope reduction
|
||||
- Recommend alternatives like channels
|
||||
|
||||
- [ ] **Unnecessary Cloning**
|
||||
- Identify expensive clones in async contexts
|
||||
- Suggest `Arc` for shared data
|
||||
- Recommend reference passing where possible
|
||||
|
||||
- [ ] **Futures Not Being Awaited**
|
||||
- Find async functions called without `.await`
|
||||
- Detect unused futures
|
||||
- Identify missing error handling
|
||||
|
||||
### Performance Issues
|
||||
|
||||
- [ ] **Excessive Task Spawning**
|
||||
- Detect unbounded task creation in loops
|
||||
- Suggest `buffer_unordered` or bounded concurrency
|
||||
- Recommend semaphore-based limiting
|
||||
|
||||
- [ ] **Large Future Sizes**
|
||||
- Identify large types stored in future state
|
||||
- Suggest boxing large data
|
||||
- Recommend heap allocation for big arrays
|
||||
|
||||
- [ ] **Inefficient Channel Usage**
|
||||
- Detect unbounded channels
|
||||
- Identify inappropriate channel types
|
||||
- Suggest buffer size optimization
|
||||
|
||||
- [ ] **Memory Allocation in Hot Paths**
|
||||
- Find repeated allocations in loops
|
||||
- Suggest buffer reuse
|
||||
- Recommend object pooling
|
||||
|
||||
### Concurrency Issues
|
||||
|
||||
- [ ] **Potential Deadlocks**
|
||||
- Detect complex lock ordering
|
||||
- Identify circular dependencies
|
||||
- Suggest lock-free alternatives
|
||||
|
||||
- [ ] **Missing Timeout Handling**
|
||||
- Find network operations without timeouts
|
||||
- Suggest `tokio::time::timeout` usage
|
||||
- Recommend timeout configuration
|
||||
|
||||
- [ ] **Improper Shutdown Handling**
|
||||
- Check for graceful shutdown implementation
|
||||
- Verify cleanup in Drop implementations
|
||||
- Ensure resource release
|
||||
|
||||
### Error Handling
|
||||
|
||||
- [ ] **Error Propagation**
|
||||
- Verify proper error context
|
||||
- Check error type appropriateness
|
||||
- Suggest improvements for error handling
|
||||
|
||||
- [ ] **Panic in Async Context**
|
||||
- Detect unwrap/expect in async code
|
||||
- Suggest proper error handling
|
||||
- Recommend Result usage
|
||||
|
||||
### Channel Patterns
|
||||
|
||||
- [ ] **Channel Selection**
|
||||
- Verify appropriate channel type (mpsc, oneshot, broadcast, watch)
|
||||
- Check buffer sizes
|
||||
- Suggest alternatives if needed
|
||||
|
||||
- [ ] **Select! Usage**
|
||||
- Review select! macro usage
|
||||
- Check for biased selection when needed
|
||||
- Verify all branches handle errors
|
||||
|
||||
### Runtime Configuration
|
||||
|
||||
- [ ] **Runtime Setup**
|
||||
- Check worker thread configuration
|
||||
- Verify blocking thread pool size
|
||||
- Suggest optimizations based on workload
|
||||
|
||||
5. **Generate Report**
|
||||
|
||||
Create a structured review report with:
|
||||
|
||||
### Critical Issues (Must Fix)
|
||||
- Blocking operations in async context
|
||||
- Potential deadlocks
|
||||
- Memory safety issues
|
||||
- Resource leaks
|
||||
|
||||
### High Priority (Should Fix)
|
||||
- Performance bottlenecks
|
||||
- Inefficient patterns
|
||||
- Missing error handling
|
||||
- Improper shutdown handling
|
||||
|
||||
### Medium Priority (Consider Fixing)
|
||||
- Suboptimal channel usage
|
||||
- Missing timeouts
|
||||
- Code organization
|
||||
- Documentation gaps
|
||||
|
||||
### Low Priority (Nice to Have)
|
||||
- Style improvements
|
||||
- Additional tracing
|
||||
- Better variable names
|
||||
|
||||
For each issue, provide:
|
||||
- **Location**: File, line number, function
|
||||
- **Issue**: Clear description of the problem
|
||||
- **Impact**: Why it matters (performance, correctness, maintainability)
|
||||
- **Suggestion**: Specific fix with code example
|
||||
- **Priority**: Critical, High, Medium, Low
|
||||
|
||||
6. **Code Examples**
|
||||
|
||||
For each suggestion, provide:
|
||||
- Current problematic code
|
||||
- Suggested improved code
|
||||
- Explanation of the improvement
|
||||
|
||||
7. **Summary Statistics**
|
||||
|
||||
Provide overview:
|
||||
- Total files reviewed
|
||||
- Total issues found (by priority)
|
||||
- Estimated effort to fix
|
||||
- Overall code health score (if applicable)
|
||||
|
||||
## Example Report Format
|
||||
|
||||
```markdown
|
||||
# Tokio Code Review Report
|
||||
|
||||
## Summary
|
||||
- Files reviewed: 15
|
||||
- Critical issues: 2
|
||||
- High priority: 5
|
||||
- Medium priority: 8
|
||||
- Low priority: 3
|
||||
|
||||
## Critical Issues
|
||||
|
||||
### 1. Blocking Operation in Async Context
|
||||
**Location**: `src/handlers/user.rs:45`
|
||||
**Function**: `process_user`
|
||||
|
||||
**Issue**:
|
||||
Using `std::thread::sleep` in async function blocks the runtime thread.
|
||||
|
||||
**Current Code**:
|
||||
\`\`\`rust
|
||||
async fn process_user(id: u64) {
|
||||
std::thread::sleep(Duration::from_secs(1)); // Blocks thread!
|
||||
// ...
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
**Suggested Fix**:
|
||||
\`\`\`rust
|
||||
async fn process_user(id: u64) {
|
||||
tokio::time::sleep(Duration::from_secs(1)).await; // Yields control
|
||||
// ...
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
**Impact**: This blocks an entire runtime worker thread, preventing other tasks from making progress. Can cause significant performance degradation under load.
|
||||
|
||||
## High Priority Issues
|
||||
|
||||
### 1. Lock Held Across Await Point
|
||||
**Location**: `src/state.rs:78`
|
||||
...
|
||||
```
|
||||
|
||||
## Best Practices Validation
|
||||
|
||||
The review should also verify:
|
||||
|
||||
1. **Tracing**: Proper use of `#[instrument]` and structured logging
|
||||
2. **Error Types**: Appropriate error types with context
|
||||
3. **Testing**: Async tests with `#[tokio::test]`
|
||||
4. **Documentation**: Doc comments on public async functions
|
||||
5. **Metrics**: Performance-critical paths instrumented
|
||||
6. **Configuration**: Runtime properly configured
|
||||
7. **Dependencies**: Using appropriate crate versions
|
||||
|
||||
## Notes
|
||||
|
||||
- Focus on actionable feedback with concrete examples
|
||||
- Prioritize issues that impact correctness over style
|
||||
- Provide educational explanations for async concepts
|
||||
- Suggest resources for learning more about identified issues
|
||||
- Be constructive and supportive in feedback tone
|
||||
247
commands/tokio-scaffold.md
Normal file
247
commands/tokio-scaffold.md
Normal file
@@ -0,0 +1,247 @@
|
||||
---
|
||||
name: tokio-scaffold
|
||||
description: Scaffold new Tokio projects with proper structure and best practices
|
||||
---
|
||||
|
||||
# Tokio Scaffold Command
|
||||
|
||||
This command scaffolds new Tokio-based Rust projects with modern structure, proper dependencies, error handling patterns, tracing infrastructure, and testing setup.
|
||||
|
||||
## Arguments
|
||||
|
||||
- `$1` - Project name (required)
|
||||
- `$2` - Project type: `http-server`, `grpc-server`, `tcp-server`, `cli`, or `library` (optional, defaults to `library`)
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
/rust-tokio-expert:tokio-scaffold my-service http-server
|
||||
/rust-tokio-expert:tokio-scaffold my-cli cli
|
||||
/rust-tokio-expert:tokio-scaffold my-lib library
|
||||
```
|
||||
|
||||
## Workflow
|
||||
|
||||
1. **Validate Arguments**
|
||||
- Check that project name is provided
|
||||
- Validate project type if provided
|
||||
- Ensure target directory doesn't already exist
|
||||
|
||||
2. **Invoke Agent**
|
||||
- Use Task tool with `subagent_type="rust-tokio-expert:tokio-pro"`
|
||||
- Pass project name and type to the agent
|
||||
|
||||
3. **Agent Instructions**
|
||||
|
||||
The agent should create a complete project structure based on the type:
|
||||
|
||||
### For HTTP Server Projects
|
||||
|
||||
Create:
|
||||
- `Cargo.toml` with dependencies:
|
||||
- tokio with full features
|
||||
- axum for HTTP framework
|
||||
- tower and tower-http for middleware
|
||||
- serde and serde_json for serialization
|
||||
- tracing and tracing-subscriber for logging
|
||||
- anyhow and thiserror for error handling
|
||||
- sqlx (optional) for database
|
||||
- config for configuration management
|
||||
|
||||
- `src/main.rs` with:
|
||||
- Runtime setup with tracing
|
||||
- Router configuration
|
||||
- Graceful shutdown handling
|
||||
- Health check endpoints
|
||||
|
||||
- `src/handlers/mod.rs` with example HTTP handlers
|
||||
- `src/error.rs` with custom error types
|
||||
- `src/config.rs` with configuration loading
|
||||
- `src/telemetry.rs` with tracing setup
|
||||
|
||||
- `tests/integration_test.rs` with API integration tests
|
||||
- `.env.example` with configuration template
|
||||
- `README.md` with usage instructions
|
||||
|
||||
### For gRPC Server Projects
|
||||
|
||||
Create:
|
||||
- `Cargo.toml` with:
|
||||
- tokio with full features
|
||||
- tonic and tonic-build
|
||||
- prost for protobuf
|
||||
- tower for middleware
|
||||
- tracing infrastructure
|
||||
- error handling crates
|
||||
|
||||
- `proto/service.proto` with example service definition
|
||||
- `build.rs` for proto compilation
|
||||
- `src/main.rs` with gRPC server setup
|
||||
- `src/service.rs` with service implementation
|
||||
- `src/error.rs` with error types
|
||||
- `tests/integration_test.rs`
|
||||
|
||||
### For TCP Server Projects
|
||||
|
||||
Create:
|
||||
- `Cargo.toml` with:
|
||||
- tokio with io-util, net features
|
||||
- tokio-util with codec
|
||||
- bytes for buffer management
|
||||
- tracing infrastructure
|
||||
|
||||
- `src/main.rs` with TCP server setup
|
||||
- `src/protocol.rs` with protocol definition
|
||||
- `src/handler.rs` with connection handler
|
||||
- `tests/integration_test.rs`
|
||||
|
||||
### For CLI Projects
|
||||
|
||||
Create:
|
||||
- `Cargo.toml` with:
|
||||
- tokio with full features
|
||||
- clap for argument parsing
|
||||
- anyhow for error handling
|
||||
- tracing-subscriber for logging
|
||||
|
||||
- `src/main.rs` with CLI setup
|
||||
- `src/commands/mod.rs` with command structure
|
||||
- `src/config.rs` with configuration
|
||||
- `tests/cli_test.rs`
|
||||
|
||||
### For Library Projects
|
||||
|
||||
Create:
|
||||
- `Cargo.toml` with:
|
||||
- tokio as optional dependency
|
||||
- async-trait
|
||||
- thiserror for errors
|
||||
|
||||
- `src/lib.rs` with library structure
|
||||
- `tests/lib_test.rs` with comprehensive tests
|
||||
- `examples/basic.rs` with usage example
|
||||
- `README.md` with API documentation
|
||||
|
||||
4. **Common Files for All Types**
|
||||
|
||||
- `.gitignore` with Rust-specific ignores
|
||||
- `Cargo.toml` with proper metadata
|
||||
- `rustfmt.toml` with formatting rules
|
||||
- `clippy.toml` with linting configuration (if needed)
|
||||
|
||||
5. **Initialize Testing**
|
||||
|
||||
For all project types:
|
||||
- Add `#[tokio::test]` examples
|
||||
- Include timeout tests
|
||||
- Add mock/test utilities
|
||||
- Set up test helpers
|
||||
|
||||
6. **Documentation**
|
||||
|
||||
Generate `README.md` with:
|
||||
- Project description
|
||||
- Requirements
|
||||
- Installation instructions
|
||||
- Usage examples
|
||||
- Development setup
|
||||
- Testing instructions
|
||||
- Contributing guidelines
|
||||
|
||||
7. **Verification**
|
||||
|
||||
After scaffolding:
|
||||
- Run `cargo check` to verify compilation
|
||||
- Run `cargo test` to verify tests
|
||||
- Report any issues found
|
||||
|
||||
## Example Cargo.toml Template (HTTP Server)
|
||||
|
||||
```toml
|
||||
[package]
|
||||
name = "{{project_name}}"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
axum = "0.7"
|
||||
tower = "0.4"
|
||||
tower-http = { version = "0.5", features = ["trace", "compression-gzip"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }
|
||||
anyhow = "1"
|
||||
thiserror = "1"
|
||||
config = "0.14"
|
||||
|
||||
[dev-dependencies]
|
||||
tokio-test = "0.4"
|
||||
```
|
||||
|
||||
## Example Main Template (HTTP Server)
|
||||
|
||||
```rust
|
||||
use axum::{Router, routing::get};
|
||||
use std::net::SocketAddr;
|
||||
use tower_http::trace::TraceLayer;
|
||||
|
||||
mod handlers;
|
||||
mod error;
|
||||
mod config;
|
||||
mod telemetry;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
// Initialize telemetry
|
||||
telemetry::init()?;
|
||||
|
||||
// Load configuration
|
||||
let config = config::load()?;
|
||||
|
||||
// Create router
|
||||
let app = Router::new()
|
||||
.route("/health", get(handlers::health_check))
|
||||
.route("/api/v1/users", get(handlers::list_users))
|
||||
.layer(TraceLayer::new_for_http());
|
||||
|
||||
// Start server
|
||||
let addr = SocketAddr::from(([0, 0, 0, 0], config.port));
|
||||
tracing::info!("Starting server on {}", addr);
|
||||
|
||||
let listener = tokio::net::TcpListener::bind(addr).await?;
|
||||
axum::serve(listener, app)
|
||||
.with_graceful_shutdown(shutdown_signal())
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn shutdown_signal() {
|
||||
tokio::signal::ctrl_c()
|
||||
.await
|
||||
.expect("failed to install CTRL+C signal handler");
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
The scaffolded project should follow these best practices:
|
||||
|
||||
1. **Error Handling**: Use `thiserror` for domain errors, `anyhow` for application errors
|
||||
2. **Configuration**: Use environment variables with sensible defaults
|
||||
3. **Logging**: Use `tracing` with structured logging
|
||||
4. **Testing**: Include both unit and integration tests
|
||||
5. **Documentation**: Generate comprehensive README with examples
|
||||
6. **Security**: Include basic security headers and validation
|
||||
7. **Performance**: Configure runtime appropriately for workload type
|
||||
8. **Observability**: Include metrics and health check endpoints
|
||||
|
||||
## Notes
|
||||
|
||||
- Always use the latest stable versions of dependencies
|
||||
- Include comments explaining key architectural decisions
|
||||
- Provide both simple and advanced usage examples
|
||||
- Generate projects that compile and pass tests out of the box
|
||||
- Follow Rust API guidelines and naming conventions
|
||||
425
commands/tokio-test.md
Normal file
425
commands/tokio-test.md
Normal file
@@ -0,0 +1,425 @@
|
||||
---
|
||||
name: tokio-test
|
||||
description: Generate comprehensive async tests for Tokio applications
|
||||
---
|
||||
|
||||
# Tokio Test Command
|
||||
|
||||
This command generates comprehensive async tests for Tokio applications, including unit tests, integration tests, benchmarks, and property-based tests.
|
||||
|
||||
## Arguments
|
||||
|
||||
- `$1` - Target to generate tests for: file path, module name, or function name (required)
|
||||
- `$2` - Test type: `unit`, `integration`, `benchmark`, or `all` (optional, defaults to `unit`)
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
/rust-tokio-expert:tokio-test src/handlers/user.rs
|
||||
/rust-tokio-expert:tokio-test src/service.rs integration
|
||||
/rust-tokio-expert:tokio-test process_request benchmark
|
||||
/rust-tokio-expert:tokio-test src/api/ all
|
||||
```
|
||||
|
||||
## Workflow
|
||||
|
||||
1. **Parse Arguments**
|
||||
- Validate target is provided
|
||||
- Determine test type (unit, integration, benchmark, all)
|
||||
- Identify target scope (file, module, or function)
|
||||
|
||||
2. **Analyze Target Code**
|
||||
- Read the target file(s) using Read tool
|
||||
- Identify async functions to test
|
||||
- Analyze function signatures and dependencies
|
||||
- Detect error types and return values
|
||||
|
||||
3. **Invoke Agent**
|
||||
- Use Task tool with `subagent_type="rust-tokio-expert:tokio-pro"`
|
||||
- Provide code context and test requirements
|
||||
- Request test generation based on type
|
||||
|
||||
4. **Generate Unit Tests**
|
||||
|
||||
For each async function, create tests covering:
|
||||
|
||||
### Happy Path Tests
|
||||
```rust
|
||||
#[tokio::test]
|
||||
async fn test_process_user_success() {
|
||||
// Arrange
|
||||
let user_id = 1;
|
||||
let expected_name = "John Doe";
|
||||
|
||||
// Act
|
||||
let result = process_user(user_id).await;
|
||||
|
||||
// Assert
|
||||
assert!(result.is_ok());
|
||||
let user = result.unwrap();
|
||||
assert_eq!(user.name, expected_name);
|
||||
}
|
||||
```
|
||||
|
||||
### Error Handling Tests
|
||||
```rust
|
||||
#[tokio::test]
|
||||
async fn test_process_user_not_found() {
|
||||
let result = process_user(999).await;
|
||||
|
||||
assert!(result.is_err());
|
||||
assert!(matches!(result.unwrap_err(), Error::NotFound));
|
||||
}
|
||||
```
|
||||
|
||||
### Timeout Tests
|
||||
```rust
|
||||
#[tokio::test]
|
||||
async fn test_operation_completes_within_timeout() {
|
||||
use tokio::time::{timeout, Duration};
|
||||
|
||||
let result = timeout(
|
||||
Duration::from_secs(5),
|
||||
slow_operation()
|
||||
).await;
|
||||
|
||||
assert!(result.is_ok(), "Operation timed out");
|
||||
}
|
||||
```
|
||||
|
||||
### Concurrent Execution Tests
|
||||
```rust
|
||||
#[tokio::test]
|
||||
async fn test_concurrent_processing() {
|
||||
let handles: Vec<_> = (0..10)
|
||||
.map(|i| tokio::spawn(process_item(i)))
|
||||
.collect();
|
||||
|
||||
let results: Vec<_> = futures::future::join_all(handles)
|
||||
.await
|
||||
.into_iter()
|
||||
.map(|r| r.unwrap())
|
||||
.collect();
|
||||
|
||||
assert_eq!(results.len(), 10);
|
||||
assert!(results.iter().all(|r| r.is_ok()));
|
||||
}
|
||||
```
|
||||
|
||||
### Mock Tests
|
||||
```rust
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use mockall::predicate::*;
|
||||
use mockall::mock;
|
||||
|
||||
mock! {
|
||||
UserRepository {}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl UserRepository for UserRepository {
|
||||
async fn find_by_id(&self, id: u64) -> Result<User, Error>;
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_with_mock_repository() {
|
||||
let mut mock_repo = MockUserRepository::new();
|
||||
mock_repo
|
||||
.expect_find_by_id()
|
||||
.with(eq(1))
|
||||
.times(1)
|
||||
.returning(|_| Ok(User { id: 1, name: "Test".into() }));
|
||||
|
||||
let service = UserService::new(Box::new(mock_repo));
|
||||
let user = service.get_user(1).await.unwrap();
|
||||
|
||||
assert_eq!(user.name, "Test");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
5. **Generate Integration Tests**
|
||||
|
||||
Create `tests/integration_test.rs` with:
|
||||
|
||||
### API Integration Tests
|
||||
```rust
|
||||
use tokio::net::TcpListener;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_http_endpoint() {
|
||||
// Start test server
|
||||
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
|
||||
let addr = listener.local_addr().unwrap();
|
||||
|
||||
tokio::spawn(async move {
|
||||
run_server(listener).await.unwrap();
|
||||
});
|
||||
|
||||
// Make request
|
||||
let client = reqwest::Client::new();
|
||||
let response = client
|
||||
.get(format!("http://{}/health", addr))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(response.status(), 200);
|
||||
}
|
||||
```
|
||||
|
||||
### Database Integration Tests
|
||||
```rust
|
||||
#[tokio::test]
|
||||
async fn test_database_operations() {
|
||||
let pool = create_test_pool().await;
|
||||
|
||||
// Insert test data
|
||||
let user = User { id: 1, name: "Test".into() };
|
||||
save_user(&pool, &user).await.unwrap();
|
||||
|
||||
// Verify
|
||||
let fetched = find_user(&pool, 1).await.unwrap();
|
||||
assert_eq!(fetched.unwrap().name, "Test");
|
||||
|
||||
// Cleanup
|
||||
cleanup_test_data(&pool).await;
|
||||
}
|
||||
```
|
||||
|
||||
### End-to-End Tests
|
||||
```rust
|
||||
#[tokio::test]
|
||||
async fn test_complete_workflow() {
|
||||
// Setup
|
||||
let app = create_test_app().await;
|
||||
|
||||
// Create user
|
||||
let create_response = app.create_user("John").await.unwrap();
|
||||
let user_id = create_response.id;
|
||||
|
||||
// Fetch user
|
||||
let user = app.get_user(user_id).await.unwrap();
|
||||
assert_eq!(user.name, "John");
|
||||
|
||||
// Update user
|
||||
app.update_user(user_id, "Jane").await.unwrap();
|
||||
|
||||
// Verify update
|
||||
let updated = app.get_user(user_id).await.unwrap();
|
||||
assert_eq!(updated.name, "Jane");
|
||||
|
||||
// Delete user
|
||||
app.delete_user(user_id).await.unwrap();
|
||||
|
||||
// Verify deletion
|
||||
let deleted = app.get_user(user_id).await;
|
||||
assert!(deleted.is_err());
|
||||
}
|
||||
```
|
||||
|
||||
6. **Generate Benchmarks**
|
||||
|
||||
Create `benches/async_bench.rs` with:
|
||||
|
||||
```rust
|
||||
use criterion::{criterion_group, criterion_main, Criterion, BenchmarkId};
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
fn benchmark_async_operations(c: &mut Criterion) {
|
||||
let rt = Runtime::new().unwrap();
|
||||
|
||||
let mut group = c.benchmark_group("async-operations");
|
||||
|
||||
// Throughput benchmark
|
||||
for size in [10, 100, 1000].iter() {
|
||||
group.throughput(criterion::Throughput::Elements(*size as u64));
|
||||
group.bench_with_input(
|
||||
BenchmarkId::from_parameter(size),
|
||||
size,
|
||||
|b, &size| {
|
||||
b.to_async(&rt).iter(|| async move {
|
||||
process_batch(size).await
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Latency benchmark
|
||||
group.bench_function("single_request", |b| {
|
||||
b.to_async(&rt).iter(|| async {
|
||||
process_request().await
|
||||
});
|
||||
});
|
||||
|
||||
// Concurrent operations
|
||||
group.bench_function("concurrent_10", |b| {
|
||||
b.to_async(&rt).iter(|| async {
|
||||
let handles: Vec<_> = (0..10)
|
||||
.map(|_| tokio::spawn(process_request()))
|
||||
.collect();
|
||||
|
||||
for handle in handles {
|
||||
handle.await.unwrap();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
group.finish();
|
||||
}
|
||||
|
||||
criterion_group!(benches, benchmark_async_operations);
|
||||
criterion_main!(benches);
|
||||
```
|
||||
|
||||
7. **Generate Test Utilities**
|
||||
|
||||
Create `tests/common/mod.rs` with helpers:
|
||||
|
||||
```rust
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
pub fn create_test_runtime() -> Runtime {
|
||||
Runtime::new().unwrap()
|
||||
}
|
||||
|
||||
pub async fn setup_test_database() -> TestDb {
|
||||
// Create test database
|
||||
// Run migrations
|
||||
// Return handle
|
||||
}
|
||||
|
||||
pub async fn cleanup_test_database(db: TestDb) {
|
||||
// Drop test database
|
||||
}
|
||||
|
||||
pub struct TestApp {
|
||||
// Application state for testing
|
||||
}
|
||||
|
||||
impl TestApp {
|
||||
pub async fn new() -> Self {
|
||||
// Initialize test application
|
||||
}
|
||||
|
||||
pub async fn cleanup(self) {
|
||||
// Cleanup resources
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
8. **Add Test Configuration**
|
||||
|
||||
Update `Cargo.toml` with test dependencies:
|
||||
|
||||
```toml
|
||||
[dev-dependencies]
|
||||
tokio-test = "0.4"
|
||||
mockall = "0.12"
|
||||
criterion = { version = "0.5", features = ["async_tokio"] }
|
||||
proptest = "1"
|
||||
futures = "0.3"
|
||||
```
|
||||
|
||||
9. **Generate Property-Based Tests**
|
||||
|
||||
For appropriate functions:
|
||||
|
||||
```rust
|
||||
use proptest::prelude::*;
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
fn test_parse_always_succeeds(input in "\\PC*") {
|
||||
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||
rt.block_on(async {
|
||||
let result = parse_input(&input).await;
|
||||
assert!(result.is_ok() || result.is_err());
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
10. **Run and Verify Tests**
|
||||
|
||||
After generation:
|
||||
- Run `cargo test` to verify tests compile and pass
|
||||
- Run `cargo bench` to verify benchmarks work
|
||||
- Report coverage gaps if any
|
||||
- Suggest additional test cases if needed
|
||||
|
||||
## Test Categories
|
||||
|
||||
Generate tests for:
|
||||
|
||||
1. **Functional Correctness**
|
||||
- Happy path scenarios
|
||||
- Edge cases
|
||||
- Error conditions
|
||||
- Boundary values
|
||||
|
||||
2. **Concurrency**
|
||||
- Race conditions
|
||||
- Deadlocks
|
||||
- Task spawning
|
||||
- Shared state access
|
||||
|
||||
3. **Performance**
|
||||
- Throughput
|
||||
- Latency
|
||||
- Resource usage
|
||||
- Scalability
|
||||
|
||||
4. **Reliability**
|
||||
- Error recovery
|
||||
- Timeout handling
|
||||
- Retry logic
|
||||
- Graceful degradation
|
||||
|
||||
5. **Integration**
|
||||
- API endpoints
|
||||
- Database operations
|
||||
- External services
|
||||
- End-to-end workflows
|
||||
|
||||
## Best Practices
|
||||
|
||||
Generated tests should:
|
||||
|
||||
1. Use descriptive test names that explain what is being tested
|
||||
2. Follow Arrange-Act-Assert pattern
|
||||
3. Be independent and idempotent
|
||||
4. Clean up resources properly
|
||||
5. Use appropriate timeouts
|
||||
6. Include helpful assertion messages
|
||||
7. Mock external dependencies
|
||||
8. Test both success and failure paths
|
||||
9. Use `#[tokio::test]` for async tests
|
||||
10. Configure runtime appropriately for test type
|
||||
|
||||
## Example Test Organization
|
||||
|
||||
```
|
||||
tests/
|
||||
├── common/
|
||||
│ ├── mod.rs # Shared test utilities
|
||||
│ └── fixtures.rs # Test data fixtures
|
||||
├── integration_test.rs # API integration tests
|
||||
├── database_test.rs # Database integration tests
|
||||
└── e2e_test.rs # End-to-end tests
|
||||
|
||||
benches/
|
||||
├── throughput.rs # Throughput benchmarks
|
||||
└── latency.rs # Latency benchmarks
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Generate tests that are maintainable and easy to understand
|
||||
- Include comments explaining complex test scenarios
|
||||
- Provide setup and teardown helpers
|
||||
- Use realistic test data
|
||||
- Consider using test fixtures for consistency
|
||||
- Document any test-specific configuration needed
|
||||
Reference in New Issue
Block a user