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