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