1#[cfg(feature = "cli")]
2use clap::Parser;
3use serde::Deserialize;
4use std::path::PathBuf;
5
6#[derive(Debug, Deserialize, Default, Clone)]
7#[cfg_attr(feature = "cli", derive(Parser))]
8#[serde(default)]
9#[cfg_attr(feature = "cli", command(author, version, about, long_about = None))]
10pub struct Config {
11 #[cfg_attr(feature = "cli", arg(short = 'i', long = "input"))]
13 pub input: Option<Vec<PathBuf>>,
14
15 #[cfg_attr(feature = "cli", arg(long = "include"))]
17 pub include: Option<Vec<PathBuf>>,
18
19 #[cfg_attr(feature = "cli", arg(short = 'o', long = "output", alias = "out"))]
21 pub output: Option<Vec<PathBuf>>,
22
23 #[cfg_attr(feature = "cli", arg(long = "output-schemas"))]
25 pub output_schemas: Option<Vec<PathBuf>>,
26
27 #[cfg_attr(feature = "cli", arg(long = "output-paths"))]
29 pub output_paths: Option<Vec<PathBuf>>,
30
31 #[cfg_attr(feature = "cli", arg(long = "output-fragments"))]
33 pub output_fragments: Option<Vec<PathBuf>>,
34
35 #[cfg_attr(feature = "cli", arg(long = "config"))]
37 #[serde(skip)]
38 pub config_file: Option<PathBuf>,
39}
40
41#[derive(Deserialize)]
42struct CargoConfig {
43 package: Option<CargoPackage>,
44}
45
46#[derive(Deserialize)]
47struct CargoPackage {
48 metadata: Option<CargoMetadata>,
49}
50
51#[derive(Deserialize)]
52struct CargoMetadata {
53 #[serde(rename = "oas-forge")]
54 oas_forge: Option<Config>,
55}
56
57impl Config {
58 #[cfg(feature = "cli")]
64 pub fn load() -> Self {
65 let cli_args = Config::parse();
66
67 let mut final_config = Config::default();
69
70 if let Ok(cargo_conf) = load_cargo_toml() {
72 final_config.merge(cargo_conf);
73 }
74
75 if let Ok(toml_conf) = load_toml_file("openapi.toml") {
77 final_config.merge(toml_conf);
78 }
79
80 if let Some(path) = &cli_args.config_file {
82 if let Ok(file_conf) = load_toml_file(path) {
83 final_config.merge(file_conf);
84 }
85 }
86
87 final_config.merge(cli_args);
89
90 final_config
91 }
92
93 fn merge(&mut self, other: Config) {
94 if let Some(input) = other.input {
95 self.input = Some(input);
96 }
97 if let Some(include) = other.include {
98 self.include = Some(include);
99 }
100 if let Some(output) = other.output {
101 self.output = Some(output);
102 }
103 if let Some(output_schemas) = other.output_schemas {
104 self.output_schemas = Some(output_schemas);
105 }
106 if let Some(output_paths) = other.output_paths {
107 self.output_paths = Some(output_paths);
108 }
109 if let Some(output_fragments) = other.output_fragments {
110 self.output_fragments = Some(output_fragments);
111 }
112 }
113}
114
115fn load_cargo_toml() -> Result<Config, Box<dyn std::error::Error>> {
116 let content = std::fs::read_to_string("Cargo.toml")?;
117 let config: CargoConfig = toml::from_str(&content)?;
118 Ok(config
119 .package
120 .and_then(|p| p.metadata)
121 .and_then(|m| m.oas_forge)
122 .unwrap_or_default())
123}
124
125fn load_toml_file<P: AsRef<std::path::Path>>(
126 path: P,
127) -> Result<Config, Box<dyn std::error::Error>> {
128 let content = std::fs::read_to_string(path)?;
129 let config: Config = toml::from_str(&content)?;
130 Ok(config)
131}