1#![warn(missing_docs)]
2
3#[cfg(feature = "serde")]
6pub mod serde;
7
8#[cfg(all(feature = "serde", feature = "utoipa"))]
9#[macro_export]
11macro_rules! constant_string {
12 ($name:ident, $code_name:ident, $code:literal) => {
13 $crate::constant_string_base!($name, $code_name, $code);
14 $crate::constant_string_serde!($name, $code_name, $code);
15 $crate::constant_string_utoipa!($name, $code_name, $code);
16 };
17}
18
19#[cfg(all(feature = "serde", not(feature = "utoipa")))]
20#[macro_export]
22macro_rules! constant_string {
23 ($name:ident, $code_name:ident, $code:literal) => {
24 $crate::constant_string_base!($name, $code_name, $code);
25 $crate::constant_string_serde!($name, $code_name, $code);
26 };
27}
28
29#[cfg(all(not(feature = "serde"), feature = "utoipa"))]
30#[macro_export]
32macro_rules! constant_string {
33 ($name:ident, $code_name:ident, $code:literal) => {
34 $crate::constant_string_base!($name, $code_name, $code);
35 $crate::constant_string_utoipa!($name, $code_name, $code);
36 };
37}
38
39#[cfg(all(not(feature = "serde"), not(feature = "utoipa")))]
40#[macro_export]
42macro_rules! constant_string {
43 ($name:ident, $code_name:ident, $code:literal) => {
44 $crate::constant_string_base!($name, $code_name, $code);
45 };
46}
47
48#[macro_export]
50macro_rules! constant_string_base {
51 ($name:ident, $code_name:ident, $code:literal) => {
52 #[doc = concat!("Constant for [`", stringify!($name), "`].")]
53 const $code_name: &str = $code;
54
55 #[doc = concat!("Constant string `", stringify!($code), "`.")]
56 #[derive(Eq, PartialEq)]
57 pub struct $name;
58
59 impl Default for $name {
60 fn default() -> Self {
61 Self
62 }
63 }
64
65 impl ::std::ops::Deref for $name {
66 type Target = str;
67
68 fn deref(&self) -> &Self::Target {
69 $code_name
70 }
71 }
72
73 impl ::std::fmt::Debug for $name {
74 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
75 ::std::fmt::Debug::fmt(&**self, f)
76 }
77 }
78 };
79}
80
81#[cfg(feature = "serde")]
83#[macro_export]
84macro_rules! constant_string_serde {
85 ($name:ident, $code_name:ident, $code:literal) => {
86 impl<'de> ::serde::Deserialize<'de> for $name {
87 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
88 where
89 D: ::serde::Deserializer<'de>,
90 {
91 deserializer
92 .deserialize_any($crate::serde::MustBeStrVisitor($code_name))
93 .map(|()| Self)
94 }
95 }
96
97 impl ::serde::Serialize for $name {
98 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
99 where
100 S: ::serde::Serializer,
101 {
102 serializer.serialize_str($code_name)
103 }
104 }
105 };
106}
107
108#[cfg(feature = "utoipa")]
110#[macro_export]
111macro_rules! constant_string_utoipa {
112 ($name:ident, $code_name:ident, $code:literal) => {
113 impl ::utoipa::PartialSchema for $name {
114 fn schema() -> ::utoipa::openapi::RefOr<::utoipa::openapi::schema::Schema> {
115 ::utoipa::openapi::schema::ObjectBuilder::new()
116 .schema_type(::utoipa::openapi::schema::Type::String)
117 .enum_values(Some([$code_name]))
118 .build()
119 .into()
120 }
121 }
122
123 impl ::utoipa::ToSchema for $name {}
124 };
125}
126
127#[cfg(test)]
128mod tests {
129 use std::ops::Deref;
130
131 constant_string!(Constant, CONSTANT, "constant");
132
133 #[test]
134 fn constant() {
135 assert_eq!(Constant.deref(), "constant");
136 assert_eq!(Constant.to_string(), "constant".to_owned());
137 }
138
139 #[test]
140 #[expect(clippy::default_constructed_unit_structs)]
141 fn default() {
142 assert_eq!(Constant::default().deref(), "constant");
143 assert_eq!(Constant::default().to_string(), "constant".to_owned());
144 }
145
146 #[cfg(feature = "serde")]
147 #[test]
148 fn serde() {
149 assert_eq!(
150 "\"constant\"",
151 serde_json::to_string(&Constant).expect("serializable value")
152 );
153 assert_eq!(
154 Constant,
155 serde_json::from_str("\"constant\"").expect("deserializable value")
156 );
157 }
158
159 #[cfg(feature = "utoipa")]
160 #[test]
161 fn utoipa() {
162 use utoipa::{
163 PartialSchema,
164 openapi::{
165 RefOr, Type,
166 schema::{Object, Schema},
167 },
168 };
169
170 assert_eq!(
171 RefOr::T(Schema::Object(
172 Object::builder()
173 .schema_type(Type::String)
174 .enum_values(Some(["constant"]))
175 .build()
176 )),
177 Constant::schema()
178 )
179 }
180}