1#![warn(missing_docs)]
2
3#[cfg(feature = "serde")]
29pub mod serde;
30
31#[cfg(all(feature = "serde", feature = "utoipa"))]
32#[macro_export]
41macro_rules! constant_string {
42 ($name:ident, $code_name:ident, $code:literal) => {
43 $crate::constant_string_base!($name, $code_name, $code);
44 $crate::constant_string_serde!($name, $code_name, $code);
45 $crate::constant_string_utoipa!($name, $code_name, $code);
46 };
47}
48
49#[cfg(all(feature = "serde", not(feature = "utoipa")))]
50#[macro_export]
59macro_rules! constant_string {
60 ($name:ident, $code_name:ident, $code:literal) => {
61 $crate::constant_string_base!($name, $code_name, $code);
62 $crate::constant_string_serde!($name, $code_name, $code);
63 };
64}
65
66#[cfg(all(not(feature = "serde"), feature = "utoipa"))]
67#[macro_export]
76macro_rules! constant_string {
77 ($name:ident, $code_name:ident, $code:literal) => {
78 $crate::constant_string_base!($name, $code_name, $code);
79 $crate::constant_string_utoipa!($name, $code_name, $code);
80 };
81}
82
83#[cfg(all(not(feature = "serde"), not(feature = "utoipa")))]
84#[macro_export]
93macro_rules! constant_string {
94 ($name:ident, $code_name:ident, $code:literal) => {
95 $crate::constant_string_base!($name, $code_name, $code);
96 };
97}
98
99#[doc(hidden)]
101#[macro_export]
102macro_rules! constant_string_base {
103 ($name:ident, $code_name:ident, $code:literal) => {
104 #[doc = concat!("Constant for [`", stringify!($name), "`].")]
105 const $code_name: &str = $code;
106
107 #[doc = concat!("Constant string `", stringify!($code), "`.")]
108 #[derive(Eq, PartialEq)]
109 pub struct $name;
110
111 impl Default for $name {
112 fn default() -> Self {
113 Self
114 }
115 }
116
117 impl ::std::ops::Deref for $name {
118 type Target = str;
119
120 fn deref(&self) -> &Self::Target {
121 $code_name
122 }
123 }
124
125 impl ::std::fmt::Debug for $name {
126 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
127 ::std::fmt::Debug::fmt(&**self, f)
128 }
129 }
130 };
131}
132
133#[cfg(feature = "serde")]
135#[doc(hidden)]
136#[macro_export]
137macro_rules! constant_string_serde {
138 ($name:ident, $code_name:ident, $code:literal) => {
139 impl<'de> ::serde::Deserialize<'de> for $name {
140 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
141 where
142 D: ::serde::Deserializer<'de>,
143 {
144 deserializer
145 .deserialize_any($crate::serde::MustBeStrVisitor($code_name))
146 .map(|()| Self)
147 }
148 }
149
150 impl ::serde::Serialize for $name {
151 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
152 where
153 S: ::serde::Serializer,
154 {
155 serializer.serialize_str($code_name)
156 }
157 }
158 };
159}
160
161#[cfg(feature = "utoipa")]
163#[doc(hidden)]
164#[macro_export]
165macro_rules! constant_string_utoipa {
166 ($name:ident, $code_name:ident, $code:literal) => {
167 impl ::utoipa::PartialSchema for $name {
168 fn schema() -> ::utoipa::openapi::RefOr<::utoipa::openapi::schema::Schema> {
169 ::utoipa::openapi::schema::ObjectBuilder::new()
170 .schema_type(::utoipa::openapi::schema::Type::String)
171 .enum_values(Some([$code_name]))
172 .build()
173 .into()
174 }
175 }
176
177 impl ::utoipa::ToSchema for $name {}
178 };
179}
180
181#[cfg(test)]
182mod tests {
183 use std::ops::Deref;
184
185 constant_string!(Constant, CONSTANT, "constant");
186
187 #[test]
188 fn constant() {
189 assert_eq!(Constant.deref(), "constant");
190 assert_eq!(Constant.to_string(), "constant".to_owned());
191 }
192
193 #[test]
194 #[expect(clippy::default_constructed_unit_structs)]
195 fn default() {
196 assert_eq!(Constant::default().deref(), "constant");
197 assert_eq!(Constant::default().to_string(), "constant".to_owned());
198 }
199
200 #[cfg(feature = "serde")]
201 #[test]
202 fn serde() {
203 assert_eq!(
204 "\"constant\"",
205 serde_json::to_string(&Constant).expect("serializable value")
206 );
207 assert_eq!(
208 Constant,
209 serde_json::from_str("\"constant\"").expect("deserializable value")
210 );
211 }
212
213 #[cfg(feature = "utoipa")]
214 #[test]
215 fn utoipa() {
216 use utoipa::{
217 PartialSchema,
218 openapi::{
219 RefOr, Type,
220 schema::{Object, Schema},
221 },
222 };
223
224 assert_eq!(
225 RefOr::T(Schema::Object(
226 Object::builder()
227 .schema_type(Type::String)
228 .enum_values(Some(["constant"]))
229 .build()
230 )),
231 Constant::schema()
232 )
233 }
234}