1use serde::{Deserialize, Serialize};
2use thiserror::Error;
3
4#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
6#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
7pub enum ErrorCode {
8 AuthFailed,
9 AccessDenied,
10 CredentialNotFound,
11 VaultLocked,
12 LeaseExpired,
13 RateLimited,
14 SessionExpired,
15 #[serde(other)]
16 Unknown,
17}
18
19impl std::fmt::Display for ErrorCode {
20 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21 match self {
22 Self::AuthFailed => write!(f, "AUTH_FAILED"),
23 Self::AccessDenied => write!(f, "ACCESS_DENIED"),
24 Self::CredentialNotFound => write!(f, "CREDENTIAL_NOT_FOUND"),
25 Self::VaultLocked => write!(f, "VAULT_LOCKED"),
26 Self::LeaseExpired => write!(f, "LEASE_EXPIRED"),
27 Self::RateLimited => write!(f, "RATE_LIMITED"),
28 Self::SessionExpired => write!(f, "SESSION_EXPIRED"),
29 Self::Unknown => write!(f, "UNKNOWN"),
30 }
31 }
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct VaultError {
37 pub code: ErrorCode,
38 pub message: String,
39 #[serde(skip_serializing_if = "Option::is_none")]
40 pub detail: Option<String>,
41 #[serde(skip_serializing_if = "Option::is_none")]
42 pub suggestion: Option<String>,
43 #[serde(skip_serializing_if = "Option::is_none")]
44 pub docs_url: Option<String>,
45 #[serde(skip_serializing_if = "Option::is_none")]
46 pub context: Option<serde_json::Value>,
47}
48
49impl std::fmt::Display for VaultError {
50 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51 write!(f, "[{}] {}", self.code, self.message)?;
52 if let Some(ref detail) = self.detail {
53 write!(f, " — {detail}")?;
54 }
55 Ok(())
56 }
57}
58
59#[derive(Debug, Error)]
61pub enum SanctumError {
62 #[error("IO error: {0}")]
63 Io(#[from] std::io::Error),
64
65 #[error("JSON error: {0}")]
66 Json(#[from] serde_json::Error),
67
68 #[error("Vault error: {0}")]
69 Vault(Box<VaultError>),
70
71 #[error("Protocol error: {0}")]
72 Protocol(String),
73
74 #[error("Authentication failed: {0}")]
75 Auth(String),
76
77 #[error("Signature error: {0}")]
78 Signature(#[from] ed25519_dalek::SignatureError),
79}
80
81pub type Result<T> = std::result::Result<T, SanctumError>;