| name | feature-implement |
| description | Systematic approach to implementing new features in the Rust memory system following project conventions. Use when adding new functionality with proper testing and documentation. |
Feature Implementation
Systematic approach to implementing new features in the Rust memory system.
Purpose
Add new functionality following project conventions, maintaining code quality and test coverage.
Implementation Process
Phase 1: Planning
1. Understand Requirements
- What is the feature?
- Why is it needed?
- Who will use it?
- What are the acceptance criteria?
2. Design Approach
- How does it fit into existing architecture?
- What modules need changes?
- What new modules are needed?
- What are the data structures?
- What are the API signatures?
3. Check Constraints
- File size limit: ≤ 500 LOC per file
- Async/Tokio patterns for I/O
- Error handling with
anyhow::Result
- Storage: Turso (durable) + redb (cache)
Phase 2: Implementation
1. Create Module Structure
touch src/new_feature/mod.rs
touch src/new_feature/core.rs
touch src/new_feature/storage.rs
Example structure:
src/
├── new_feature/
│ ├── mod.rs # Public API exports
│ ├── core.rs # Core logic (<500 LOC)
│ ├── storage.rs # Storage operations (<500 LOC)
│ └── types.rs # Data structures (<500 LOC)
2. Define Types
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FeatureData {
pub id: String,
pub created_at: i64,
pub data: serde_json::Value,
}
#[derive(Debug, Clone)]
pub struct FeatureConfig {
pub enabled: bool,
pub max_items: usize,
}
3. Implement Core Logic
use anyhow::Result;
pub struct Feature {
config: FeatureConfig,
}
impl Feature {
pub fn new(config: FeatureConfig) -> Self {
Self { config }
}
pub async fn process(&self, input: FeatureData) -> Result<FeatureData> {
self.validate(&input)?;
let processed = self.process_internal(input).await?;
self.store(&processed).await?;
Ok(processed)
}
fn validate(&self, data: &FeatureData) -> Result<()> {
Ok(())
}
async fn process_internal(&, data: FeatureData) <FeatureData> {
(data)
}
(&, data: &FeatureData) <()> {
(())
}
}
4. Add Storage Layer
use super::types::FeatureData;
use anyhow::Result;
pub struct FeatureStorage {
turso: TursoClient,
}
impl FeatureStorage {
pub async fn save(&self, data: &FeatureData) -> Result<()> {
let sql = "INSERT OR REPLACE INTO feature_table (id, data, created_at) VALUES (?, ?, ?)";
self.turso
.execute(sql)
.bind(&data.id)
.bind(serde_json::to_string(&data.data)?)
.bind(data.created_at)
.await?;
Ok(())
}
pub async fn get(&self, id: &str) -> Result<Option<FeatureData>> {
let sql = "SELECT id, data, created_at FROM feature_table WHERE id = ?";
let row = self.turso.query(sql).bind(id).await?;
()
}
}
5. Implement Public API
mod core;
mod storage;
mod types;
pub use types::{FeatureConfig, FeatureData};
pub use core::Feature;
pub async fn quick_process(data: FeatureData) -> anyhow::Result<FeatureData> {
let feature = Feature::new(FeatureConfig::default());
feature.process(data).await
}
Phase 3: Testing
1. Unit Tests
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_feature_creation() {
let config = FeatureConfig {
enabled: true,
max_items: 100,
};
let feature = Feature::new(config);
assert!(feature.config.enabled);
}
#[test]
fn test_validation() {
let feature = Feature::new(FeatureConfig::default());
let data = FeatureData {
id: "test".to_string(),
created_at: 0,
data: serde_json::json!({}),
};
assert!(feature.validate(&data).is_ok());
}
#[tokio::test]
async fn test_process() {
let feature = Feature::new(FeatureConfig::default());
let input = create_test_data();
let result = feature.process(input).await;
assert!(result.());
}
}
2. Integration Tests
use memory_core::new_feature::*;
#[tokio::test]
async fn test_end_to_end_feature() {
let memory = create_test_memory().await;
let data = FeatureData { };
let result = memory.feature_operation(data).await;
assert!(result.is_ok());
let stored = memory.get_feature_data("id").await.unwrap();
assert_eq!(stored.id, "id");
}
Phase 4: Integration
1. Wire into Main API
pub mod new_feature;
use new_feature::Feature;
pub struct SelfLearningMemory {
feature: Feature,
}
impl SelfLearningMemory {
pub async fn feature_operation(&self, data: FeatureData) -> Result<FeatureData> {
self.feature.process(data).await
}
}
2. Update Database Schema
CREATE TABLE IF NOT EXISTS feature_table (
id TEXT PRIMARY KEY,
data TEXT NOT NULL,
created_at INTEGER NOT NULL
);
CREATE INDEX idx_feature_created ON feature_table(created_at DESC);
3. Add Configuration
pub struct Config {
pub feature_config: FeatureConfig,
}
Phase 5: Documentation
1. Code Documentation
pub async fn feature_operation(&self, data: FeatureData) -> Result<FeatureData>
2. Update README
Add feature to main README.md:
## Features
- Episodic memory storage
- Pattern extraction and learning
- Context retrieval
- **NEW: Feature name** - Brief description
Phase 6: Quality Checks
cargo fmt
cargo clippy --all -- -D warnings
cargo build --all
cargo test --all
cargo doc --no-deps
Phase 7: Commit
git add src/new_feature/ tests/integration/feature_test.rs
git commit -m "[feature] add new_feature module
- Implemented core Feature struct with process logic
- Added Turso storage layer with save/get operations
- Created comprehensive unit and integration tests
- Updated main API to expose feature_operation
- Added database migration for feature_table
Closes: #123
"
Best Practices
Code Organization
- One feature per module
- Split large modules (< 500 LOC per file)
- Clear separation: types, core logic, storage, API
Error Handling
pub async fn operation(&self) -> Result<Data> {
let data = self.fetch().await?;
self.process(data).await?;
Ok(data)
}
pub async fn operation(&self) -> Option<Data> {
let data = self.fetch().await.ok()?;
Some(data)
}
Async Patterns
let (result1, result2) = tokio::join!(
operation1(),
operation2(),
);
let result1 = operation1().await;
let result2 = operation2().await;
Testing
- Unit tests for each function
- Integration tests for workflows
- Test error cases
- Test edge cases (empty, max, invalid)
Documentation
- Public APIs must be documented
- Include examples for complex APIs
- Document errors and edge cases
- Keep docs up to date with code
Feature Checklist
Common Pitfalls
1. Forgetting .await
let data = async_function();
let data = async_function().await?;
2. Blocking in Async Context
async fn process() {
std::thread::sleep(Duration::from_secs(1));
}
async fn process() {
tokio::time::sleep(Duration::from_secs(1)).await;
}
3. Not Testing Error Cases
#[tokio::test]
async fn test_error_handling() {
let result = operation_with_invalid_input().await;
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), Error::InvalidInput(_)));
}
4. Ignoring Performance
for item in items {
storage.save(item).await?;
}
storage.save_batch(items).await?;
Examples