22
33use crate :: config;
44use crate :: db:: { ConnectionConfig , connection_url, make_manager_config} ;
5+ use std:: collections:: HashMap ;
56use std:: sync:: Arc ;
67
78use crate :: email:: Emails ;
89use 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 } ;
1112use axum:: extract:: { FromRef , FromRequestParts , State } ;
13+ use bon:: Builder ;
1214use crates_io_github:: GitHubClient ;
1315use deadpool_diesel:: Runtime ;
1416use 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 ) ]
2831pub 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
0 commit comments