2
2
3
3
use crate :: config;
4
4
use crate :: db:: { ConnectionConfig , connection_url, make_manager_config} ;
5
+ use std:: collections:: HashMap ;
5
6
use std:: sync:: Arc ;
6
7
7
8
use crate :: email:: Emails ;
8
9
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 } ;
11
12
use axum:: extract:: { FromRef , FromRequestParts , State } ;
13
+ use bon:: Builder ;
12
14
use crates_io_github:: GitHubClient ;
13
15
use deadpool_diesel:: Runtime ;
14
16
use derive_more:: Deref ;
@@ -25,6 +27,7 @@ type DeadpoolResult = Result<
25
27
26
28
/// The `App` struct holds the main components of the application like
27
29
/// the database connection pool and configurations
30
+ #[ derive( Builder ) ]
28
31
pub struct App {
29
32
/// Database connection pool connected to the primary database
30
33
pub primary_database : DeadpoolPool < AsyncPgConnection > ,
@@ -49,29 +52,27 @@ pub struct App {
49
52
pub storage : Arc < Storage > ,
50
53
51
54
/// Metrics related to the service as a whole
55
+ #[ builder( default = ServiceMetrics :: new( ) . expect( "could not initialize service metrics" ) ) ]
52
56
pub service_metrics : ServiceMetrics ,
53
57
54
58
/// Metrics related to this specific instance of the service
59
+ #[ builder( default = InstanceMetrics :: new( ) . expect( "could not initialize instance metrics" ) ) ]
55
60
pub instance_metrics : InstanceMetrics ,
56
61
57
62
/// Rate limit select actions.
58
63
pub rate_limiter : RateLimiter ,
59
64
}
60
65
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
+ {
70
74
use oauth2:: { AuthUrl , TokenUrl } ;
71
75
72
- let instance_metrics =
73
- InstanceMetrics :: new ( ) . expect ( "could not initialize instance metrics" ) ;
74
-
75
76
let auth_url = "https://github.com/login/oauth/authorize" ;
76
77
let auth_url = AuthUrl :: new ( auth_url. into ( ) ) . unwrap ( ) ;
77
78
let token_url = "https://github.com/login/oauth/access_token" ;
@@ -82,43 +83,54 @@ impl App {
82
83
. set_auth_uri ( auth_url)
83
84
. set_token_uri ( token_url) ;
84
85
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
+ {
85
97
let primary_database = {
86
98
use secrecy:: ExposeSecret ;
87
99
88
100
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 ,
91
103
} ;
92
104
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 ) ;
95
107
let manager = AsyncDieselConnectionManager :: new_with_config ( url, manager_config) ;
96
108
97
109
DeadpoolPool :: builder ( manager)
98
110
. 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 ) )
101
113
. post_create ( primary_db_connection_config)
102
114
. build ( )
103
115
. unwrap ( )
104
116
} ;
105
117
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 ( ) {
107
119
use secrecy:: ExposeSecret ;
108
120
109
121
let replica_db_connection_config = ConnectionConfig {
110
- statement_timeout : config. db . statement_timeout ,
122
+ statement_timeout : config. statement_timeout ,
111
123
read_only : pool_config. read_only_mode ,
112
124
} ;
113
125
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 ) ;
116
128
let manager = AsyncDieselConnectionManager :: new_with_config ( url, manager_config) ;
117
129
118
130
let pool = DeadpoolPool :: builder ( manager)
119
131
. runtime ( Runtime :: Tokio1 )
120
132
. max_size ( pool_config. pool_size )
121
- . wait_timeout ( Some ( config. db . connection_timeout ) )
133
+ . wait_timeout ( Some ( config. connection_timeout ) )
122
134
. post_create ( replica_db_connection_config)
123
135
. build ( )
124
136
. unwrap ( ) ;
@@ -128,20 +140,32 @@ impl App {
128
140
None
129
141
} ;
130
142
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)
143
145
}
144
146
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 {
145
169
/// A unique key to generate signed cookies
146
170
pub fn session_key ( & self ) -> & cookie:: Key {
147
171
& self . config . session_key
0 commit comments