@@ -92,9 +92,19 @@ impl FromStr for LevelSizes {
92
92
}
93
93
}
94
94
95
- fn main ( ) {
96
- #[ allow( deprecated) ]
97
- let matches = App :: new ( crate_name ! ( ) )
95
+ struct Config {
96
+ db_url : String ,
97
+ output_file : Option < File > ,
98
+ room_id : String ,
99
+ max_state_group : Option < i64 > ,
100
+ min_saved_rows : Option < i32 > ,
101
+ transactions : bool ,
102
+ level_sizes : LevelSizes ,
103
+ }
104
+
105
+ impl Config {
106
+ fn parse_arguments ( ) -> Config {
107
+ let matches = App :: new ( crate_name ! ( ) )
98
108
. version ( crate_version ! ( ) )
99
109
. author ( crate_authors ! ( "\n " ) )
100
110
. about ( crate_description ! ( ) )
@@ -156,33 +166,50 @@ fn main() {
156
166
. takes_value ( true ) ,
157
167
) . get_matches ( ) ;
158
168
159
- let db_url = matches
160
- . value_of ( "postgres-url" )
161
- . expect ( "db url should be required" ) ;
169
+ let db_url = matches
170
+ . value_of ( "postgres-url" )
171
+ . expect ( "db url should be required" ) ;
172
+
173
+ let output_file = matches
174
+ . value_of ( "output_file" )
175
+ . map ( |path| File :: create ( path) . unwrap ( ) ) ;
162
176
163
- let mut output_file = matches
164
- . value_of ( "output_file " )
165
- . map ( |path| File :: create ( path ) . unwrap ( ) ) ;
177
+ let room_id = matches
178
+ . value_of ( "room_id " )
179
+ . expect ( "room_id should be required since no file" ) ;
166
180
167
- let room_id = matches
168
- . value_of ( "room_id " )
169
- . expect ( "room_id should be required since no file" ) ;
181
+ let max_state_group = matches
182
+ . value_of ( "max_state_group " )
183
+ . map ( |s| s . parse ( ) . expect ( "max_state_group must be an integer" ) ) ;
170
184
171
- let max_state_group = matches
172
- . value_of ( "max_state_group " )
173
- . map ( |s| s . parse ( ) . expect ( "max_state_group must be an integer" ) ) ;
185
+ let min_saved_rows = matches
186
+ . value_of ( "min_saved_rows " )
187
+ . map ( |v| v . parse ( ) . expect ( "COUNT must be an integer" ) ) ;
174
188
175
- let min_saved_rows = matches
176
- . value_of ( "min_saved_rows" )
177
- . map ( |v| v. parse ( ) . expect ( "COUNT must be an integer" ) ) ;
189
+ let transactions = matches. is_present ( "transactions" ) ;
178
190
179
- let transactions = matches . is_present ( "transactions" ) ;
191
+ let level_sizes = value_t_or_exit ! ( matches , "level_sizes" , LevelSizes ) ;
180
192
181
- let level_sizes = value_t_or_exit ! ( matches, "level_sizes" , LevelSizes ) ;
193
+ Config {
194
+ db_url : String :: from ( db_url) ,
195
+ output_file,
196
+ room_id : String :: from ( room_id) ,
197
+ max_state_group,
198
+ min_saved_rows,
199
+ transactions,
200
+ level_sizes,
201
+ }
202
+ }
203
+ }
204
+
205
+ fn main ( ) {
206
+ let mut config = Config :: parse_arguments ( ) ;
182
207
183
208
// First we need to get the current state groups
184
- println ! ( "Fetching state from DB for room '{}'..." , room_id) ;
185
- let state_group_map = database:: get_data_from_db ( db_url, room_id, max_state_group) ;
209
+ println ! ( "Fetching state from DB for room '{}'..." , config. room_id) ;
210
+
211
+ let state_group_map =
212
+ database:: get_data_from_db ( & config. db_url , & config. room_id , config. max_state_group ) ;
186
213
187
214
println ! ( "Number of state groups: {}" , state_group_map. len( ) ) ;
188
215
@@ -196,7 +223,7 @@ fn main() {
196
223
197
224
println ! ( "Compressing state..." ) ;
198
225
199
- let compressor = Compressor :: compress ( & state_group_map, & level_sizes. 0 ) ;
226
+ let compressor = Compressor :: compress ( & state_group_map, & config . level_sizes . 0 ) ;
200
227
201
228
let new_state_group_map = compressor. new_state_group_map ;
202
229
@@ -228,7 +255,7 @@ fn main() {
228
255
compressor. stats. state_groups_changed
229
256
) ;
230
257
231
- if let Some ( min) = min_saved_rows {
258
+ if let Some ( min) = config . min_saved_rows {
232
259
let saving = ( original_summed_size - compressed_summed_size) as i32 ;
233
260
if saving < min {
234
261
println ! (
@@ -239,25 +266,39 @@ fn main() {
239
266
}
240
267
}
241
268
269
+ check_that_maps_match ( & state_group_map, & new_state_group_map) ;
270
+
242
271
// If we are given an output file, we output the changes as SQL. If the
243
272
// `transactions` argument is set we wrap each change to a state group in a
244
273
// transaction.
245
274
246
- if let Some ( output ) = & mut output_file {
247
- println ! ( "Writing changes..." ) ;
275
+ output_sql ( & mut config , & state_group_map , & new_state_group_map ) ;
276
+ }
248
277
249
- let pb = ProgressBar :: new ( state_group_map. len ( ) as u64 ) ;
250
- pb. set_style (
251
- ProgressStyle :: default_bar ( ) . template ( "[{elapsed_precise}] {bar} {pos}/{len} {msg}" ) ,
252
- ) ;
253
- pb. set_message ( "state groups" ) ;
254
- pb. enable_steady_tick ( 100 ) ;
278
+ fn output_sql (
279
+ config : & mut Config ,
280
+ old_map : & BTreeMap < i64 , StateGroupEntry > ,
281
+ new_map : & BTreeMap < i64 , StateGroupEntry > ,
282
+ ) {
283
+ if config. output_file . is_none ( ) {
284
+ return ;
285
+ }
286
+
287
+ println ! ( "Writing changes..." ) ;
288
+
289
+ let pb = ProgressBar :: new ( old_map. len ( ) as u64 ) ;
290
+ pb. set_style (
291
+ ProgressStyle :: default_bar ( ) . template ( "[{elapsed_precise}] {bar} {pos}/{len} {msg}" ) ,
292
+ ) ;
293
+ pb. set_message ( "state groups" ) ;
294
+ pb. enable_steady_tick ( 100 ) ;
255
295
256
- for ( sg, old_entry) in & state_group_map {
257
- let new_entry = & new_state_group_map[ sg] ;
296
+ if let Some ( output) = & mut config. output_file {
297
+ for ( sg, old_entry) in old_map {
298
+ let new_entry = & new_map[ sg] ;
258
299
259
300
if old_entry != new_entry {
260
- if transactions {
301
+ if config . transactions {
261
302
writeln ! ( output, "BEGIN;" ) . unwrap ( ) ;
262
303
}
263
304
@@ -292,7 +333,7 @@ fn main() {
292
333
output,
293
334
"({}, {}, {}, {}, {})" ,
294
335
sg,
295
- PGEscape ( room_id) ,
336
+ PGEscape ( & config . room_id) ,
296
337
PGEscape ( t) ,
297
338
PGEscape ( s) ,
298
339
PGEscape ( e)
@@ -302,21 +343,26 @@ fn main() {
302
343
writeln ! ( output, ";" ) . unwrap ( ) ;
303
344
}
304
345
305
- if transactions {
346
+ if config . transactions {
306
347
writeln ! ( output, "COMMIT;" ) . unwrap ( ) ;
307
348
}
308
349
writeln ! ( output) . unwrap ( ) ;
309
350
}
310
351
311
352
pb. inc ( 1 ) ;
312
353
}
313
-
314
- pb. finish ( ) ;
315
354
}
316
355
356
+ pb. finish ( ) ;
357
+ }
358
+
359
+ fn check_that_maps_match (
360
+ old_map : & BTreeMap < i64 , StateGroupEntry > ,
361
+ new_map : & BTreeMap < i64 , StateGroupEntry > ,
362
+ ) {
317
363
println ! ( "Checking that state maps match..." ) ;
318
364
319
- let pb = ProgressBar :: new ( state_group_map . len ( ) as u64 ) ;
365
+ let pb = ProgressBar :: new ( old_map . len ( ) as u64 ) ;
320
366
pb. set_style (
321
367
ProgressStyle :: default_bar ( ) . template ( "[{elapsed_precise}] {bar} {pos}/{len} {msg}" ) ,
322
368
) ;
@@ -325,11 +371,11 @@ fn main() {
325
371
326
372
// Now let's iterate through and assert that the state for each group
327
373
// matches between the two versions.
328
- state_group_map
374
+ old_map
329
375
. par_iter ( ) // This uses rayon to run the checks in parallel
330
376
. try_for_each ( |( sg, _) | {
331
- let expected = collapse_state_maps ( & state_group_map , * sg) ;
332
- let actual = collapse_state_maps ( & new_state_group_map , * sg) ;
377
+ let expected = collapse_state_maps ( & old_map , * sg) ;
378
+ let actual = collapse_state_maps ( & new_map , * sg) ;
333
379
334
380
pb. inc ( 1 ) ;
335
381
0 commit comments