Skip to main content

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}