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", alias = "out")]
19 pub output: Option<Vec<PathBuf>>,
20
21 #[arg(long = "output-schemas")]
23 pub output_schemas: Option<Vec<PathBuf>>,
24
25 #[arg(long = "output-paths")]
27 pub output_paths: Option<Vec<PathBuf>>,
28
29 #[arg(long = "output-fragments")]
31 pub output_fragments: Option<Vec<PathBuf>>,
32
33 #[arg(long = "config")]
35 #[serde(skip)]
36 pub config_file: Option<PathBuf>,
37}
38
39#[derive(Deserialize)]
40struct CargoConfig {
41 package: Option<CargoPackage>,
42}
43
44#[derive(Deserialize)]
45struct CargoPackage {
46 metadata: Option<CargoMetadata>,
47}
48
49#[derive(Deserialize)]
50struct CargoMetadata {
51 #[serde(rename = "oas-forge")]
52 oas_forge: Option<Config>,
53}
54
55impl Config {
56 pub fn load() -> Self {
62 let cli_args = Config::parse();
63
64 let mut final_config = Config::default();
66
67 if let Ok(cargo_conf) = load_cargo_toml() {
69 final_config.merge(cargo_conf);
70 }
71
72 if let Ok(toml_conf) = load_toml_file("openapi.toml") {
74 final_config.merge(toml_conf);
75 }
76
77 if let Some(path) = &cli_args.config_file {
79 if let Ok(file_conf) = load_toml_file(path) {
80 final_config.merge(file_conf);
81 }
82 }
83
84 final_config.merge(cli_args);
86
87 final_config
88 }
89
90 fn merge(&mut self, other: Config) {
91 if let Some(input) = other.input {
92 self.input = Some(input);
93 }
94 if let Some(include) = other.include {
95 self.include = Some(include);
96 }
97 if let Some(output) = other.output {
98 self.output = Some(output);
99 }
100 if let Some(output_schemas) = other.output_schemas {
101 self.output_schemas = Some(output_schemas);
102 }
103 if let Some(output_paths) = other.output_paths {
104 self.output_paths = Some(output_paths);
105 }
106 if let Some(output_fragments) = other.output_fragments {
107 self.output_fragments = Some(output_fragments);
108 }
109 }
110}
111
112fn load_cargo_toml() -> Result<Config, Box<dyn std::error::Error>> {
113 let content = std::fs::read_to_string("Cargo.toml")?;
114 let config: CargoConfig = toml::from_str(&content)?;
115 Ok(config
116 .package
117 .and_then(|p| p.metadata)
118 .and_then(|m| m.oas_forge)
119 .unwrap_or_default())
120}
121
122fn load_toml_file<P: AsRef<std::path::Path>>(
123 path: P,
124) -> Result<Config, Box<dyn std::error::Error>> {
125 let content = std::fs::read_to_string(path)?;
126 let config: Config = toml::from_str(&content)?;
127 Ok(config)
128}