use reinhardt_macros::settings;
#[settings(fragment = true, section = "openapi")]
#[non_exhaustive]
pub struct OpenApiSettings {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_json_path")]
pub json_path: String,
#[serde(default = "default_swagger_path")]
pub swagger_path: String,
#[serde(default = "default_redoc_path")]
pub redoc_path: String,
#[serde(default = "default_title")]
pub title: String,
#[serde(default = "default_version")]
pub version: String,
#[serde(default)]
pub description: Option<String>,
}
impl Default for OpenApiSettings {
fn default() -> Self {
Self {
enabled: true,
json_path: "/api/openapi.json".to_string(),
swagger_path: "/api/docs".to_string(),
redoc_path: "/api/redoc".to_string(),
title: "API Documentation".to_string(),
version: "1.0.0".to_string(),
description: None,
}
}
}
fn default_true() -> bool {
true
}
fn default_json_path() -> String {
"/api/openapi.json".to_string()
}
fn default_swagger_path() -> String {
"/api/docs".to_string()
}
fn default_redoc_path() -> String {
"/api/redoc".to_string()
}
fn default_title() -> String {
"API Documentation".to_string()
}
fn default_version() -> String {
"1.0.0".to_string()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::settings::fragment::SettingsFragment;
use rstest::rstest;
#[rstest]
fn test_openapi_section_name() {
let section = OpenApiSettings::section();
assert_eq!(section, "openapi");
}
#[rstest]
fn test_openapi_default_values() {
let settings = OpenApiSettings::default();
assert!(settings.enabled);
assert_eq!(settings.json_path, "/api/openapi.json");
assert_eq!(settings.swagger_path, "/api/docs");
assert_eq!(settings.redoc_path, "/api/redoc");
assert_eq!(settings.title, "API Documentation");
assert_eq!(settings.version, "1.0.0");
assert_eq!(settings.description, None);
}
#[rstest]
fn test_openapi_serde_roundtrip() {
let original = OpenApiSettings {
title: "My API".to_string(),
version: "2.0.0".to_string(),
description: Some("Custom API".to_string()),
..Default::default()
};
let json = serde_json::to_string(&original).unwrap();
let deserialized: OpenApiSettings = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.title, "My API");
assert_eq!(deserialized.version, "2.0.0");
assert_eq!(deserialized.description, Some("Custom API".to_string()));
assert!(deserialized.enabled);
}
#[rstest]
fn test_openapi_serde_missing_fields_use_defaults() {
let json = r#"{"title":"Test API"}"#;
let settings: OpenApiSettings = serde_json::from_str(json).unwrap();
assert_eq!(settings.title, "Test API");
assert_eq!(settings.version, "1.0.0");
assert!(settings.enabled);
assert_eq!(settings.json_path, "/api/openapi.json");
assert_eq!(settings.swagger_path, "/api/docs");
assert_eq!(settings.redoc_path, "/api/redoc");
assert_eq!(settings.description, None);
}
#[rstest]
fn test_openapi_toml_deserialization() {
let toml_str = r#"
enabled = true
title = "My REST API"
version = "3.0.0"
swagger_path = "/docs/swagger"
redoc_path = "/docs/redoc"
json_path = "/docs/openapi.json"
description = "Full API documentation"
"#;
let settings: OpenApiSettings = toml::from_str(toml_str).expect("failed to parse TOML");
assert!(settings.enabled);
assert_eq!(settings.title, "My REST API");
assert_eq!(settings.version, "3.0.0");
assert_eq!(settings.swagger_path, "/docs/swagger");
assert_eq!(settings.redoc_path, "/docs/redoc");
assert_eq!(settings.json_path, "/docs/openapi.json");
assert_eq!(
settings.description,
Some("Full API documentation".to_string())
);
}
#[rstest]
fn test_openapi_toml_partial_uses_defaults() {
let toml_str = r#"
title = "Partial API"
"#;
let settings: OpenApiSettings = toml::from_str(toml_str).expect("failed to parse TOML");
assert!(settings.enabled);
assert_eq!(settings.title, "Partial API");
assert_eq!(settings.version, "1.0.0");
assert_eq!(settings.swagger_path, "/api/docs");
}
#[rstest]
fn test_openapi_disabled() {
let json = r#"{"enabled":false}"#;
let settings: OpenApiSettings = serde_json::from_str(json).unwrap();
assert!(!settings.enabled);
assert_eq!(settings.title, "API Documentation");
}
#[rstest]
fn test_has_openapi_settings_trait() {
struct Wrapper {
openapi: OpenApiSettings,
}
impl HasOpenApiSettings for Wrapper {
fn openapi(&self) -> &OpenApiSettings {
&self.openapi
}
}
let wrapper = Wrapper {
openapi: OpenApiSettings {
title: "Trait Test".to_string(),
..Default::default()
},
};
let settings = wrapper.openapi();
assert_eq!(settings.title, "Trait Test");
}
}