Skip to content

Commit f65d3e0

Browse files
committed
enhancement(cli): Adds optional file output to generator
Signed-off-by: Ian Henry <[email protected]>
1 parent 32162bb commit f65d3e0

File tree

1 file changed

+49
-12
lines changed

1 file changed

+49
-12
lines changed

src/generate.rs

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,12 @@ use crate::config::{
55
use colored::*;
66
use indexmap::IndexMap;
77
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+
};
914
use structopt::StructOpt;
1015
use toml::Value;
1116

@@ -46,6 +51,10 @@ pub struct Opts {
4651
/// is then up to you to restructure the `inputs` of each component to build
4752
/// the topology you need.
4853
expression: String,
54+
55+
/// Generate config as a file
56+
#[structopt(long, parse(from_os_str))]
57+
file: Option<PathBuf>,
4958
}
5059

5160
#[derive(Serialize)]
@@ -71,7 +80,11 @@ pub struct Config {
7180
pub sinks: Option<IndexMap<String, SinkOuter>>,
7281
}
7382

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>> {
7588
let components: Vec<Vec<_>> = expression
7689
.split(|c| c == '|' || c == '/')
7790
.map(|s| {
@@ -309,6 +322,16 @@ fn generate_example(include_globals: bool, expression: &str) -> Result<String, V
309322
}
310323
}
311324

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+
312335
if !errs.is_empty() {
313336
Err(errs)
314337
} else {
@@ -317,7 +340,7 @@ fn generate_example(include_globals: bool, expression: &str) -> Result<String, V
317340
}
318341

319342
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) {
321344
Ok(s) => {
322345
println!("{}", s);
323346
exitcode::OK
@@ -329,6 +352,20 @@ pub fn cmd(opts: &Opts) -> exitcode::ExitCode {
329352
}
330353
}
331354

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+
332369
#[cfg(test)]
333370
mod tests {
334371
use super::*;
@@ -339,23 +376,23 @@ mod tests {
339376

340377
for name in SourceDescription::types() {
341378
let param = format!("{}//", name);
342-
let cfg = generate_example(true, &param).unwrap();
379+
let cfg = generate_example(true, &param, &None).unwrap();
343380
if let Err(error) = toml::from_str::<crate::config::ConfigBuilder>(&cfg) {
344381
errors.push((param, error));
345382
}
346383
}
347384

348385
for name in TransformDescription::types() {
349386
let param = format!("/{}/", name);
350-
let cfg = generate_example(true, &param).unwrap();
387+
let cfg = generate_example(true, &param, &None).unwrap();
351388
if let Err(error) = toml::from_str::<crate::config::ConfigBuilder>(&cfg) {
352389
errors.push((param, error));
353390
}
354391
}
355392

356393
for name in SinkDescription::types() {
357394
let param = format!("//{}", name);
358-
let cfg = generate_example(true, &param).unwrap();
395+
let cfg = generate_example(true, &param, &None).unwrap();
359396
if let Err(error) = toml::from_str::<crate::config::ConfigBuilder>(&cfg) {
360397
errors.push((param, error));
361398
}
@@ -371,7 +408,7 @@ mod tests {
371408
#[test]
372409
fn generate_basic() {
373410
assert_eq!(
374-
generate_example(true, "stdin/json_parser/console"),
411+
generate_example(true, "stdin/json_parser/console", &None),
375412
Ok(r#"data_dir = "/var/lib/vector/"
376413
377414
[sources.source0]
@@ -402,7 +439,7 @@ when_full = "block"
402439
);
403440

404441
assert_eq!(
405-
generate_example(true, "stdin|json_parser|console"),
442+
generate_example(true, "stdin|json_parser|console", &None),
406443
Ok(r#"data_dir = "/var/lib/vector/"
407444
408445
[sources.source0]
@@ -433,7 +470,7 @@ when_full = "block"
433470
);
434471

435472
assert_eq!(
436-
generate_example(true, "stdin//console"),
473+
generate_example(true, "stdin//console", &None),
437474
Ok(r#"data_dir = "/var/lib/vector/"
438475
439476
[sources.source0]
@@ -458,7 +495,7 @@ when_full = "block"
458495
);
459496

460497
assert_eq!(
461-
generate_example(true, "//console"),
498+
generate_example(true, "//console", &None),
462499
Ok(r#"data_dir = "/var/lib/vector/"
463500
464501
[sinks.sink0]
@@ -479,7 +516,7 @@ when_full = "block"
479516
);
480517

481518
assert_eq!(
482-
generate_example(true, "/add_fields,json_parser,remove_fields"),
519+
generate_example(true, "/add_fields,json_parser,remove_fields", &None),
483520
Ok(r#"data_dir = "/var/lib/vector/"
484521
485522
[transforms.transform0]
@@ -504,7 +541,7 @@ type = "remove_fields"
504541
);
505542

506543
assert_eq!(
507-
generate_example(false, "/add_fields,json_parser,remove_fields"),
544+
generate_example(false, "/add_fields,json_parser,remove_fields", &None),
508545
Ok(r#"
509546
[transforms.transform0]
510547
inputs = []

0 commit comments

Comments
 (0)