Skip to main content

kynos_openapi/model/
server.rs

1//! The Server and Server Variable Objects.
2
3use serde::{Deserialize, Serialize};
4
5use crate::{Map, model::extensions::Extensions};
6
7/// A server hosting the API.
8///
9/// Kynos never infers this from a bind address. The description states the
10/// public URL clients use, which is frequently not the socket the process
11/// listens on.
12#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
13pub struct Server {
14    /// A URL to the target host, optionally templated with `{variable}`.
15    ///
16    /// May be relative to the location the description is served from. Query
17    /// string and fragment components are not permitted.
18    pub url: String,
19
20    /// A name for the server, for use by tooling.
21    ///
22    /// Introduced in OpenAPI 3.2.
23    #[cfg(feature = "openapi32")]
24    #[serde(default, skip_serializing_if = "Option::is_none")]
25    pub name: Option<String>,
26
27    /// A description of the host designated by the URL.
28    #[serde(default, skip_serializing_if = "Option::is_none")]
29    pub description: Option<String>,
30
31    /// A map between a variable name and its value, for URL substitution.
32    #[serde(default, skip_serializing_if = "Map::is_empty")]
33    pub variables: Map<ServerVariable>,
34
35    /// Specification extensions.
36    #[serde(flatten)]
37    pub extensions: Extensions,
38}
39
40impl Server {
41    /// Creates a server at the given URL.
42    pub fn new(url: impl Into<String>) -> Self {
43        Self {
44            url: url.into(),
45            ..Self::default()
46        }
47    }
48
49    /// Sets the description.
50    #[must_use]
51    pub fn with_description(mut self, description: impl Into<String>) -> Self {
52        self.description = Some(description.into());
53        self
54    }
55
56    /// Declares a substitution variable used in the URL template.
57    #[must_use]
58    pub fn with_variable(mut self, name: impl Into<String>, variable: ServerVariable) -> Self {
59        self.variables.insert(name.into(), variable);
60        self
61    }
62}
63
64/// A substitution variable for a templated [`Server::url`].
65#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
66pub struct ServerVariable {
67    /// The set of values this variable may take.
68    ///
69    /// When present it must not be empty, and must contain
70    /// [`default_value`](ServerVariable::default_value).
71    #[serde(rename = "enum", default, skip_serializing_if = "Option::is_none")]
72    pub enumeration: Option<Vec<String>>,
73
74    /// The value to use for substitution when none is supplied.
75    ///
76    /// Unlike JSON Schema's `default`, this field is required.
77    #[serde(rename = "default")]
78    pub default_value: String,
79
80    /// A description of this variable. [CommonMark] syntax may be used.
81    ///
82    /// [CommonMark]: https://spec.commonmark.org/
83    #[serde(default, skip_serializing_if = "Option::is_none")]
84    pub description: Option<String>,
85
86    /// Specification extensions.
87    #[serde(flatten)]
88    pub extensions: Extensions,
89}
90
91impl ServerVariable {
92    /// Creates a free-form variable with the given default.
93    pub fn new(default_value: impl Into<String>) -> Self {
94        Self {
95            default_value: default_value.into(),
96            ..Self::default()
97        }
98    }
99
100    /// Creates a variable constrained to a fixed set of values.
101    pub fn enumerated(
102        default_value: impl Into<String>,
103        values: impl IntoIterator<Item = impl Into<String>>,
104    ) -> Self {
105        Self {
106            enumeration: Some(values.into_iter().map(Into::into).collect()),
107            default_value: default_value.into(),
108            description: None,
109            extensions: Extensions::new(),
110        }
111    }
112}
113
114#[cfg(test)]
115mod tests;