cli_error.rs
1 //! Typed CLI error variants with actionable help text. 2 3 /// Structured CLI errors with built-in suggestion and documentation links. 4 #[derive(thiserror::Error, Debug)] 5 pub enum CliError { 6 #[error("key rotation failed — no pre-rotation commitment found")] 7 NoPrerotationCommitment, 8 9 #[error("identity not found — run `auths init` to create one")] 10 IdentityNotFound, 11 12 #[error("keychain unavailable — set AUTHS_KEYCHAIN_BACKEND=file for headless environments")] 13 KeychainUnavailable, 14 15 #[error("device key '{alias}' not found — import it first with `auths key import`")] 16 DeviceKeyNotFound { alias: String }, 17 18 #[error("passphrase required — set AUTHS_PASSPHRASE env var for CI environments")] 19 PassphraseRequired, 20 21 #[error("attestation expired — issue a new one with `auths device link`")] 22 AttestationExpired, 23 24 #[error("capability '{capability}' not granted — check device authorization policies")] 25 MissingCapability { capability: String }, 26 } 27 28 impl CliError { 29 /// Human-readable suggestion for how to recover from this error. 30 pub fn suggestion(&self) -> &str { 31 match self { 32 Self::NoPrerotationCommitment => { 33 "Run: auths key precommit --next-key <path-to-next-pubkey>" 34 } 35 Self::IdentityNotFound => "Run: auths init", 36 Self::KeychainUnavailable => { 37 "Set AUTHS_KEYCHAIN_BACKEND=file and AUTHS_PASSPHRASE=<passphrase> in your environment." 38 } 39 Self::DeviceKeyNotFound { .. } => "Run: auths key import --alias <alias> --file <path>", 40 Self::PassphraseRequired => { 41 "Set AUTHS_PASSPHRASE=<your-passphrase> in the environment, or run interactively." 42 } 43 Self::AttestationExpired => "Run: auths device link --device-alias <name>", 44 Self::MissingCapability { .. } => { 45 "Run: auths device link --capability <cap> to add the capability." 46 } 47 } 48 } 49 50 /// Documentation URL for this error, if available. 51 /// 52 /// These URLs map to Markdown source files under `docs/guides/` in this repository 53 /// (e.g., `docs/guides/key-rotation.md`). Keep the slugs in sync with those filenames 54 /// so static site generators (e.g., mdBook, Docusaurus) can serve them correctly. 55 pub fn docs_url(&self) -> Option<&str> { 56 match self { 57 Self::NoPrerotationCommitment => Some("https://docs.auths.dev/guides/key-rotation"), 58 Self::IdentityNotFound => Some("https://docs.auths.dev/guides/getting-started"), 59 Self::KeychainUnavailable => Some("https://docs.auths.dev/guides/headless-setup"), 60 _ => None, 61 } 62 } 63 }