Loading...
Loading...
General guidance for building Rust CLI programs (clap/anyhow/tracing/serde_json), with agent-friendly patterns: JSON output mode, stdout/stderr separation, predictable exit codes, integration testing, and a cargo-release based release workflow.
npx skill4agent add tumf/skills rust-clioss-publishclapanyhowthiserrortracingtracing-subscriberserdeserde_jsonagentic-cli-designdirectories::BaseDirsSHGetKnownFolderPathHOMEUSERPROFILEdirectories$HOME| Platform | Behavior | Override via env? |
|---|---|---|
| Unix/Linux/macOS | Reads | ✅ Yes |
| Windows | Calls Win32 API ( | ❌ No |
// Pattern 1: Unix-only test (recommended for HOME override)
#[test]
#[cfg(not(target_os = "windows"))]
fn test_home_override() {
let temp = TempDir::new().unwrap();
env::set_var("HOME", temp.path());
// Test code that uses directories::BaseDirs
}
// Pattern 2: Platform-specific logic
#[test]
fn test_cross_platform_home() {
#[cfg(unix)]
{
env::set_var("HOME", "/tmp/test");
// Unix-specific test
}
#[cfg(windows)]
{
// Windows: use explicit paths or dependency injection
let test_dir = PathBuf::from("C:\\temp\\test");
// Windows-specific test
}
}
// Pattern 3: Dependency injection (best for portability)
fn install_skill_to_path(base_dir: &Path, skill_name: &str) {
// Accept path directly, avoid BaseDirs in implementation
}
#[test]
fn test_install_with_explicit_path() {
let temp = TempDir::new().unwrap();
install_skill_to_path(temp.path(), "my-skill");
// Portable across all platforms
}| Function | Platform behavior | Override via env? |
|---|---|---|
| Calls OS API | ❌ No |
| Calls OS API | ❌ No |
| Windows: API, Unix: XDG vars | Partial (Unix only) |
BaseDirsProjectDirsi64SystemTime::now()record()created_at = now_secs()1707408142cleanup_old_entries(0)cutoff = now_secs()1707408143WHERE created_at < cutoff1707408142 < 1707408143cleanup_old_entries(1)0days <= 0<<=#[test]
fn test_cleanup_old_entries() {
let (ledger, _temp) = create_test_ledger();
ledger
.record("test-1", "hash-1", "tweet-1", "success")
.unwrap();
// Ensure the cutoff is not computed in the exact same instant.
std::thread::sleep(std::time::Duration::from_millis(10));
// Use a safe margin: a fresh entry should not be deleted.
let deleted = ledger.cleanup_old_entries(1).unwrap();
assert_eq!(deleted, 0);
let entry = ledger.lookup("test-1").unwrap();
assert!(entry.is_some());
}| Feature | Unix/macOS | Windows | Testing approach |
|---|---|---|---|
| Path separator | | | Always use |
| Line endings | | | Explicitly specify in tests or use |
| Executable extension | none | | Use |
| Case sensitivity | Yes | No | Test with varied cases on Windows |
| Temp directory | | | Use |
--jsontracingoss-publishcargo-releasecargo install cargo-release# Patch: 0.1.0 -> 0.1.1
cargo release patch --execute --no-confirm --no-publish
# Minor: 0.1.0 -> 0.2.0
cargo release minor --execute --no-confirm --no-publish
# Major: 0.1.0 -> 1.0.0
cargo release major --execute --no-confirm --no-publish--no-confirm--no-publishcargo publishcargo fmt
cargo clippy -- -D warnings
cargo test
# Ensures the package can be built as it will be uploaded
cargo publish --dry-rungit checkout <tag>
cargo publishrust-cli/references/crates.mdrust-cli/references/templates.md