lenso_openapi_plugin/
config.rs1use serde::{Deserialize, Serialize};
2
3#[derive(Clone, Debug, Deserialize)]
5#[serde(default, deny_unknown_fields)]
6pub struct OpenApiConfig {
7 title: String,
8 version: String,
9 description: Option<String>,
10 document_path: String,
11 servers: Vec<OpenApiServer>,
12 components: Option<serde_json::Map<String, serde_json::Value>>,
13}
14
15impl OpenApiConfig {
16 pub(crate) fn validate(&self) -> Result<(), String> {
17 if self.title.trim().is_empty() {
18 return Err("title must not be empty".to_owned());
19 }
20 if self.version.trim().is_empty() {
21 return Err("version must not be empty".to_owned());
22 }
23 if !self.document_path.starts_with('/') || self.document_path.contains(['?', '#', '{', '}'])
24 {
25 return Err("document_path must be one static absolute path".to_owned());
26 }
27 if self
28 .servers
29 .iter()
30 .any(|server| server.url.trim().is_empty())
31 {
32 return Err("server URLs must not be empty".to_owned());
33 }
34 Ok(())
35 }
36
37 pub(crate) fn title(&self) -> &str {
38 &self.title
39 }
40
41 pub(crate) fn version(&self) -> &str {
42 &self.version
43 }
44
45 pub(crate) fn description(&self) -> Option<&str> {
46 self.description.as_deref()
47 }
48
49 pub(crate) fn document_path(&self) -> &str {
50 &self.document_path
51 }
52
53 pub(crate) fn servers(&self) -> &[OpenApiServer] {
54 &self.servers
55 }
56
57 pub(crate) fn components(&self) -> Option<&serde_json::Map<String, serde_json::Value>> {
58 self.components.as_ref()
59 }
60}
61
62impl Default for OpenApiConfig {
63 fn default() -> Self {
64 Self {
65 title: "Lenso App".to_owned(),
66 version: "0.0.0".to_owned(),
67 description: None,
68 document_path: "/openapi.json".to_owned(),
69 servers: Vec::new(),
70 components: None,
71 }
72 }
73}
74
75#[derive(Clone, Debug, Deserialize, Serialize)]
77#[serde(deny_unknown_fields)]
78pub struct OpenApiServer {
79 url: String,
80 #[serde(skip_serializing_if = "Option::is_none")]
81 description: Option<String>,
82}
83
84#[cfg(test)]
85mod tests {
86 use super::OpenApiConfig;
87
88 #[test]
89 fn defaults_are_valid_and_unknown_configuration_is_rejected() {
90 OpenApiConfig::default().validate().unwrap();
91 let error = serde_json::from_str::<OpenApiConfig>(r#"{"enabled":true}"#).unwrap_err();
92 assert!(error.to_string().contains("unknown field"));
93 }
94
95 #[test]
96 fn document_path_must_be_one_static_absolute_path() {
97 for path in [
98 "openapi.json",
99 "/openapi/{version}.json",
100 "/openapi.json?x=1",
101 ] {
102 let config = serde_json::from_value::<OpenApiConfig>(serde_json::json!({
103 "document_path": path
104 }))
105 .unwrap();
106 assert!(config.validate().is_err(), "accepted {path}");
107 }
108 }
109}