doiget_cli/commands/mod.rs
1//! Subcommand implementations for the `doiget` CLI.
2//!
3//! Each module corresponds to a single `clap` subcommand declared in
4//! `main.rs`. The dispatch table in `main.rs` calls `run(...)` on the
5//! matching module. Subcommands return `anyhow::Result<()>`; any error
6//! surfaces via the CLI's top-level error reporter (stderr).
7//!
8//! ## Phase 1 surface (so far)
9//!
10//! - [`audit_log`] — `doiget audit-log --verify` recomputes the SHA-256 hash
11//! chain on the provenance log and reports any mismatches.
12//! - [`batch`] — `doiget batch <path>` multi-ref orchestrator (rate-bounded).
13//! - [`bib`] — `doiget bib <ref>` BibTeX exporter (Phase 2 starter).
14//! - [`config`] — `doiget config show/path/doctor`.
15//! - [`csl`] — `doiget csl <ref>` exports a stored entry as CSL JSON 1.0.
16//! - [`fetch`] — `doiget fetch <ref>` orchestrator (arXiv E2E + DOI metadata-only).
17//! - [`info`] — prints a stored entry's `Metadata` as TOML on stdout.
18//! - [`list_recent`] — prints up to N most-recently-fetched entries.
19//! - [`search`] — case-insensitive substring search over stored metadata.
20//!
21//! Other subcommands (`serve`) land in separate PRs.
22
23pub mod audit_log;
24pub mod batch;
25pub mod bib;
26pub mod capabilities;
27pub mod config;
28pub mod csl;
29pub mod fetch;
30pub mod info;
31pub mod list_recent;
32pub mod output;
33pub mod provenance;
34pub mod search;
35
36// Phase 4 / Slice 16. Compile-gated by the `citation` Cargo feature
37// (which itself enables `doiget-core/citation`).
38#[cfg(feature = "citation")]
39pub mod graph;
40
41use anyhow::{Context, Result};
42use camino::Utf8PathBuf;
43
44/// Resolve the on-disk store root.
45///
46/// Resolution order (subset of `docs/CONFIG.md` §4 — full CLI-flag /
47/// config-file resolution lands with the `config` subcommand):
48///
49/// 1. `DOIGET_STORE_ROOT` environment variable, if set and non-empty.
50/// 2. Fallback to `$HOME/papers` (POSIX) or `%USERPROFILE%\papers` (Windows).
51///
52/// The env-var hook is sufficient for both real use and integration tests
53/// — tests set `DOIGET_STORE_ROOT` to a `tempfile::TempDir` to keep the
54/// real `~/papers/` untouched.
55pub(crate) fn resolve_store_root() -> Result<Utf8PathBuf> {
56 if let Ok(s) = std::env::var("DOIGET_STORE_ROOT") {
57 if !s.is_empty() {
58 return Ok(Utf8PathBuf::from(s));
59 }
60 }
61 let home = std::env::var("HOME")
62 .or_else(|_| std::env::var("USERPROFILE"))
63 .context(
64 "could not determine home directory: \
65 neither HOME nor USERPROFILE is set, and DOIGET_STORE_ROOT was not provided",
66 )?;
67 Ok(Utf8PathBuf::from(home).join("papers"))
68}