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 pub fn masked_api_key(&self) -> String {
41 if self.api_key.len() > 12 {
42 format!("{}...{}", &self.api_key[..12], &self.api_key[self.api_key.len() - 4..])
43 } else {
44 "***".to_string()
45 }
46 }
47}
48
49#[derive(Debug, Clone, Serialize, Deserialize)]
53pub struct DeviceCodeResponse {
54 pub device_code: String,
56
57 pub user_code: String,
59
60 pub verification_uri: String,
62
63 pub expires_in: u32,
65
66 pub interval: u32,
68}
69
70#[derive(Debug, Clone, Serialize, Deserialize)]
72#[serde(tag = "status", rename_all = "snake_case")]
73pub enum DeviceCodeStatus {
74 Pending,
76
77 Authorized {
79 api_key: String,
80 user_id: String,
81 email: String,
82 name: Option<String>,
83 },
84
85 Denied,
87
88 Expired,
90}
91
92impl DeviceCodeStatus {
93 pub fn is_terminal(&self) -> bool {
95 !matches!(self, DeviceCodeStatus::Pending)
96 }
97
98 pub fn is_authorized(&self) -> bool {
100 matches!(self, DeviceCodeStatus::Authorized { .. })
101 }
102}
103
104#[derive(Debug, Clone, Serialize, Deserialize)]
106#[serde(tag = "error", rename_all = "snake_case")]
107pub enum AuthError {
108 NetworkError { message: String },
110
111 ServerError { message: String, status_code: Option<u16> },
113
114 ExpiredCode,
116
117 AccessDenied,
119
120 InvalidCredentials { message: String },
122
123 RateLimited { retry_after: Option<u32> },
125}
126
127impl std::fmt::Display for AuthError {
128 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
129 match self {
130 AuthError::NetworkError { message } => write!(f, "Network error: {}", message),
131 AuthError::ServerError { message, status_code } => {
132 if let Some(code) = status_code {
133 write!(f, "Server error ({}): {}", code, message)
134 } else {
135 write!(f, "Server error: {}", message)
136 }
137 }
138 AuthError::ExpiredCode => write!(f, "Device code expired"),
139 AuthError::AccessDenied => write!(f, "Access denied by user"),
140 AuthError::InvalidCredentials { message } => write!(f, "Invalid credentials: {}", message),
141 AuthError::RateLimited { retry_after } => {
142 if let Some(secs) = retry_after {
143 write!(f, "Rate limited, retry after {} seconds", secs)
144 } else {
145 write!(f, "Rate limited")
146 }
147 }
148 }
149 }
150}
151
152impl std::error::Error for AuthError {}