tonic_rest_openapi/
config.rs1use std::path::Path;
42
43use serde::Deserialize;
44
45#[derive(Debug, Deserialize)]
50#[serde(default)]
51pub struct ProjectConfig {
52 pub error_schema_ref: String,
54
55 pub unimplemented_methods: Vec<String>,
57
58 pub public_methods: Vec<String>,
60
61 pub plain_text_endpoints: Vec<PlainTextEndpoint>,
63
64 pub metrics_path: Option<String>,
66
67 pub readiness_path: Option<String>,
69
70 pub transforms: TransformConfig,
72}
73
74#[derive(Debug, Clone, Deserialize)]
76pub struct PlainTextEndpoint {
77 pub path: String,
79 pub example: Option<String>,
81}
82
83#[derive(Debug, Deserialize)]
85#[serde(default)]
86#[allow(clippy::struct_excessive_bools)]
87pub struct TransformConfig {
88 pub upgrade_to_3_1: bool,
90
91 pub annotate_sse: bool,
93
94 pub inject_validation: bool,
96
97 pub add_security: bool,
99
100 pub inline_request_bodies: bool,
102
103 pub flatten_uuid_refs: bool,
105
106 pub normalize_line_endings: bool,
108}
109
110impl Default for ProjectConfig {
111 fn default() -> Self {
112 Self {
113 error_schema_ref: crate::DEFAULT_ERROR_SCHEMA_REF.to_string(),
114 unimplemented_methods: Vec::new(),
115 public_methods: Vec::new(),
116 plain_text_endpoints: Vec::new(),
117 metrics_path: None,
118 readiness_path: None,
119 transforms: TransformConfig::default(),
120 }
121 }
122}
123
124impl Default for TransformConfig {
125 fn default() -> Self {
126 Self {
127 upgrade_to_3_1: true,
128 annotate_sse: true,
129 inject_validation: true,
130 add_security: true,
131 inline_request_bodies: true,
132 flatten_uuid_refs: true,
133 normalize_line_endings: true,
134 }
135 }
136}
137
138impl ProjectConfig {
139 pub fn load(path: &Path) -> crate::error::Result<Self> {
145 let content = std::fs::read_to_string(path)?;
146 let config: Self = serde_yaml_ng::from_str(&content)?;
147 Ok(config)
148 }
149}
150
151#[cfg(test)]
152mod tests {
153 use super::*;
154
155 #[test]
156 fn deserialize_defaults() {
157 let config: ProjectConfig = serde_yaml_ng::from_str("{}").unwrap();
158 assert!(config.unimplemented_methods.is_empty());
159 assert!(config.public_methods.is_empty());
160 assert!(config.plain_text_endpoints.is_empty());
161 assert!(config.metrics_path.is_none());
162 assert!(config.readiness_path.is_none());
163 assert!(config.transforms.upgrade_to_3_1);
164 assert!(config.transforms.annotate_sse);
165 }
166
167 #[test]
168 fn deserialize_full() {
169 let yaml = r##"
170error_schema_ref: "#/components/schemas/MyError"
171unimplemented_methods:
172 - SetupMfa
173 - DisableMfa
174public_methods:
175 - Authenticate
176plain_text_endpoints:
177 - path: /health/live
178 example: "OK"
179 - path: /metrics
180metrics_path: /metrics
181readiness_path: /health/ready
182transforms:
183 add_security: false
184"##;
185 let config: ProjectConfig = serde_yaml_ng::from_str(yaml).unwrap();
186 assert_eq!(config.error_schema_ref, "#/components/schemas/MyError");
187 assert_eq!(config.unimplemented_methods, vec!["SetupMfa", "DisableMfa"]);
188 assert_eq!(config.public_methods, vec!["Authenticate"]);
189 assert_eq!(config.plain_text_endpoints.len(), 2);
190 assert_eq!(config.plain_text_endpoints[0].path, "/health/live");
191 assert_eq!(
192 config.plain_text_endpoints[0].example.as_deref(),
193 Some("OK")
194 );
195 assert!(config.plain_text_endpoints[1].example.is_none());
196 assert_eq!(config.metrics_path.as_deref(), Some("/metrics"));
197 assert_eq!(config.readiness_path.as_deref(), Some("/health/ready"));
198 assert!(!config.transforms.add_security);
199 assert!(config.transforms.upgrade_to_3_1);
201 assert!(config.transforms.inline_request_bodies);
202 }
203
204 #[test]
205 fn load_from_file() {
206 let dir = std::env::temp_dir().join("tonic-rest-openapi-test");
207 std::fs::create_dir_all(&dir).unwrap();
208 let path = dir.join("test-config.yaml");
209 std::fs::write(
210 &path,
211 "public_methods:\n - Login\nmetrics_path: /metrics\n",
212 )
213 .unwrap();
214
215 let config = ProjectConfig::load(&path).unwrap();
216 assert_eq!(config.public_methods, vec!["Login"]);
217 assert_eq!(config.metrics_path.as_deref(), Some("/metrics"));
218 assert!(config.transforms.upgrade_to_3_1);
220
221 std::fs::remove_dir_all(&dir).ok();
222 }
223
224 #[test]
225 fn load_nonexistent_file_returns_error() {
226 let result = ProjectConfig::load(Path::new("/nonexistent/config.yaml"));
227 assert!(result.is_err());
228 }
229
230 #[test]
231 fn load_invalid_yaml_returns_error() {
232 let dir = std::env::temp_dir().join("tonic-rest-openapi-test-invalid");
233 std::fs::create_dir_all(&dir).unwrap();
234 let path = dir.join("bad.yaml");
235 std::fs::write(&path, "public_methods: [[[invalid").unwrap();
236
237 let result = ProjectConfig::load(&path);
238 assert!(result.is_err());
239
240 std::fs::remove_dir_all(&dir).ok();
241 }
242}