Agent skill
rust-crate-creator-wcygan-sql-database
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/testing/rust-crate-creator-wcygan-sql-database
SKILL.md
Rust Crate Creator
Creates new Rust crates in the workspace following established project conventions for dependency management, testing, documentation, and modular architecture.
Instructions
When creating a new crate, follow this structured approach:
1. Analyze Workspace Structure
Workspace root patterns:
- All dependencies must be pinned in
Cargo.toml:[workspace.dependencies] - Workspace members live under
crates/ - Use
resolver = "2"for workspace-level dependency resolution - Generate artifacts belong in
target/(never commit)
Current workspace dependencies to reference:
[workspace.dependencies]
# Internal crates (always use path references)
common = { path = "crates/common" }
expr = { path = "crates/expr" }
types = { path = "crates/types" }
# External dependencies (pin versions here)
proptest = "1.9.0"
pretty_assertions = "1"
serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0.145"
sqlparser = "0.43"
tempfile = "3.23.0"
thiserror = "1.0.69"
ahash = "0.8.12"
hashbrown = { version = "0.14.5", features = ["serde"] }
uuid = { version = "1.18.1", features = ["serde", "v4"] }
bincode = { version = "2.0.1", features = ["serde"] }
bytes = "1.10.1"
2. Create Crate Structure
Directory layout:
crates/<crate-name>/
├── Cargo.toml
├── CLAUDE.md
├── AGENTS.md
├── src/
│ ├── lib.rs
│ └── tests.rs (or mod tests in lib.rs)
└── tests/ (optional integration tests)
Cargo.toml template:
[package]
name = "crate-name"
version = "0.1.0"
edition = "2024" # or "2021" if needed
[dependencies]
# Reference workspace deps with { workspace = true }
common = { workspace = true }
types = { workspace = true }
serde = { workspace = true }
thiserror = { workspace = true }
[dev-dependencies]
# Test dependencies also from workspace
tempfile = { workspace = true }
proptest = { workspace = true }
pretty_assertions = { workspace = true }
Critical rules:
- NEVER specify versions in member crates—always use
{ workspace = true } - Even path-only dependencies (
common,expr,types) must use workspace references - Add new dependencies to root
[workspace.dependencies]first, then reference them
3. Generate CLAUDE.md (Crate Guidelines)
Template structure:
# [Crate Name] Guidelines
## Role Within The Workspace
- Purpose and responsibility of this crate
- How it integrates with other workspace members
- Key contracts and APIs it provides
## Integration Contracts
- **Parser** – How parser interacts with this crate
- **Types** – Type dependencies and shared structures
- **Common** – Error handling and shared utilities
- **Storage/Catalog/Expr** – Domain-specific integrations
## Module Layout & Extension Points
- Key source files and their responsibilities
- How to add new features or extend existing ones
- Important patterns to follow
## Build, Test, and Development Commands
- `cargo check -p [crate-name]` — fast validation
- `cargo test -p [crate-name]` — run crate tests
- `cargo fmt` / `cargo fmt -- --check` — formatting
- `cargo clippy -p [crate-name] --all-targets` — linting
- `scripts/coverage.sh -- --package [crate-name]` — coverage
## Coding Style & Naming Conventions
- Follow rustfmt defaults (4-space indent, trailing commas)
- Modules: snake_case (`mod storage_backend`)
- Types/traits: UpperCamelCase (`SqlValue`)
- Constants: SCREAMING_SNAKE_CASE
- Workspace dependency pattern: `{ workspace = true }`
## Testing Guidelines
- Co-locate unit tests using `mod tests`
- Use `tempfile::tempdir()` for filesystem tests
- Property tests with `proptest` (name as `prop_*`)
- Integration tests in `tests/` directory
- Run tests before commits
## Commit & Pull Request Guidelines
- Imperative mood commits
- PRs include: motivation, changes, validation commands, affected crates
- Mention downstream impacts
4. Generate AGENTS.md (Implementation Guardrails)
Template structure:
# [Crate Name] - agents.md
> Implementation guardrails for the `[crate-name]` crate so future agents can extend it without breaking workspace conventions.
## Purpose
- Core responsibility and domain
- Key abstractions provided
- Integration points with other crates
## Architecture checkpoints
1. **Key pattern 1** - Description and constraints
2. **Key pattern 2** - Description and constraints
3. **Key pattern 3** - Description and constraints
## Workspace coordination
- Dependencies must be declared via `{ workspace = true }`
- Shared domain types come from `common`/`types`
- Tests rely on `tempfile` for temporary directories
- All errors use `common::DbResult` and `DbError` variants
## Extending the crate
- How to add new features safely
- Patterns to maintain consistency
- Integration points to coordinate with other crates
By following these constraints we keep the [crate-name] layer aligned with the rest of the SQL database workspace.
5. Add Rust Documentation Comments
Module-level docs (in lib.rs):
//! Brief description of the crate's purpose.
//!
//! # Examples
//!
//! ```
//! // Show basic usage
//! ```
//!
//! # Architecture
//!
//! Explain key design decisions or patterns.
Type/function docs:
/// Brief description of what this does.
///
/// # Examples
///
/// ```
/// let example: TypeName = ...;
/// // Show usage
/// ```
///
/// # Errors
///
/// Returns `DbError::...` when conditions occur.
pub struct TypeName { ... }
/// Identifier for a column within a table schema.
/// Examples:
/// - `let id_col: ColumnId = 1; // maps to "id"`
/// - `let name_col: ColumnId = 2; // maps to "name"`
pub type ColumnId = u64;
6. Set Up Testing Patterns
Unit tests (co-located):
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn descriptive_test_name() {
// Arrange
// Act
// Assert
}
}
Using tempfile for filesystem tests:
use tempfile::tempdir;
#[test]
fn test_with_temp_directory() {
let dir = tempdir().unwrap();
let path = dir.path().join("test.file");
// Use path for testing
// dir automatically cleaned up on drop
}
Property-based tests:
use proptest::prelude::*;
proptest! {
#[test]
fn prop_roundtrip_preserves_value(input: Vec<u8>) {
let encoded = encode(&input);
let decoded = decode(&encoded).unwrap();
prop_assert_eq!(input, decoded);
}
}
7. Update Workspace Configuration
Add to root Cargo.toml members:
[workspace]
members = [
# ... existing members
"crates/new-crate-name",
]
Add internal dependency to workspace:
[workspace.dependencies]
new-crate-name = { path = "crates/new-crate-name" }
8. Code Coverage Setup
The project uses cargo-llvm-cov for coverage:
Run coverage for specific crate:
scripts/coverage.sh -- --package crate-name
Coverage generates:
- HTML report:
target/llvm-cov/html/index.html - LCOV format:
target/llvm-cov/lcov.info
9. Common Patterns
Error handling:
use common::{DbError, DbResult};
pub fn operation() -> DbResult<T> {
// ... implementation
Err(DbError::CrateName(format!("descriptive error")))
}
Serialization with bincode:
use bincode::config::{self, Config};
use bincode::serde::{decode_from_slice, encode_to_vec};
fn bincode_config() -> impl Config {
config::legacy()
}
// Encoding
let bytes = encode_to_vec(data, bincode_config())?;
// Decoding
let (decoded, read) = decode_from_slice(&bytes, bincode_config())?;
Module organization:
// src/lib.rs
pub mod submodule;
pub use submodule::PublicType;
// src/submodule.rs
use common::{DbError, DbResult};
pub struct PublicType { ... }
#[cfg(test)]
mod tests { ... }
Common Pitfalls and Solutions
Test Module Configuration
Problem: Tests fail to compile with "unresolved import" errors for dev-dependencies.
Solution: Always add #[cfg(test)] attribute to test modules:
#[cfg(test)]
mod tests;
This ensures test-only imports (like tempfile) are only compiled during test runs.
File I/O and Clippy Warnings
Problem: Clippy warns about "file opened with create, but truncate behavior not defined".
Solution: Explicitly specify truncate behavior when using create(true):
// For append-only files (don't truncate existing content)
OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(false) // Keep existing content
.open(&path)?;
// For overwriting files
OpenOptions::new()
.write(true)
.create(true)
.truncate(true) // Overwrite existing content
.open(&path)?;
Page Allocation and File Extension
Problem: Sequential ID allocation fails because the file doesn't actually extend until data is written.
Solution: Write the page to disk immediately after allocation to reserve the space:
fn allocate_page(&mut self, table: TableId) -> DbResult<PageId> {
let pid = PageId(file_len / PAGE_SIZE);
let page = Page::new(pid.0);
// Write immediately to extend the file
self.write_page(table, &page)?;
// Then add to cache
self.cache.push((table, pid), page);
Ok(pid)
}
Coverage Script Usage
Problem: Running scripts/coverage.sh -- --package crate-name fails with "Unrecognized option: 'package'".
Solution: Use cargo llvm-cov directly for single-crate coverage:
# Correct approach
cargo llvm-cov --package crate-name --html
# View summary
cargo llvm-cov --package crate-name --summary-only
The workspace scripts/coverage.sh is designed for full workspace coverage, not filtered runs.
Import Organization
Problem: Rustfmt reorders imports in unexpected ways.
Solution: Follow rustfmt's preference:
- External crates first (alphabetically)
- Then standard library imports
- Import items alphabetically within each use statement
use common::{DbError, DbResult, PageId, TableId};
use hashbrown::HashMap;
use lru::LruCache;
use std::{
fs::OpenOptions,
io::{Read, Seek, SeekFrom, Write},
num::NonZeroUsize,
path::PathBuf,
};
use storage::{PAGE_SIZE, Page}; // Items sorted alphabetically
Function Coverage Targets
Problem: Coverage shows low function coverage (e.g., 50%) even when tests seem comprehensive.
Solution: Test error paths and edge cases explicitly:
- Test functions that return
Result<T>with both success and error cases - Test private helper functions indirectly through public API
- Add tests for all public trait implementations
- Test panic conditions with
#[should_panic] - Target 90%+ line coverage and 85%+ function coverage
Example:
#[test]
fn test_error_path() {
let result = operation_that_can_fail();
assert!(matches!(result, Err(DbError::Storage(_))));
}
#[test]
#[should_panic(expected = "descriptive message")]
fn test_panic_condition() {
function_that_panics();
}
Validation Checklist
Before completing crate creation:
-
Cargo.tomluses{ workspace = true }for all dependencies - Added to workspace members in root
Cargo.toml -
CLAUDE.mddocuments integration contracts -
AGENTS.mdcaptures architecture guardrails - Module-level doc comments (
//!) inlib.rs - Type/function doc comments (
///) with examples - Unit tests use
mod testspattern - Filesystem tests use
tempfile::tempdir() - Error types use
common::DbErrorvariants -
cargo check -p crate-namepasses -
cargo test -p crate-namepasses -
cargo fmt -- --checkpasses -
cargo clippy -p crate-name --all-targetspasses -
cargo llvm-cov --package crate-name --summary-onlyshows 85%+ function coverage -
cargo llvm-cov --package crate-name --summary-onlyshows 90%+ line coverage - Error paths and edge cases are explicitly tested
Output Format
When creating a new crate, generate files in this order:
- Show workspace analysis - List existing crates and dependencies
- Create directory structure - Use Write tool for
Cargo.toml,CLAUDE.md,AGENTS.md - Generate src/lib.rs - With proper doc comments
- Add tests - Either in-module or separate
tests.rs - Update workspace - Modify root
Cargo.toml - Run validation - Execute check/test/fmt/clippy
- Summary - List created files and next steps
Example Usage
User: "Create a new crate for query planning"
Didn't find tool you were looking for?