Skip to main content

openapi_nexus_config/
cli.rs

1//! Command-line argument definitions
2
3use std::collections::HashMap;
4use std::str::FromStr;
5
6use clap::Parser;
7
8use crate::errors::ConfigError;
9use crate::global_config::GlobalConfig;
10use openapi_nexus_common::GeneratorType;
11
12/// Command-line arguments with environment variable support
13#[derive(Debug, Parser)]
14#[command(name = "openapi-nexus")]
15#[command(about = "Generate code from OpenAPI 3.1 specifications")]
16#[command(version)]
17pub struct CliArgs {
18    #[command(subcommand)]
19    pub command: Commands,
20}
21
22#[derive(Debug, Parser)]
23pub enum Commands {
24    /// Generate code from an OpenAPI specification
25    Generate {
26        /// Path to the OpenAPI specification file
27        #[arg(short, long, env = "OPENAPI_NEXUS_INPUT")]
28        input: String,
29
30        /// Verbose output
31        #[arg(short, long, env = "OPENAPI_NEXUS_VERBOSE")]
32        verbose: bool,
33
34        /// Path to configuration file (overrides auto-discovery)
35        #[arg(long, env = "OPENAPI_NEXUS_CONFIG")]
36        config: Option<String>,
37
38        /// Global configuration options
39        #[command(flatten)]
40        global: GlobalConfig,
41
42        /// Override generator-specific config values
43        /// Format: <generator>.<key>=<value>
44        /// Example: --generator-config typescript-fetch.file_naming_convention=PascalCase
45        #[arg(long = "generator-config", value_name = "GENERATOR.KEY=VALUE")]
46        generator_config: Vec<String>,
47    },
48}
49
50impl Commands {
51    /// Parse generator config overrides from CLI arguments
52    /// Returns a map from Generator to TOML table values
53    pub fn parse_generator_overrides(
54        &self,
55    ) -> Result<HashMap<GeneratorType, toml::value::Table>, ConfigError> {
56        let generator_configs = match self {
57            Commands::Generate {
58                generator_config, ..
59            } => generator_config,
60        };
61
62        let mut overrides: HashMap<GeneratorType, toml::value::Table> = HashMap::new();
63
64        for config_str in generator_configs {
65            // Parse format: generator.key=value
66            let parts: Vec<&str> = config_str.splitn(2, '=').collect();
67            if parts.len() != 2 {
68                return Err(ConfigError::ParseOverrides(format!(
69                    "Invalid generator config format: '{}'. Expected format: <generator>.<key>=<value>",
70                    config_str
71                )));
72            }
73
74            let key_part = parts[0];
75            let value_str = parts[1];
76
77            // Split generator and key
78            let key_parts: Vec<&str> = key_part.splitn(2, '.').collect();
79            if key_parts.len() != 2 {
80                return Err(ConfigError::ParseOverrides(format!(
81                    "Invalid generator config format: '{}'. Expected format: <generator>.<key>=<value>",
82                    config_str
83                )));
84            }
85
86            let generator_str = key_parts[0];
87            let key = key_parts[1].to_string();
88
89            // Parse generator
90            let generator = GeneratorType::from_str(generator_str).map_err(|e| {
91                ConfigError::ParseOverrides(format!(
92                    "Invalid generator name '{}': {}",
93                    generator_str, e
94                ))
95            })?;
96
97            // Parse value as TOML value
98            let toml_value = Self::parse_toml_value(value_str);
99
100            // Add to overrides
101            overrides
102                .entry(generator)
103                .or_default()
104                .insert(key, toml_value);
105        }
106
107        Ok(overrides)
108    }
109
110    /// Parse a string value into an appropriate TOML value
111    fn parse_toml_value(value: &str) -> toml::Value {
112        // Try to parse as boolean
113        if let Ok(b) = value.parse::<bool>() {
114            return toml::Value::Boolean(b);
115        }
116        // Try to parse as integer
117        if let Ok(i) = value.parse::<i64>() {
118            return toml::Value::Integer(i);
119        }
120        // Try to parse as float
121        if let Ok(f) = value.parse::<f64>() {
122            return toml::Value::Float(f);
123        }
124        // Default to string
125        toml::Value::String(value.to_string())
126    }
127}