use super::{ADMIN_SCOPE, TOKEN_PREFIX};
#[derive(Debug)]
pub enum TokenError {
InvalidPrefix,
Expired(Option<ExpiryFacts>),
Revoked,
NotFound(String),
Invalid(String),
InsufficientScope,
LimitExceeded(Option<BudgetFacts>),
TokenLimitExceeded(Option<BudgetFacts>),
RateLimitExceeded,
Storage(String),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ExpiryFacts {
pub issued_at: i64,
pub expires_at: i64,
pub ago_seconds: i64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BudgetFacts {
pub used: u64,
pub limit: u64,
}
fn render_time(seconds: i64) -> String {
chrono::DateTime::from_timestamp(seconds, 0)
.map_or_else(|| seconds.to_string(), |time| time.to_rfc3339())
}
fn render_duration(seconds: i64) -> String {
let seconds = seconds.abs();
match seconds {
0..=90 => format!("{seconds}s"),
91..=5399 => format!("{}m", (seconds + 30) / 60),
5400..=172_799 => format!("{}h", (seconds + 1800) / 3600),
_ => format!("{}d", (seconds + 43200) / 86400),
}
}
impl std::fmt::Display for TokenError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::InvalidPrefix => {
write!(
f,
"Token must start with '{TOKEN_PREFIX}' or '{}' prefix",
crate::token::CODEX_TOKEN_PREFIX
)
}
Self::Expired(None) => write!(f, "Token has expired"),
Self::Expired(Some(facts)) => write!(
f,
"Token expired at {} ({} ago)",
render_time(facts.expires_at),
render_duration(facts.ago_seconds)
),
Self::Revoked => write!(f, "Token has been revoked"),
Self::NotFound(id) => write!(f, "Token not found: {id}"),
Self::Invalid(msg) => write!(f, "Invalid token: {msg}"),
Self::InsufficientScope => {
write!(f, "Token does not carry the '{ADMIN_SCOPE}' scope")
}
Self::LimitExceeded(None) => write!(f, "Token has reached its request limit"),
Self::LimitExceeded(Some(facts)) => write!(
f,
"Token has reached its request limit: {} of {} requests used",
facts.used, facts.limit
),
Self::TokenLimitExceeded(None) => write!(f, "Token has reached its token limit"),
Self::TokenLimitExceeded(Some(facts)) => write!(
f,
"Token has reached its token limit: {} of {} tokens used",
facts.used, facts.limit
),
Self::RateLimitExceeded => write!(f, "Token has reached its per-minute rate limit"),
Self::Storage(msg) => write!(f, "Token storage error: {msg}"),
}
}
}
impl TokenError {
#[must_use]
pub fn client_message(&self) -> std::borrow::Cow<'static, str> {
use std::borrow::Cow;
match self {
Self::InvalidPrefix | Self::Invalid(_) => Cow::Borrowed("invalid token"),
Self::Expired(None) => Cow::Borrowed(
"Token has expired: this is the router's own token, not the model provider's. \
A per-run token from `router with` lives for --run-ttl-hours; re-running the \
command mints a new one.",
),
Self::Expired(Some(facts)) => Cow::Owned(format!(
"Token has expired: this is the router's own token, not the model provider's. \
Issued {issued}, good for {lifetime}, expired {expired} ({ago} ago). \
A per-run token from `router with` lives for --run-ttl-hours; re-running the \
command mints a new one.",
issued = render_time(facts.issued_at),
lifetime = render_duration(facts.expires_at - facts.issued_at),
expired = render_time(facts.expires_at),
ago = render_duration(facts.ago_seconds),
)),
Self::Revoked => Cow::Borrowed("Token has been revoked"),
Self::NotFound(_) => Cow::Borrowed("token not found"),
Self::InsufficientScope => Cow::Borrowed("insufficient token scope"),
Self::LimitExceeded(None) => Cow::Borrowed("Token has reached its request limit"),
Self::LimitExceeded(Some(facts)) => Cow::Owned(format!(
"Token has reached its request limit: {} of {} requests used. Issue a token \
with a larger --max-requests, or use a new one.",
facts.used, facts.limit
)),
Self::TokenLimitExceeded(None) => Cow::Borrowed("Token has reached its token limit"),
Self::TokenLimitExceeded(Some(facts)) => Cow::Owned(format!(
"Token has reached its token limit: {} of {} tokens used. Issue a token with a \
larger --max-tokens, or use a new one.",
facts.used, facts.limit
)),
Self::RateLimitExceeded => Cow::Borrowed("Token has reached its per-minute rate limit"),
Self::Storage(_) => Cow::Borrowed("token validation failed"),
}
}
}
impl std::error::Error for TokenError {}