use async_trait::async_trait;
mod static_provider;
pub use static_provider::StaticTokenProvider;
#[async_trait]
pub trait TokenProvider: Send + Sync {
async fn get_token(&self, scopes: &[&str]) -> Result<String, TokenError>;
fn on_token_rejected(&self) {
}
fn quota_project_id(&self) -> Option<&str> {
None
}
}
#[derive(Debug, thiserror::Error)]
pub enum TokenError {
#[error("No credentials found")]
NoCredentialsFound,
#[error("Failed to read credentials file: {path}")]
CredentialFileError {
path: std::path::PathBuf,
#[source]
source: std::io::Error,
},
#[error("Invalid credentials: {message}")]
InvalidCredentials {
message: String,
},
#[error("Token refresh failed: {message}")]
RefreshFailed {
message: String,
},
}
#[cfg(test)]
mod tests {
use super::*;
struct TestProvider {
quota_project: Option<String>,
}
#[async_trait]
impl TokenProvider for TestProvider {
async fn get_token(&self, _scopes: &[&str]) -> Result<String, TokenError> {
Ok("test-token".to_string())
}
fn quota_project_id(&self) -> Option<&str> {
self.quota_project.as_deref()
}
}
#[test]
fn test_quota_project_id_default_is_none() {
let provider = crate::token::StaticTokenProvider::new("token");
assert!(provider.quota_project_id().is_none());
}
#[test]
fn test_quota_project_id_custom_impl() {
let provider = TestProvider {
quota_project: Some("my-project".to_string()),
};
assert_eq!(provider.quota_project_id(), Some("my-project"));
}
}