codegenr_lib/
opt.rs

1use crate::Options;
2use serde_json::Value;
3use std::{collections::HashMap, fs::read_to_string};
4use structopt::StructOpt;
5
6pub const CODEGENR_CONFIG_FILE: &str = "codegenr.toml";
7
8//https://docs.rs/structopt/latest/structopt/#specifying-argument-types
9#[derive(StructOpt, Debug)]
10#[structopt(name = "codegenr")]
11pub struct Opt {
12  // todo later
13  // #[structopt(
14  //   long,
15  //   short,
16  //   help = "If set, codegenr keeps running and watch for all concerned files change. And then re-run the generation.",
17  //   global = true
18  // )]
19  // pub watch: bool,
20  #[structopt(subcommand)]
21  pub cmd: Option<Command>,
22}
23
24#[derive(StructOpt, Debug)]
25#[structopt(about = "Commands")]
26pub enum Command {
27  #[structopt(name = "file", help = "Executes all generations from a config file.")]
28  FromFile {
29    #[structopt(
30        long,
31        help = "Path to a full codegenr configuration file. If existing, all other command line parameters are ignored",
32        default_value = CODEGENR_CONFIG_FILE
33      )]
34    file: String,
35  },
36  #[structopt(name = "gen", help = "Executes one generation from command line parameters.")]
37  FromLine {
38    #[structopt(long, short, help = "Source json/yaml/openapi file.")]
39    source: String,
40    #[structopt(long, short, help = "Output folder.")]
41    output: String,
42    #[structopt(
43      long,
44      short,
45      help = "Templates folder(s), in which only one .hbs file should have no `_` as prefix (Underscored templates are partial templates)."
46    )]
47    templates: Vec<String>,
48    #[structopt(
49      long,
50      short,
51      help = "Optional path to a file where the intermediate json representation of resolved source(s) will be output. 
52        The resolved json will be output as <file>.resolved.json, the full text rendered result will be output as <file>.rendered.txt."
53    )]
54    intermediate: Option<String>,
55    #[structopt(long, short, help = "Path to custom helper files.")]
56    custom_helpers: Vec<String>,
57    #[structopt(
58        long,
59        short,
60        help = "Global parameters values formatted `key=value`. Values will be parsed as json or strings if the json parsing fails.",
61        parse(try_from_str = parse_key_val)
62      )]
63    global_parameters: Vec<(String, serde_json::Value)>,
64  },
65}
66
67impl Default for Command {
68  fn default() -> Self {
69    Self::FromFile {
70      file: CODEGENR_CONFIG_FILE.into(),
71    }
72  }
73}
74
75// From here: https://github.com/TeXitoi/structopt/blob/master/examples/keyvalue.rs
76// Parse a single key-value pair
77fn parse_key_val(s: &str) -> Result<(String, Value), anyhow::Error> {
78  let pos = s
79    .find('=')
80    .ok_or_else(|| anyhow::anyhow!("Invalid key=value: no `=` found in `{}`.", s))?;
81  let value = &s[pos + 1..];
82  Ok((
83    s[..pos].parse()?,
84    value.parse().unwrap_or_else(|_| Value::String(value.to_string())),
85  ))
86}
87
88impl TryFrom<Command> for HashMap<String, Options> {
89  type Error = anyhow::Error;
90
91  fn try_from(cmd: Command) -> Result<Self, Self::Error> {
92    match cmd {
93      Command::FromFile { file } => {
94        let config = read_to_string(&file).map_err(|e| {
95          anyhow::anyhow!(
96            "Unable to read `{}` file: `{}`. Did you run codegenr in the right directory ?",
97            file,
98            e
99          )
100        })?;
101        let opts: HashMap<String, Options> =
102          toml::from_str(&config).map_err(|e| anyhow::anyhow!("Unable to deserialize `{}` config file: `{}`.", file, e))?;
103        Ok(opts)
104      }
105      Command::FromLine {
106        source,
107        output,
108        templates,
109        intermediate,
110        custom_helpers,
111        global_parameters,
112      } => {
113        let options = Options {
114          source,
115          output,
116          templates,
117          intermediate,
118          custom_helpers,
119          global_parameters: global_parameters.into_iter().collect(),
120        };
121        let map = HashMap::<String, Options>::from_iter(std::iter::once(("command_line".into(), options)));
122        Ok(map)
123      }
124    }
125  }
126}