1use clap::Parser;
2use serde::Deserialize;
3use std::path::PathBuf;
4
5#[derive(Debug, Deserialize, Parser, Default, Clone)]
6#[serde(default)]
7#[command(author, version, about, long_about = None)]
8pub struct Config {
9 #[arg(short = 'i', long = "input")]
11 pub input: Option<Vec<PathBuf>>,
12
13 #[arg(long = "include")]
15 pub include: Option<Vec<PathBuf>>,
16
17 #[arg(short = 'o', long = "output")]
19 pub output: Option<PathBuf>,
20
21 #[arg(long = "config")]
23 #[serde(skip)]
24 pub config_file: Option<PathBuf>,
25}
26
27#[derive(Deserialize)]
28struct CargoConfig {
29 package: Option<CargoPackage>,
30}
31
32#[derive(Deserialize)]
33struct CargoPackage {
34 metadata: Option<CargoMetadata>,
35}
36
37#[derive(Deserialize)]
38struct CargoMetadata {
39 #[serde(rename = "oas-forge")]
40 oas_forge: Option<Config>,
41}
42
43impl Config {
44 pub fn load() -> Self {
50 let cli_args = Config::parse();
51
52 let mut final_config = Config::default();
54
55 if let Ok(cargo_conf) = load_cargo_toml() {
57 final_config.merge(cargo_conf);
58 }
59
60 if let Ok(toml_conf) = load_toml_file("openapi.toml") {
62 final_config.merge(toml_conf);
63 }
64
65 if let Some(path) = &cli_args.config_file {
67 if let Ok(file_conf) = load_toml_file(path) {
68 final_config.merge(file_conf);
69 }
70 }
71
72 final_config.merge(cli_args);
74
75 final_config
76 }
77
78 fn merge(&mut self, other: Config) {
79 if let Some(input) = other.input {
80 self.input = Some(input);
81 }
82 if let Some(include) = other.include {
83 self.include = Some(include);
84 }
85 if let Some(output) = other.output {
86 self.output = Some(output);
87 }
88 }
89}
90
91fn load_cargo_toml() -> Result<Config, Box<dyn std::error::Error>> {
92 let content = std::fs::read_to_string("Cargo.toml")?;
93 let config: CargoConfig = toml::from_str(&content)?;
94 Ok(config
95 .package
96 .and_then(|p| p.metadata)
97 .and_then(|m| m.oas_forge)
98 .unwrap_or_default())
99}
100
101fn load_toml_file<P: AsRef<std::path::Path>>(
102 path: P,
103) -> Result<Config, Box<dyn std::error::Error>> {
104 let content = std::fs::read_to_string(path)?;
105 let config: Config = toml::from_str(&content)?;
106 Ok(config)
107}