1use crate::{Context, Error, ProvideCredential, Result, SignRequest, SigningCredential};
19use std::any::type_name;
20use std::sync::{Arc, Mutex};
21use std::time::Duration;
22
23#[derive(Clone, Debug)]
25pub struct Signer<K: SigningCredential> {
26 ctx: Context,
27 loader: Arc<dyn ProvideCredential<Credential = K>>,
28 builder: Arc<dyn SignRequest<Credential = K>>,
29 credential: Arc<Mutex<Option<K>>>,
30}
31
32impl<K: SigningCredential> Signer<K> {
33 pub fn new(
35 ctx: Context,
36 loader: impl ProvideCredential<Credential = K>,
37 builder: impl SignRequest<Credential = K>,
38 ) -> Self {
39 Self {
40 ctx,
41
42 loader: Arc::new(loader),
43 builder: Arc::new(builder),
44 credential: Arc::new(Mutex::new(None)),
45 }
46 }
47
48 pub fn with_context(mut self, ctx: Context) -> Self {
50 self.ctx = ctx;
51 self
52 }
53
54 pub fn with_credential_provider(
56 mut self,
57 provider: impl ProvideCredential<Credential = K>,
58 ) -> Self {
59 self.loader = Arc::new(provider);
60 self.credential = Arc::new(Mutex::new(None)); self
62 }
63
64 pub fn with_request_signer(mut self, signer: impl SignRequest<Credential = K>) -> Self {
66 self.builder = Arc::new(signer);
67 self
68 }
69
70 pub async fn sign(
72 &self,
73 req: &mut http::request::Parts,
74 expires_in: Option<Duration>,
75 ) -> Result<()> {
76 let credential = self.credential.lock().expect("lock poisoned").clone();
77 let credential = if credential.is_valid() {
78 credential
79 } else {
80 let ctx = self.loader.provide_credential(&self.ctx).await?;
81 *self.credential.lock().expect("lock poisoned") = ctx.clone();
82 ctx
83 };
84
85 let credential_ref = credential.as_ref().ok_or_else(|| {
86 Error::credential_invalid("failed to load signing credential")
87 .with_context(format!("credential_type: {}", type_name::<K>()))
88 })?;
89
90 self.builder
91 .sign_request(&self.ctx, req, Some(credential_ref), expires_in)
92 .await
93 }
94}