kynos_openapi/model/parameter/style.rs
1//! The Style Object, and the closed style/location table.
2
3use serde::{Deserialize, Serialize};
4
5use crate::model::parameter::ParameterIn;
6
7/// How a parameter value is serialized.
8///
9/// Not every combination of style and location is legal; OpenAPI 3.2 states
10/// that the table of valid combinations is closed. [`crate::validate`] checks
11/// the pairing.
12/// `#[non_exhaustive]` because OpenAPI 3.2 adds to this and the addition is
13/// `#[cfg]`-gated. Cargo unifies features across a dependency graph, so any
14/// crate enabling `openapi32` enables it for every crate in the build -- and
15/// without this attribute that would turn a downstream exhaustive `match` into
16/// a compile error, which is not what "purely additive" is supposed to mean.
17#[non_exhaustive]
18#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
19#[serde(rename_all = "camelCase")]
20pub enum Style {
21 /// Path-style parameters defined by RFC 6570. Path only.
22 Matrix,
23 /// Label-style expansion defined by RFC 6570. Path only.
24 Label,
25 /// Comma-separated values. The default for path and header.
26 Simple,
27 /// Form-style expansion. The default for query and cookie.
28 Form,
29 /// Space-separated array or object values. Query only.
30 SpaceDelimited,
31 /// Pipe-separated array or object values. Query only.
32 PipeDelimited,
33 /// Nested objects rendered as `param[prop]=value`.
34 ///
35 /// Query only, and defined only for objects whose properties are scalars.
36 /// Anything deeper needs [`ParameterIn::Querystring`].
37 DeepObject,
38 /// Cookie-style serialization.
39 ///
40 /// Introduced in OpenAPI 3.2. Cookie only.
41 #[cfg(feature = "openapi32")]
42 Cookie,
43}
44
45/// The one style a header may declare.
46///
47/// A [`Style`] narrowed to the value the specification leaves legal. A header
48/// has no `in` field for a style to disagree with, so the restriction is not a
49/// pairing between two fields but a domain: one variant, and a description
50/// naming any other style does not parse.
51#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
52#[serde(rename_all = "camelCase")]
53pub enum HeaderStyle {
54 /// Comma-separated values, defined by RFC 6570.
55 Simple,
56}
57
58impl From<HeaderStyle> for Style {
59 fn from(_: HeaderStyle) -> Self {
60 Self::Simple
61 }
62}
63
64/// The styles an encoded property may declare.
65///
66/// A [`Style`] narrowed the way [`HeaderStyle`] is. The specification gives an
67/// encoded property the query parameter styles and no others, and an encoding
68/// has no `in` field either, so this is a domain rather than a pairing.
69#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
70#[serde(rename_all = "camelCase")]
71pub enum EncodingStyle {
72 /// Form-style expansion. Applied when none is stated.
73 Form,
74 /// Space-separated array or object values.
75 SpaceDelimited,
76 /// Pipe-separated array or object values.
77 PipeDelimited,
78 /// Nested objects rendered as `prop[key]=value`.
79 DeepObject,
80}
81
82impl From<EncodingStyle> for Style {
83 fn from(style: EncodingStyle) -> Self {
84 match style {
85 EncodingStyle::Form => Self::Form,
86 EncodingStyle::SpaceDelimited => Self::SpaceDelimited,
87 EncodingStyle::PipeDelimited => Self::PipeDelimited,
88 EncodingStyle::DeepObject => Self::DeepObject,
89 }
90 }
91}
92
93impl Style {
94 /// The style applied when none is stated, given a parameter location.
95 #[must_use]
96 pub fn default_for(location: ParameterIn) -> Self {
97 match location {
98 ParameterIn::Query | ParameterIn::Cookie => Self::Form,
99 ParameterIn::Path | ParameterIn::Header => Self::Simple,
100 #[cfg(feature = "openapi32")]
101 ParameterIn::Querystring => Self::Form,
102 }
103 }
104
105 /// Whether this style may be used at the given location.
106 #[must_use]
107 pub fn is_valid_for(self, location: ParameterIn) -> bool {
108 match self {
109 Self::Matrix | Self::Label => location == ParameterIn::Path,
110 Self::Simple => matches!(location, ParameterIn::Path | ParameterIn::Header),
111 Self::Form => matches!(location, ParameterIn::Query | ParameterIn::Cookie),
112 Self::SpaceDelimited | Self::PipeDelimited | Self::DeepObject => {
113 location == ParameterIn::Query
114 }
115 #[cfg(feature = "openapi32")]
116 Self::Cookie => location == ParameterIn::Cookie,
117 }
118 }
119
120 /// Whether `explode` defaults to `true` for this style.
121 ///
122 /// The two styles that pair a name with each value default to exploding;
123 /// every other style defaults to `false`.
124 #[must_use]
125 pub fn default_explode(self) -> bool {
126 match self {
127 Self::Form => true,
128 #[cfg(feature = "openapi32")]
129 Self::Cookie => true,
130 _ => false,
131 }
132 }
133}