| name | rust-crate-creator |
| description | Create new Rust crates in this Cargo workspace following established patterns for dependency management, testing, documentation, and project structure. Use when adding new workspace members, creating modules, or structuring code. Keywords: cargo, crate, workspace, module, new crate, add crate, dependency, Rust project |
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]
common = { path = "crates/common" }
expr = { path = "crates/expr" }
types = { path = "crates/types" }
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"
[dependencies]
common = { workspace = true }
types = { workspace = true }
serde = { workspace = true }
thiserror = { workspace = true }
[dev-dependencies]
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):
Type/function docs:
pub struct TypeName { ... }
pub type ColumnId = u64;
6. Set Up Testing Patterns
Unit tests (co-located):
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn descriptive_test_name() {
}
}
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");
}
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 = [
"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> {
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()
}
let bytes = encode_to_vec(data, bincode_config())?;
let (decoded, read) = decode_from_slice(&bytes, bincode_config())?;
Module organization:
pub mod submodule;
pub use submodule::PublicType;
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):
OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(false)
.open(&path)?;
OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.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);
self.write_page(table, &page)?;
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:
cargo llvm-cov --package crate-name --html
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};
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:
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"