use crate::Context;
use crate::Error;
use crate::ProvideCredential;
use crate::ProvideCredentialDyn;
use crate::Result;
use crate::SignRequest;
use crate::SignRequestDyn;
use crate::SigningCredential;
use std::any::type_name;
use std::sync::{Arc, Mutex};
use std::time::Duration;
#[derive(Clone, Debug)]
pub struct Signer<K: SigningCredential> {
ctx: Context,
loader: Arc<dyn ProvideCredentialDyn<Credential = K>>,
builder: Arc<dyn SignRequestDyn<Credential = K>>,
credential: Arc<Mutex<Option<K>>>,
}
impl<K: SigningCredential> Signer<K> {
pub fn new(
ctx: Context,
loader: impl ProvideCredential<Credential = K>,
builder: impl SignRequest<Credential = K>,
) -> Self {
Self {
ctx,
loader: Arc::new(loader),
builder: Arc::new(builder),
credential: Arc::new(Mutex::new(None)),
}
}
pub fn with_context(mut self, ctx: Context) -> Self {
self.ctx = ctx;
self
}
pub fn with_credential_provider(
mut self,
provider: impl ProvideCredential<Credential = K>,
) -> Self {
self.loader = Arc::new(provider);
self.credential = Arc::new(Mutex::new(None)); self
}
pub fn with_request_signer(mut self, signer: impl SignRequest<Credential = K>) -> Self {
self.builder = Arc::new(signer);
self
}
pub async fn sign(
&self,
req: &mut http::request::Parts,
expires_in: Option<Duration>,
) -> Result<()> {
let credential = self.credential.lock().expect("lock poisoned").clone();
let credential = if credential.is_valid() {
credential
} else {
let ctx = self.loader.provide_credential_dyn(&self.ctx).await?;
*self.credential.lock().expect("lock poisoned") = ctx.clone();
ctx
};
let credential_ref = credential.as_ref().ok_or_else(|| {
Error::credential_invalid("failed to load signing credential")
.with_context(format!("credential_type: {}", type_name::<K>()))
})?;
self.builder
.sign_request_dyn(&self.ctx, req, Some(credential_ref), expires_in)
.await
}
}