Skip to content

Commit f46ccff

Browse files
committed
feat: remove futures and derive_builder
1 parent 01e15be commit f46ccff

4 files changed

Lines changed: 182 additions & 30 deletions

File tree

Cargo.toml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ async-trait = "0.1"
1212
axum = { version = "0.8", features = ["macros"] }
1313
axum-extra = { version = "0.12", features = ["typed-header", "cookie"] }
1414
dashmap = "6.1.0"
15-
derive_builder = "0.20.2"
16-
futures = "0.3"
1715
jsonwebtoken = { version = "10", features = ["rust_crypto"] }
1816
reqwest = { version = "0.12", default-features = false, features = [
1917
"json",

src/local.rs

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use async_trait::async_trait;
2-
use derive_builder::Builder;
32
use jsonwebtoken::{DecodingKey, TokenData, Validation};
43
use serde::de::DeserializeOwned;
54

@@ -26,7 +25,7 @@ use crate::{Error, JwtDecoder};
2625
/// .build()
2726
/// .unwrap();
2827
/// ```
29-
#[derive(Clone, Builder)]
28+
#[derive(Clone)]
3029
pub struct LocalDecoder {
3130
keys: Vec<DecodingKey>,
3231
validation: Validation,
@@ -63,7 +62,46 @@ impl LocalDecoder {
6362

6463
/// Creates a new `LocalDecoderBuilder` for configuring a decoder.
6564
pub fn builder() -> LocalDecoderBuilder {
66-
LocalDecoderBuilder::default()
65+
LocalDecoderBuilder {
66+
keys: None,
67+
validation: None,
68+
}
69+
}
70+
}
71+
72+
/// Builder for `LocalDecoder`.
73+
pub struct LocalDecoderBuilder {
74+
keys: Option<Vec<DecodingKey>>,
75+
validation: Option<Validation>,
76+
}
77+
78+
impl LocalDecoderBuilder {
79+
/// Sets the decoding keys.
80+
pub fn keys(mut self, keys: Vec<DecodingKey>) -> Self {
81+
self.keys = Some(keys);
82+
self
83+
}
84+
85+
/// Sets the validation settings.
86+
pub fn validation(mut self, validation: Validation) -> Self {
87+
self.validation = Some(validation);
88+
self
89+
}
90+
91+
/// Builds the `LocalDecoder`.
92+
///
93+
/// # Errors
94+
///
95+
/// Returns `Error::Configuration` if required fields are missing or invalid.
96+
pub fn build(self) -> Result<LocalDecoder, Error> {
97+
let keys = self
98+
.keys
99+
.ok_or_else(|| Error::Configuration("keys are required".into()))?;
100+
let validation = self
101+
.validation
102+
.ok_or_else(|| Error::Configuration("validation is required".into()))?;
103+
104+
LocalDecoder::new(keys, validation)
67105
}
68106
}
69107

src/remote.rs

Lines changed: 135 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ use std::sync::Arc;
22

33
use async_trait::async_trait;
44
use dashmap::DashMap;
5-
use derive_builder::Builder;
65
use jsonwebtoken::{jwk::JwkSet, DecodingKey, TokenData, Validation};
76
use serde::de::DeserializeOwned;
87
use tokio::sync::Notify;
@@ -14,16 +13,13 @@ const DEFAULT_RETRY_COUNT: usize = 3; // 3 attempts
1413
const DEFAULT_BACKOFF: std::time::Duration = std::time::Duration::from_secs(1); // 1 second
1514

1615
/// Configuration for remote JWKS fetching and caching behavior.
17-
#[derive(Debug, Clone, Builder)]
16+
#[derive(Debug, Clone)]
1817
pub struct RemoteJwksDecoderConfig {
1918
/// Duration to cache JWKS keys before refreshing (default: 1 hour)
20-
#[builder(default = "DEFAULT_CACHE_DURATION")]
2119
pub cache_duration: std::time::Duration,
2220
/// Number of retry attempts when fetching JWKS fails (default: 3)
23-
#[builder(default = "DEFAULT_RETRY_COUNT")]
2421
pub retry_count: usize,
2522
/// Delay between retry attempts (default: 1 second)
26-
#[builder(default = "DEFAULT_BACKOFF")]
2723
pub backoff: std::time::Duration,
2824
}
2925

@@ -40,7 +36,47 @@ impl Default for RemoteJwksDecoderConfig {
4036
impl RemoteJwksDecoderConfig {
4137
/// Creates a new builder for configuring JWKS fetching behavior.
4238
pub fn builder() -> RemoteJwksDecoderConfigBuilder {
43-
RemoteJwksDecoderConfigBuilder::default()
39+
RemoteJwksDecoderConfigBuilder {
40+
cache_duration: None,
41+
retry_count: None,
42+
backoff: None,
43+
}
44+
}
45+
}
46+
47+
/// Builder for `RemoteJwksDecoderConfig`.
48+
pub struct RemoteJwksDecoderConfigBuilder {
49+
cache_duration: Option<std::time::Duration>,
50+
retry_count: Option<usize>,
51+
backoff: Option<std::time::Duration>,
52+
}
53+
54+
impl RemoteJwksDecoderConfigBuilder {
55+
/// Sets the cache duration.
56+
pub fn cache_duration(mut self, cache_duration: std::time::Duration) -> Self {
57+
self.cache_duration = Some(cache_duration);
58+
self
59+
}
60+
61+
/// Sets the retry count.
62+
pub fn retry_count(mut self, retry_count: usize) -> Self {
63+
self.retry_count = Some(retry_count);
64+
self
65+
}
66+
67+
/// Sets the backoff duration.
68+
pub fn backoff(mut self, backoff: std::time::Duration) -> Self {
69+
self.backoff = Some(backoff);
70+
self
71+
}
72+
73+
/// Builds the `RemoteJwksDecoderConfig` with defaults for unset fields.
74+
pub fn build(self) -> RemoteJwksDecoderConfig {
75+
RemoteJwksDecoderConfig {
76+
cache_duration: self.cache_duration.unwrap_or(DEFAULT_CACHE_DURATION),
77+
retry_count: self.retry_count.unwrap_or(DEFAULT_RETRY_COUNT),
78+
backoff: self.backoff.unwrap_or(DEFAULT_BACKOFF),
79+
}
4480
}
4581
}
4682

@@ -67,23 +103,19 @@ impl RemoteJwksDecoderConfig {
67103
/// decoder_clone.refresh_keys_periodically().await;
68104
/// });
69105
/// ```
70-
#[derive(Clone, Builder)]
106+
#[derive(Clone)]
71107
pub struct RemoteJwksDecoder {
72108
/// The JWKS endpoint URL
73109
jwks_url: String,
74110
/// Configuration for caching and retry behavior
75-
#[builder(default = "RemoteJwksDecoderConfig::default()")]
76111
config: RemoteJwksDecoderConfig,
77112
/// Thread-safe cache mapping key IDs to decoding keys
78-
#[builder(default = "Arc::new(DashMap::new())")]
79113
keys_cache: Arc<DashMap<String, DecodingKey>>,
80114
/// JWT validation settings
81115
validation: Validation,
82116
/// HTTP client for fetching JWKS
83-
#[builder(default = "reqwest::Client::new()")]
84117
client: reqwest::Client,
85118
/// Notification for initialization completion
86-
#[builder(default = "Arc::new(Notify::new())")]
87119
initialized: Arc<Notify>,
88120
}
89121

@@ -94,15 +126,12 @@ impl RemoteJwksDecoder {
94126
///
95127
/// Returns `Error::Configuration` if the builder fails to construct the decoder.
96128
pub fn new(jwks_url: String) -> Result<Self, Error> {
97-
RemoteJwksDecoderBuilder::default()
98-
.jwks_url(jwks_url)
99-
.build()
100-
.map_err(|e| Error::Configuration(e.to_string()))
129+
RemoteJwksDecoderBuilder::new().jwks_url(jwks_url).build()
101130
}
102131

103132
/// Creates a new builder for configuring a remote JWKS decoder.
104133
pub fn builder() -> RemoteJwksDecoderBuilder {
105-
RemoteJwksDecoderBuilder::default()
134+
RemoteJwksDecoderBuilder::new()
106135
}
107136

108137
/// Refreshes the JWKS cache with retry logic.
@@ -221,6 +250,96 @@ impl RemoteJwksDecoder {
221250
}
222251
}
223252

253+
/// Builder for `RemoteJwksDecoder`.
254+
pub struct RemoteJwksDecoderBuilder {
255+
jwks_url: Option<String>,
256+
config: Option<RemoteJwksDecoderConfig>,
257+
keys_cache: Option<Arc<DashMap<String, DecodingKey>>>,
258+
validation: Option<Validation>,
259+
client: Option<reqwest::Client>,
260+
initialized: Option<Arc<Notify>>,
261+
}
262+
263+
impl RemoteJwksDecoderBuilder {
264+
/// Creates a new `RemoteJwksDecoderBuilder`.
265+
pub fn new() -> Self {
266+
Self {
267+
jwks_url: None,
268+
config: None,
269+
keys_cache: None,
270+
validation: None,
271+
client: None,
272+
initialized: None,
273+
}
274+
}
275+
276+
/// Sets the JWKS URL.
277+
pub fn jwks_url(mut self, jwks_url: String) -> Self {
278+
self.jwks_url = Some(jwks_url);
279+
self
280+
}
281+
282+
/// Sets the configuration.
283+
pub fn config(mut self, config: RemoteJwksDecoderConfig) -> Self {
284+
self.config = Some(config);
285+
self
286+
}
287+
288+
/// Sets the keys cache.
289+
pub fn keys_cache(mut self, keys_cache: Arc<DashMap<String, DecodingKey>>) -> Self {
290+
self.keys_cache = Some(keys_cache);
291+
self
292+
}
293+
294+
/// Sets the validation settings.
295+
pub fn validation(mut self, validation: Validation) -> Self {
296+
self.validation = Some(validation);
297+
self
298+
}
299+
300+
/// Sets the HTTP client.
301+
pub fn client(mut self, client: reqwest::Client) -> Self {
302+
self.client = Some(client);
303+
self
304+
}
305+
306+
/// Sets the initialized notifier.
307+
pub fn initialized(mut self, initialized: Arc<Notify>) -> Self {
308+
self.initialized = Some(initialized);
309+
self
310+
}
311+
312+
/// Builds the `RemoteJwksDecoder`.
313+
///
314+
/// # Errors
315+
///
316+
/// Returns `Error::Configuration` if required fields are missing.
317+
pub fn build(self) -> Result<RemoteJwksDecoder, Error> {
318+
let jwks_url = self
319+
.jwks_url
320+
.ok_or_else(|| Error::Configuration("jwks_url is required".into()))?;
321+
322+
let validation = self
323+
.validation
324+
.ok_or_else(|| Error::Configuration("validation is required".into()))?;
325+
326+
Ok(RemoteJwksDecoder {
327+
jwks_url,
328+
config: self.config.unwrap_or_default(),
329+
keys_cache: self.keys_cache.unwrap_or_else(|| Arc::new(DashMap::new())),
330+
validation,
331+
client: self.client.unwrap_or_default(),
332+
initialized: self.initialized.unwrap_or_else(|| Arc::new(Notify::new())),
333+
})
334+
}
335+
}
336+
337+
impl Default for RemoteJwksDecoderBuilder {
338+
fn default() -> Self {
339+
Self::new()
340+
}
341+
}
342+
224343
#[async_trait]
225344
impl<T> JwtDecoder<T> for RemoteJwksDecoder
226345
where

tests/integration_test.rs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,7 @@ async fn remote_decoder() {
160160
.cache_duration(Duration::milliseconds(100).to_std().unwrap()) // Short duration for testing
161161
.retry_count(1) // Minimal retries for faster tests
162162
.backoff(Duration::milliseconds(50).to_std().unwrap()) // Short backoff for testing
163-
.build()
164-
.unwrap(),
163+
.build(),
165164
)
166165
.validation(validation)
167166
.build()
@@ -262,8 +261,7 @@ async fn test_remote_decoder_initialization() {
262261
RemoteJwksDecoderConfig::builder()
263262
.cache_duration(Duration::seconds(5).to_std().unwrap())
264263
.retry_count(1)
265-
.build()
266-
.unwrap(),
264+
.build(),
267265
)
268266
.validation(validation)
269267
.build()
@@ -290,11 +288,10 @@ async fn test_remote_decoder_initialization() {
290288
}
291289

292290
// Wait for all decode attempts
293-
let completion_times = futures::future::join_all(handles)
294-
.await
295-
.into_iter()
296-
.map(|r| r.unwrap())
297-
.collect::<Vec<_>>();
291+
let mut completion_times = Vec::new();
292+
for handle in handles {
293+
completion_times.push(handle.await.unwrap());
294+
}
298295

299296
// All tasks should complete at roughly the same time (after the initial 2-second delay)
300297
for time in &completion_times {

0 commit comments

Comments
 (0)