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