use std::sync::Arc;
use async_trait::async_trait;
use tokio::sync::watch;
pub mod mock;
pub mod refresh;
pub mod static_token;
pub type TokenSourceCause = Arc<dyn std::error::Error + Sync + Send>;
#[derive(Debug, Clone, thiserror::Error)]
pub enum TokenSourceError {
#[error("token temporarily unavailable: {0}")]
Unavailable(TokenSourceCause),
#[error("token rejected: {0}")]
Rejected(TokenSourceCause),
#[error("token source broken: {0}")]
Broken(TokenSourceCause),
}
impl TokenSourceError {
pub fn unavailable(cause: impl Into<Box<dyn std::error::Error + Sync + Send>>) -> Self {
Self::Unavailable(cause.into().into())
}
pub fn rejected(cause: impl Into<Box<dyn std::error::Error + Sync + Send>>) -> Self {
Self::Rejected(cause.into().into())
}
pub fn broken(cause: impl Into<Box<dyn std::error::Error + Sync + Send>>) -> Self {
Self::Broken(cause.into().into())
}
#[must_use]
pub fn is_transient(&self) -> bool {
match self {
Self::Unavailable(_) => true,
Self::Rejected(_) | Self::Broken(_) => false,
}
}
}
pub type TokenSourceWatch = watch::Receiver<Option<Result<String, TokenSourceError>>>;
#[async_trait]
pub trait TokenSource: Send + Sync + 'static {
fn watch(&self) -> TokenSourceWatch;
async fn get_token(&self) -> Result<String, TokenSourceError> {
let mut watch = self.watch();
match watch.borrow_and_update().as_ref() {
Some(Ok(token)) => return Ok(token.clone()),
Some(Err(e)) => return Err(e.clone()),
None => {}
}
watch.changed().await.map_err(TokenSourceError::broken)?;
match watch.borrow().as_ref() {
Some(Ok(token)) => Ok(token.clone()),
Some(Err(e)) => Err(e.clone()),
None => {
Err(TokenSourceError::broken(
"token source watch channel has no value",
))
}
}
}
fn format_header(&self, token: String) -> String {
format!("Bearer {token}")
}
}