syntaxis/shared/rust/INTEGRATION_GUIDE.md
Jesús Pérez 9cef9b8d57 refactor: consolidate configuration directories
Merge _configs/ into config/ for single configuration directory.
Update all path references.

Changes:
- Move _configs/* to config/
- Update .gitignore for new patterns
- No code references to _configs/ found

Impact: -1 root directory (layout_conventions.md compliance)
2025-12-26 18:36:23 +00:00

481 lines
12 KiB
Markdown

# Tools Shared Utilities Integration Guide
This guide explains how to integrate shared utilities into your Tools ecosystem Rust CLI.
## Quick Start
### 1. Add Dependency to Cargo.toml
In your CLI crate (e.g., `crates/your-cli/Cargo.toml`):
```toml
[dependencies]
tools-shared = { path = "../../../shared/rust" }
# For interactive project selection
tools-shared = { path = "../../../shared/rust", features = ["interactive"] }
# For manifest management
tools-shared = { path = "../../../shared/rust", features = ["manifest"] }
# All features
tools-shared = { path = "../../../shared/rust", features = ["interactive", "manifest"] }
```
### 2. Import in main.rs
```rust
use tools_shared::{
// Configuration discovery
find_config_path, find_db_path,
// Project features
detect_project_from_cwd, SelectableProject, select_project,
// Database migrations
find_migration_files, run_migration_file,
// XDG directories
tool_data_dir, tool_config_dir, ensure_dir,
// Global database
find_global_db_path,
};
```
### 3. Use in Your Code
See examples below for each feature.
## Feature Integration Examples
### Configuration Discovery
Find configuration files automatically:
```rust
use tools_shared::find_config_path;
#[derive(Parser)]
struct Cli {
#[arg(short, long)]
config: Option<String>,
}
#[tokio::main]
async fn main() -> Result<()> {
let cli = Cli::parse();
// Use provided config or search
let config_path = cli.config
.map(PathBuf::from)
.or_else(|| find_config_path("my-tool.toml"))
.ok_or_else(|| anyhow::anyhow!("No config found"))?;
let config = Config::load(&config_path)?;
Ok(())
}
```
### Project Auto-Detection
Add a global `--project` flag that auto-detects projects:
```rust
use tools_shared::detect_project_from_cwd;
#[derive(Parser)]
struct Cli {
#[arg(short, long, global = true, help = "Project name (auto-detected if omitted)")]
project: Option<String>,
#[command(subcommand)]
command: Commands,
}
#[tokio::main]
async fn main() -> Result<()> {
let cli = Cli::parse();
// Auto-detect if not specified
let project_name = cli.project
.clone()
.or_else(|| {
detect_project_from_cwd("config.toml")
.map(|p| p.name)
});
if let Some(name) = project_name {
println!("Working on project: {}", name);
} else {
println!("No project specified or detected");
}
Ok(())
}
```
### Interactive Project Selection
Add a command that lets users select a project:
```rust
use tools_shared::{select_project, SelectableProject};
#[derive(Subcommand)]
enum Commands {
/// Select a project interactively
SelectProject,
}
async fn select_project_command(projects: Vec<ProjectEntity>) -> Result<String> {
let selectable: Vec<SelectableProject> = projects
.into_iter()
.map(|p| SelectableProject {
id: p.id.clone(),
name: p.name,
path: None,
metadata: Some(format!("Phase: {:?}", p.current_phase)),
})
.collect();
select_project(selectable, "Select a project:", None)
.map_err(|e| anyhow::anyhow!("Selection failed: {}", e))
}
```
### Database Migrations
Add migration support:
```rust
use tools_shared::{find_migration_files, run_migration_file};
#[derive(Subcommand)]
enum Commands {
/// Run migrations
Migrate {
/// Path to migration file
#[arg(long)]
file: Option<String>,
/// Find and run all migrations in data/migration/
#[arg(long)]
all: Option<String>,
},
}
async fn handle_migrate(file: Option<String>, all: Option<String>) -> Result<()> {
if let Some(sql_file) = file {
// Run single migration
run_migration_file(&sql_file)?;
println!("✅ Migration executed");
} else if let Some(root) = all {
// Find and run all migrations
let migrations = find_migration_files(&root)?;
println!("Found {} migrations", migrations.len());
for migration in migrations {
if let Some(name) = migration.file_name() {
println!("Running: {:?}", name);
// Execute migration (requires sqlx or other DB driver)
}
}
}
Ok(())
}
```
### XDG Directories for Data Storage
Use standard directories for your tool's data:
```rust
use tools_shared::{tool_data_dir, tool_config_dir, ensure_dir};
use std::path::PathBuf;
#[tokio::main]
async fn main() -> Result<()> {
let tool_name = "my-tool";
// Create standard directories
let data_dir = tool_data_dir(tool_name);
let config_dir = tool_config_dir(tool_name);
ensure_dir(&data_dir)?;
ensure_dir(&config_dir)?;
// Now use them
let db_path = data_dir.join("my-tool.db");
let config_path = config_dir.join("config.toml");
println!("Data: {}", db_path.display());
println!("Config: {}", config_path.display());
Ok(())
}
```
### Global Multi-Project Database
Use a single global database to manage multiple projects:
```rust
use tools_shared::find_global_db_path;
use sqlx::sqlite::SqlitePool;
#[tokio::main]
async fn main() -> Result<()> {
// Get global database path
let db_path = find_global_db_path("my-tool");
let db_url = format!("sqlite://{}", db_path.display());
// Create database if it doesn't exist
if !db_path.exists() {
std::fs::create_dir_all(db_path.parent().unwrap())?;
}
// Connect to database
let pool = SqlitePool::connect(&db_url).await?;
// Run migrations
sqlx::query("CREATE TABLE IF NOT EXISTS projects (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
phase TEXT NOT NULL
)")
.execute(&pool)
.await?;
Ok(())
}
```
## Integration Checklist
When adding shared features to your CLI:
### Configuration Discovery
- [ ] Add `tools-shared` dependency
- [ ] Update `Cargo.toml` help text to document search order
- [ ] Use `find_config_path()` to discover config files
- [ ] Test configuration discovery from multiple locations
### Project Detection
- [ ] Add global `--project` flag to CLI struct
- [ ] Implement fallback to `detect_project_from_cwd()`
- [ ] Add `project detect` command to show detected project
- [ ] Cache project selection with `save_last_project()`
### Project Selection
- [ ] Add `features = ["interactive"]` to tools-shared
- [ ] Create struct list of available projects
- [ ] Implement project selection command
- [ ] Use `select_project()` for interactive picker
### Database Migrations
- [ ] Add migration file discovery
- [ ] Create `data/migration/` directory in examples
- [ ] Implement migration commands (`migrate --file` and `migrate --all`)
- [ ] Create sample migration files for testing
### XDG Directories
- [ ] Use `tool_data_dir()` for user data
- [ ] Use `tool_config_dir()` for configuration
- [ ] Use `tool_cache_dir()` for temporary data
- [ ] Call `ensure_dir()` before writing files
### Global Database
- [ ] Use `find_global_db_path()` for database location
- [ ] Create database parent directories if missing
- [ ] Store per-project data with project IDs
- [ ] Document multi-project schema
### Testing
- [ ] Test all configuration discovery paths
- [ ] Test project detection in various directories
- [ ] Test migration file discovery and execution
- [ ] Test directory creation with `ensure_dir()`
- [ ] Test database connectivity
### Documentation
- [ ] Update help text with configuration search order
- [ ] Document `--project` flag usage
- [ ] Add examples to README.md
- [ ] Document migration file format
## File Structure for a New Tool
When creating a new tool with shared features:
```
my-tool/
├── Cargo.toml # Workspace root
├── README.md # Tool documentation
├── QUICKSTART.md # 5-minute setup guide
├── crates/
│ ├── my-core/ # Core library
│ │ ├── src/
│ │ │ ├── lib.rs
│ │ │ ├── error.rs
│ │ │ └── service.rs
│ │ └── Cargo.toml
│ │
│ └── my-cli/ # CLI using shared features
│ ├── src/
│ │ ├── main.rs # Imports tools-shared
│ │ └── handlers/ # Command handlers
│ └── Cargo.toml # Has tools-shared dependency
├── data/
│ └── migration/ # SQL migrations
│ ├── 001_initial.sql
│ └── 002_add_index.sql
└── examples/
└── config.toml # Example configuration
```
## Examples in Tools Ecosystem
Study these existing implementations:
### Simple: hello-tool
- Basic project detection
- Migration file discovery
- Interactive selection example
```bash
# View implementation
cat /Users/Akasha/Tools/hello-tool/crates/hello-cli/src/main.rs
```
### Advanced: syntaxis
- Global `--project` flag
- Project commands (list, show, detect, select)
- Database migration handler
- Multi-project global database
```bash
# View implementation
cat /Users/Akasha/Development/syntaxis/crates/syntaxis-cli/src/main.rs
```
### Migration Examples
- Check how project-lifecycle uses migrations:
```bash
cat /Users/Akasha/Development/syntaxis/crates/syntaxis-cli/src/handlers/migrate_db.rs
```
## Testing Integration
Before publishing, verify:
```bash
# Build your tool
cargo build -p my-cli
# Test project detection
./target/debug/my-tool project detect
# Test configuration discovery
./target/debug/my-tool --config ./my-tool.toml <command>
# Test migration discovery
./target/debug/my-tool migrate --all .
# Run full test suite
cargo test --workspace
```
## Common Issues
### Configuration Not Found
**Problem**: `find_config_path()` returns `None` even though file exists
**Solution**:
- Check exact filename (case-sensitive)
- Ensure file is in one of the search locations
- Run from project root directory
- Use `find_config_path_warn_conflicts()` to debug
### Project Not Detected
**Problem**: `detect_project_from_cwd()` returns `None`
**Solution**:
- Verify `.project/lifecycle.toml` or `.vapora/lifecycle.toml` exists
- Check current working directory
- Run from project subdirectory (detection searches parents)
- Use `detect_project_from_path()` with explicit path
### Interactive Selection Not Working
**Problem**: `select_project()` shows no options
**Solution**:
- Ensure `features = ["interactive"]` in Cargo.toml
- Pass non-empty project list
- Check terminal supports ANSI (for colors/formatting)
- Test with `--no-interactive` flag if available
### Migrations Not Found
**Problem**: `find_migration_files()` returns empty vector
**Solution**:
- Create `data/migration/` directory
- Add `.sql` files (lowercase extension)
- Ensure files are readable
- Run from correct directory or use absolute path
## Migration Guide
### From Local Database to Global
If your tool previously used per-project databases:
```rust
// Old way (per-project)
let db_path = format!(".project/data/{}.db", project_name);
let db = Database::open(&db_path)?;
// New way (global, multi-project)
let db_path = find_global_db_path("my-tool");
let db = Database::open(&db_path)?;
// Store project context in data
let projects = db.get_projects()?;
let project = projects.iter().find(|p| p.name == project_name)?;
let entries = db.get_entries(project.id)?;
```
## Best Practices
1. **Always handle missing files gracefully** - Don't assume configuration exists
2. **Provide good error messages** - Tell users where to create config
3. **Cache project selection** - Use `save_last_project()` for quick re-runs
4. **Support explicit overrides** - Allow `--project` flag even when auto-detected
5. **Document search order** - Show users in help text where config is searched
6. **Use XDG directories** - Don't hardcode paths like `~/.myapp/`
7. **Support global database** - Use single DB for multi-project management
8. **Plan migrations** - Create `data/migration/` structure from start
## Getting Help
- **Read source code**: `/Users/Akasha/Tools/shared/rust/`
- **Check examples**: `/Users/Akasha/Tools/hello-tool/crates/hello-cli/src/main.rs`
- **Study advanced usage**: `/Users/Akasha/Development/syntaxis/`
- **Run tests**: `cargo test -p tools-shared`
- **API docs**: `cargo doc -p tools-shared --open`
## Next Steps
1. Add shared dependency to your CLI
2. Follow "Quick Start" section above
3. Implement features incrementally
4. Test each feature thoroughly
5. Update documentation
6. Commit with clear commit message
7. Share with the team!