@@ -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,6 +352,19 @@ pub fn cmd(opts: &Opts) -> exitcode::ExitCode {
329
352
}
330
353
}
331
354
355
+ fn write_config ( filepath : & PathBuf , body : & str ) -> Result < ( ) , crate :: Error > {
356
+ if filepath. exists ( ) {
357
+ // If the file exists, we don't want to overwrite, that's just rude.
358
+ return 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) . and_then ( |mut file| file. write ( body. as_bytes ( ) ) ) ?;
364
+ }
365
+ Ok ( ( ) )
366
+ }
367
+
332
368
#[ cfg( test) ]
333
369
mod tests {
334
370
use super :: * ;
@@ -339,23 +375,23 @@ mod tests {
339
375
340
376
for name in SourceDescription :: types ( ) {
341
377
let param = format ! ( "{}//" , name) ;
342
- let cfg = generate_example ( true , & param) . unwrap ( ) ;
378
+ let cfg = generate_example ( true , & param, & None ) . unwrap ( ) ;
343
379
if let Err ( error) = toml:: from_str :: < crate :: config:: ConfigBuilder > ( & cfg) {
344
380
errors. push ( ( param, error) ) ;
345
381
}
346
382
}
347
383
348
384
for name in TransformDescription :: types ( ) {
349
385
let param = format ! ( "/{}/" , name) ;
350
- let cfg = generate_example ( true , & param) . unwrap ( ) ;
386
+ let cfg = generate_example ( true , & param, & None ) . unwrap ( ) ;
351
387
if let Err ( error) = toml:: from_str :: < crate :: config:: ConfigBuilder > ( & cfg) {
352
388
errors. push ( ( param, error) ) ;
353
389
}
354
390
}
355
391
356
392
for name in SinkDescription :: types ( ) {
357
393
let param = format ! ( "//{}" , name) ;
358
- let cfg = generate_example ( true , & param) . unwrap ( ) ;
394
+ let cfg = generate_example ( true , & param, & None ) . unwrap ( ) ;
359
395
if let Err ( error) = toml:: from_str :: < crate :: config:: ConfigBuilder > ( & cfg) {
360
396
errors. push ( ( param, error) ) ;
361
397
}
@@ -371,7 +407,7 @@ mod tests {
371
407
#[ test]
372
408
fn generate_basic ( ) {
373
409
assert_eq ! (
374
- generate_example( true , "stdin/json_parser/console" ) ,
410
+ generate_example( true , "stdin/json_parser/console" , & None ) ,
375
411
Ok ( r#"data_dir = "/var/lib/vector/"
376
412
377
413
[sources.source0]
@@ -402,7 +438,7 @@ when_full = "block"
402
438
) ;
403
439
404
440
assert_eq ! (
405
- generate_example( true , "stdin|json_parser|console" ) ,
441
+ generate_example( true , "stdin|json_parser|console" , & None ) ,
406
442
Ok ( r#"data_dir = "/var/lib/vector/"
407
443
408
444
[sources.source0]
@@ -433,7 +469,7 @@ when_full = "block"
433
469
) ;
434
470
435
471
assert_eq ! (
436
- generate_example( true , "stdin//console" ) ,
472
+ generate_example( true , "stdin//console" , & None ) ,
437
473
Ok ( r#"data_dir = "/var/lib/vector/"
438
474
439
475
[sources.source0]
@@ -458,7 +494,7 @@ when_full = "block"
458
494
) ;
459
495
460
496
assert_eq ! (
461
- generate_example( true , "//console" ) ,
497
+ generate_example( true , "//console" , & None ) ,
462
498
Ok ( r#"data_dir = "/var/lib/vector/"
463
499
464
500
[sinks.sink0]
@@ -479,7 +515,7 @@ when_full = "block"
479
515
) ;
480
516
481
517
assert_eq ! (
482
- generate_example( true , "/add_fields,json_parser,remove_fields" ) ,
518
+ generate_example( true , "/add_fields,json_parser,remove_fields" , & None ) ,
483
519
Ok ( r#"data_dir = "/var/lib/vector/"
484
520
485
521
[transforms.transform0]
@@ -504,7 +540,7 @@ type = "remove_fields"
504
540
) ;
505
541
506
542
assert_eq ! (
507
- generate_example( false , "/add_fields,json_parser,remove_fields" ) ,
543
+ generate_example( false , "/add_fields,json_parser,remove_fields" , & None ) ,
508
544
Ok ( r#"
509
545
[transforms.transform0]
510
546
inputs = []
0 commit comments