Skip to content

Commit 1d5ce08

Browse files
authored
Merge pull request #11049 from Turbo87/app-builder
Derive `Builder` for `App` struct
2 parents 48cefa0 + a5105d6 commit 1d5ce08

File tree

3 files changed

+81
-39
lines changed

3 files changed

+81
-39
lines changed

src/app.rs

Lines changed: 61 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22
33
use crate::config;
44
use crate::db::{ConnectionConfig, connection_url, make_manager_config};
5+
use std::collections::HashMap;
56
use std::sync::Arc;
67

78
use crate::email::Emails;
89
use crate::metrics::{InstanceMetrics, ServiceMetrics};
9-
use crate::rate_limiter::RateLimiter;
10-
use crate::storage::Storage;
10+
use crate::rate_limiter::{LimitedAction, RateLimiter, RateLimiterConfig};
11+
use crate::storage::{Storage, StorageConfig};
1112
use axum::extract::{FromRef, FromRequestParts, State};
13+
use bon::Builder;
1214
use crates_io_github::GitHubClient;
1315
use deadpool_diesel::Runtime;
1416
use derive_more::Deref;
@@ -25,6 +27,7 @@ type DeadpoolResult = Result<
2527

2628
/// The `App` struct holds the main components of the application like
2729
/// the database connection pool and configurations
30+
#[derive(Builder)]
2831
pub struct App {
2932
/// Database connection pool connected to the primary database
3033
pub primary_database: DeadpoolPool<AsyncPgConnection>,
@@ -49,29 +52,27 @@ pub struct App {
4952
pub storage: Arc<Storage>,
5053

5154
/// Metrics related to the service as a whole
55+
#[builder(default = ServiceMetrics::new().expect("could not initialize service metrics"))]
5256
pub service_metrics: ServiceMetrics,
5357

5458
/// Metrics related to this specific instance of the service
59+
#[builder(default = InstanceMetrics::new().expect("could not initialize instance metrics"))]
5560
pub instance_metrics: InstanceMetrics,
5661

5762
/// Rate limit select actions.
5863
pub rate_limiter: RateLimiter,
5964
}
6065

61-
impl App {
62-
/// Creates a new `App` with a given `Config` and an optional HTTP `Client`
63-
///
64-
/// Configures and sets up:
65-
///
66-
/// - GitHub OAuth
67-
/// - Database connection pools
68-
/// - A `git2::Repository` instance from the index repo checkout (that server.rs ensures exists)
69-
pub fn new(config: config::Server, emails: Emails, github: Box<dyn GitHubClient>) -> App {
66+
impl<S: app_builder::State> AppBuilder<S> {
67+
pub fn github_oauth_from_config(
68+
self,
69+
config: &config::Server,
70+
) -> AppBuilder<app_builder::SetGithubOauth<S>>
71+
where
72+
S::GithubOauth: app_builder::IsUnset,
73+
{
7074
use oauth2::{AuthUrl, TokenUrl};
7175

72-
let instance_metrics =
73-
InstanceMetrics::new().expect("could not initialize instance metrics");
74-
7576
let auth_url = "https://github.com/login/oauth/authorize";
7677
let auth_url = AuthUrl::new(auth_url.into()).unwrap();
7778
let token_url = "https://github.com/login/oauth/access_token";
@@ -82,43 +83,54 @@ impl App {
8283
.set_auth_uri(auth_url)
8384
.set_token_uri(token_url);
8485

86+
self.github_oauth(github_oauth)
87+
}
88+
89+
pub fn databases_from_config(
90+
self,
91+
config: &config::DatabasePools,
92+
) -> AppBuilder<app_builder::SetReplicaDatabase<app_builder::SetPrimaryDatabase<S>>>
93+
where
94+
S::PrimaryDatabase: app_builder::IsUnset,
95+
S::ReplicaDatabase: app_builder::IsUnset,
96+
{
8597
let primary_database = {
8698
use secrecy::ExposeSecret;
8799

88100
let primary_db_connection_config = ConnectionConfig {
89-
statement_timeout: config.db.statement_timeout,
90-
read_only: config.db.primary.read_only_mode,
101+
statement_timeout: config.statement_timeout,
102+
read_only: config.primary.read_only_mode,
91103
};
92104

93-
let url = connection_url(&config.db, config.db.primary.url.expose_secret());
94-
let manager_config = make_manager_config(config.db.enforce_tls);
105+
let url = connection_url(config, config.primary.url.expose_secret());
106+
let manager_config = make_manager_config(config.enforce_tls);
95107
let manager = AsyncDieselConnectionManager::new_with_config(url, manager_config);
96108

97109
DeadpoolPool::builder(manager)
98110
.runtime(Runtime::Tokio1)
99-
.max_size(config.db.primary.pool_size)
100-
.wait_timeout(Some(config.db.connection_timeout))
111+
.max_size(config.primary.pool_size)
112+
.wait_timeout(Some(config.connection_timeout))
101113
.post_create(primary_db_connection_config)
102114
.build()
103115
.unwrap()
104116
};
105117

106-
let replica_database = if let Some(pool_config) = config.db.replica.as_ref() {
118+
let replica_database = if let Some(pool_config) = config.replica.as_ref() {
107119
use secrecy::ExposeSecret;
108120

109121
let replica_db_connection_config = ConnectionConfig {
110-
statement_timeout: config.db.statement_timeout,
122+
statement_timeout: config.statement_timeout,
111123
read_only: pool_config.read_only_mode,
112124
};
113125

114-
let url = connection_url(&config.db, pool_config.url.expose_secret());
115-
let manager_config = make_manager_config(config.db.enforce_tls);
126+
let url = connection_url(config, pool_config.url.expose_secret());
127+
let manager_config = make_manager_config(config.enforce_tls);
116128
let manager = AsyncDieselConnectionManager::new_with_config(url, manager_config);
117129

118130
let pool = DeadpoolPool::builder(manager)
119131
.runtime(Runtime::Tokio1)
120132
.max_size(pool_config.pool_size)
121-
.wait_timeout(Some(config.db.connection_timeout))
133+
.wait_timeout(Some(config.connection_timeout))
122134
.post_create(replica_db_connection_config)
123135
.build()
124136
.unwrap();
@@ -128,20 +140,32 @@ impl App {
128140
None
129141
};
130142

131-
App {
132-
primary_database,
133-
replica_database,
134-
github,
135-
github_oauth,
136-
emails,
137-
storage: Arc::new(Storage::from_config(&config.storage)),
138-
service_metrics: ServiceMetrics::new().expect("could not initialize service metrics"),
139-
instance_metrics,
140-
rate_limiter: RateLimiter::new(config.rate_limiter.clone()),
141-
config: Arc::new(config),
142-
}
143+
self.primary_database(primary_database)
144+
.maybe_replica_database(replica_database)
143145
}
144146

147+
pub fn storage_from_config(
148+
self,
149+
config: &StorageConfig,
150+
) -> AppBuilder<app_builder::SetStorage<S>>
151+
where
152+
S::Storage: app_builder::IsUnset,
153+
{
154+
self.storage(Arc::new(Storage::from_config(config)))
155+
}
156+
157+
pub fn rate_limiter_from_config(
158+
self,
159+
config: HashMap<LimitedAction, RateLimiterConfig>,
160+
) -> AppBuilder<app_builder::SetRateLimiter<S>>
161+
where
162+
S::RateLimiter: app_builder::IsUnset,
163+
{
164+
self.rate_limiter(RateLimiter::new(config))
165+
}
166+
}
167+
168+
impl App {
145169
/// A unique key to generate signed cookies
146170
pub fn session_key(&self) -> &cookie::Key {
147171
&self.config.session_key

src/bin/server.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,17 @@ fn main() -> anyhow::Result<()> {
3333
let github = RealGitHubClient::new(client);
3434
let github = Box::new(github);
3535

36-
let app = Arc::new(App::new(config, emails, github));
36+
let app = App::builder()
37+
.databases_from_config(&config.db)
38+
.github(github)
39+
.github_oauth_from_config(&config)
40+
.emails(emails)
41+
.storage_from_config(&config.storage)
42+
.rate_limiter_from_config(config.rate_limiter.clone())
43+
.config(Arc::new(config))
44+
.build();
45+
46+
let app = Arc::new(app);
3747

3848
// Start the background thread periodically logging instance metrics.
3949
log_instance_metrics_thread(app.clone());

src/tests/util/test_app.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,15 @@ fn build_app(config: config::Server, github: Option<MockGitHubClient>) -> (Arc<A
488488
let github = github.unwrap_or_else(|| MOCK_GITHUB_DATA.as_mock_client());
489489
let github = Box::new(github);
490490

491-
let app = App::new(config, emails, github);
491+
let app = App::builder()
492+
.databases_from_config(&config.db)
493+
.github(github)
494+
.github_oauth_from_config(&config)
495+
.emails(emails)
496+
.storage_from_config(&config.storage)
497+
.rate_limiter_from_config(config.rate_limiter.clone())
498+
.config(Arc::new(config))
499+
.build();
492500

493501
let app = Arc::new(app);
494502
let router = crate::build_handler(Arc::clone(&app));

0 commit comments

Comments
 (0)