mecha10_cli/types/
credentials.rs1use chrono::{DateTime, Utc};
6use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct Credentials {
13 pub api_key: String,
15
16 pub user_id: String,
18
19 pub email: String,
21
22 #[serde(default)]
24 pub name: Option<String>,
25
26 pub authenticated_at: DateTime<Utc>,
28
29 pub auth_url: String,
31}
32
33impl Credentials {
34 pub fn is_valid(&self) -> bool {
36 !self.api_key.is_empty() && self.api_key.starts_with("mecha_")
37 }
38
39 #[allow(dead_code)]
41 pub fn masked_api_key(&self) -> String {
42 if self.api_key.len() > 12 {
43 format!("{}...{}", &self.api_key[..12], &self.api_key[self.api_key.len() - 4..])
44 } else {
45 "***".to_string()
46 }
47 }
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct DeviceCodeResponse {
55 pub device_code: String,
57
58 pub user_code: String,
60
61 pub verification_uri: String,
63
64 pub expires_in: u32,
66
67 pub interval: u32,
69}
70
71#[derive(Debug, Clone, Serialize, Deserialize)]
73#[serde(tag = "status", rename_all = "snake_case")]
74pub enum DeviceCodeStatus {
75 Pending,
77
78 Authorized {
80 api_key: String,
81 user_id: String,
82 email: String,
83 name: Option<String>,
84 },
85
86 Denied,
88
89 Expired,
91}
92
93impl DeviceCodeStatus {
94 #[allow(dead_code)]
96 pub fn is_terminal(&self) -> bool {
97 !matches!(self, DeviceCodeStatus::Pending)
98 }
99
100 #[allow(dead_code)]
102 pub fn is_authorized(&self) -> bool {
103 matches!(self, DeviceCodeStatus::Authorized { .. })
104 }
105}
106
107#[derive(Debug, Clone, Serialize, Deserialize)]
109#[serde(tag = "error", rename_all = "snake_case")]
110pub enum AuthError {
111 NetworkError { message: String },
113
114 ServerError { message: String, status_code: Option<u16> },
116
117 ExpiredCode,
119
120 AccessDenied,
122
123 InvalidCredentials { message: String },
125
126 RateLimited { retry_after: Option<u32> },
128}
129
130impl std::fmt::Display for AuthError {
131 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
132 match self {
133 AuthError::NetworkError { message } => write!(f, "Network error: {}", message),
134 AuthError::ServerError { message, status_code } => {
135 if let Some(code) = status_code {
136 write!(f, "Server error ({}): {}", code, message)
137 } else {
138 write!(f, "Server error: {}", message)
139 }
140 }
141 AuthError::ExpiredCode => write!(f, "Device code expired"),
142 AuthError::AccessDenied => write!(f, "Access denied by user"),
143 AuthError::InvalidCredentials { message } => write!(f, "Invalid credentials: {}", message),
144 AuthError::RateLimited { retry_after } => {
145 if let Some(secs) = retry_after {
146 write!(f, "Rate limited, retry after {} seconds", secs)
147 } else {
148 write!(f, "Rate limited")
149 }
150 }
151 }
152 }
153}
154
155impl std::error::Error for AuthError {}