@@ -5,7 +5,12 @@ use crate::config::{
5
5
use colored:: * ;
6
6
use indexmap:: IndexMap ;
7
7
use serde:: Serialize ;
8
- use std:: collections:: BTreeMap ;
8
+ use std:: {
9
+ collections:: BTreeMap ,
10
+ fs:: { create_dir_all, File } ,
11
+ io:: Write ,
12
+ path:: PathBuf ,
13
+ } ;
9
14
use structopt:: StructOpt ;
10
15
use toml:: Value ;
11
16
@@ -46,6 +51,10 @@ pub struct Opts {
46
51
/// is then up to you to restructure the `inputs` of each component to build
47
52
/// the topology you need.
48
53
expression : String ,
54
+
55
+ /// Generate config as a file
56
+ #[ structopt( long, parse( from_os_str) ) ]
57
+ file : Option < PathBuf > ,
49
58
}
50
59
51
60
#[ derive( Serialize ) ]
@@ -71,7 +80,11 @@ pub struct Config {
71
80
pub sinks : Option < IndexMap < String , SinkOuter > > ,
72
81
}
73
82
74
- fn generate_example ( include_globals : bool , expression : & str ) -> Result < String , Vec < String > > {
83
+ fn generate_example (
84
+ include_globals : bool ,
85
+ expression : & str ,
86
+ file : & Option < PathBuf > ,
87
+ ) -> Result < String , Vec < String > > {
75
88
let components: Vec < Vec < _ > > = expression
76
89
. split ( |c| c == '|' || c == '/' )
77
90
. map ( |s| {
@@ -309,6 +322,16 @@ fn generate_example(include_globals: bool, expression: &str) -> Result<String, V
309
322
}
310
323
}
311
324
325
+ if file. is_some ( ) {
326
+ match write_config ( file. as_ref ( ) . unwrap ( ) , & builder) {
327
+ Ok ( _) => println ! (
328
+ "Config file written to {:?}" ,
329
+ & file. as_ref( ) . unwrap( ) . join( "\n " )
330
+ ) ,
331
+ Err ( e) => errs. push ( format ! ( "failed to write to file: {}" , e) ) ,
332
+ } ;
333
+ } ;
334
+
312
335
if !errs. is_empty ( ) {
313
336
Err ( errs)
314
337
} else {
@@ -317,7 +340,7 @@ fn generate_example(include_globals: bool, expression: &str) -> Result<String, V
317
340
}
318
341
319
342
pub fn cmd ( opts : & Opts ) -> exitcode:: ExitCode {
320
- match generate_example ( !opts. fragment , & opts. expression ) {
343
+ match generate_example ( !opts. fragment , & opts. expression , & opts . file ) {
321
344
Ok ( s) => {
322
345
println ! ( "{}" , s) ;
323
346
exitcode:: OK
@@ -329,33 +352,51 @@ pub fn cmd(opts: &Opts) -> exitcode::ExitCode {
329
352
}
330
353
}
331
354
355
+ fn write_config ( filepath : & PathBuf , body : & str ) -> Result < usize , crate :: Error > {
356
+ if filepath. exists ( ) {
357
+ // If the file exists, we don't want to overwrite, that's just rude.
358
+ Err ( format ! ( "{:?} already exists" , & filepath) . into ( ) )
359
+ } else {
360
+ if let Some ( directory) = filepath. parent ( ) {
361
+ create_dir_all ( directory) ?;
362
+ }
363
+ File :: create ( filepath)
364
+ . and_then ( |mut file| file. write ( body. as_bytes ( ) ) )
365
+ . map_err ( Into :: into)
366
+ }
367
+ }
368
+
332
369
#[ cfg( test) ]
333
370
mod tests {
334
371
use super :: * ;
372
+ use std:: fs;
373
+ use std:: path:: PathBuf ;
374
+
375
+ use tempfile:: tempdir;
335
376
336
377
#[ test]
337
378
fn generate_all ( ) {
338
379
let mut errors = Vec :: new ( ) ;
339
380
340
381
for name in SourceDescription :: types ( ) {
341
382
let param = format ! ( "{}//" , name) ;
342
- let cfg = generate_example ( true , & param) . unwrap ( ) ;
383
+ let cfg = generate_example ( true , & param, & None ) . unwrap ( ) ;
343
384
if let Err ( error) = toml:: from_str :: < crate :: config:: ConfigBuilder > ( & cfg) {
344
385
errors. push ( ( param, error) ) ;
345
386
}
346
387
}
347
388
348
389
for name in TransformDescription :: types ( ) {
349
390
let param = format ! ( "/{}/" , name) ;
350
- let cfg = generate_example ( true , & param) . unwrap ( ) ;
391
+ let cfg = generate_example ( true , & param, & None ) . unwrap ( ) ;
351
392
if let Err ( error) = toml:: from_str :: < crate :: config:: ConfigBuilder > ( & cfg) {
352
393
errors. push ( ( param, error) ) ;
353
394
}
354
395
}
355
396
356
397
for name in SinkDescription :: types ( ) {
357
398
let param = format ! ( "//{}" , name) ;
358
- let cfg = generate_example ( true , & param) . unwrap ( ) ;
399
+ let cfg = generate_example ( true , & param, & None ) . unwrap ( ) ;
359
400
if let Err ( error) = toml:: from_str :: < crate :: config:: ConfigBuilder > ( & cfg) {
360
401
errors. push ( ( param, error) ) ;
361
402
}
@@ -367,11 +408,28 @@ mod tests {
367
408
assert ! ( errors. is_empty( ) ) ;
368
409
}
369
410
411
+ #[ test]
412
+ fn generate_configfile ( ) {
413
+ let tempdir = tempdir ( ) . expect ( "Unable to create tempdir for config" ) ;
414
+ let filepath = tempdir. path ( ) . join ( "./config.example.toml" ) ;
415
+ let cfg = generate_example ( true , "stdin/json_parser/console" , & Some ( filepath. clone ( ) ) ) ;
416
+ let filecontents = fs:: read_to_string (
417
+ fs:: canonicalize ( & filepath) . expect ( "Could not return canonicalized filepath" ) ,
418
+ )
419
+ . expect ( "Could not read config file" ) ;
420
+ cleanup_configfile ( & filepath) ;
421
+ assert_eq ! ( cfg. unwrap( ) , filecontents)
422
+ }
423
+
424
+ fn cleanup_configfile ( filepath : & PathBuf ) {
425
+ fs:: remove_file ( filepath) . expect ( "Could not cleanup config file!" ) ;
426
+ }
427
+
370
428
#[ cfg( all( feature = "transforms-json_parser" , feature = "sinks-console" ) ) ]
371
429
#[ test]
372
430
fn generate_basic ( ) {
373
431
assert_eq ! (
374
- generate_example( true , "stdin/json_parser/console" ) ,
432
+ generate_example( true , "stdin/json_parser/console" , & None ) ,
375
433
Ok ( r#"data_dir = "/var/lib/vector/"
376
434
377
435
[sources.source0]
@@ -402,7 +460,7 @@ when_full = "block"
402
460
) ;
403
461
404
462
assert_eq ! (
405
- generate_example( true , "stdin|json_parser|console" ) ,
463
+ generate_example( true , "stdin|json_parser|console" , & None ) ,
406
464
Ok ( r#"data_dir = "/var/lib/vector/"
407
465
408
466
[sources.source0]
@@ -433,7 +491,7 @@ when_full = "block"
433
491
) ;
434
492
435
493
assert_eq ! (
436
- generate_example( true , "stdin//console" ) ,
494
+ generate_example( true , "stdin//console" , & None ) ,
437
495
Ok ( r#"data_dir = "/var/lib/vector/"
438
496
439
497
[sources.source0]
@@ -458,7 +516,7 @@ when_full = "block"
458
516
) ;
459
517
460
518
assert_eq ! (
461
- generate_example( true , "//console" ) ,
519
+ generate_example( true , "//console" , & None ) ,
462
520
Ok ( r#"data_dir = "/var/lib/vector/"
463
521
464
522
[sinks.sink0]
@@ -479,7 +537,7 @@ when_full = "block"
479
537
) ;
480
538
481
539
assert_eq ! (
482
- generate_example( true , "/add_fields,json_parser,remove_fields" ) ,
540
+ generate_example( true , "/add_fields,json_parser,remove_fields" , & None ) ,
483
541
Ok ( r#"data_dir = "/var/lib/vector/"
484
542
485
543
[transforms.transform0]
@@ -504,7 +562,7 @@ type = "remove_fields"
504
562
) ;
505
563
506
564
assert_eq ! (
507
- generate_example( false , "/add_fields,json_parser,remove_fields" ) ,
565
+ generate_example( false , "/add_fields,json_parser,remove_fields" , & None ) ,
508
566
Ok ( r#"
509
567
[transforms.transform0]
510
568
inputs = []
0 commit comments