1#![forbid(unsafe_code)]
31#![warn(missing_docs)]
32
33pub mod error;
35pub mod serde_helpers;
37
38#[doc(hidden)]
41#[macro_export]
42macro_rules! __string_enum_base {
43 (
44 $Type:ident, $enum_name:literal, error, {
45 $( $alias:literal => $variant:path ),+ $(,)?
46 }
47 ) => {
48 impl paft_utils::StringCode for $Type {
49 fn code(&self) -> &str { $Type::code(self) }
50 }
51
52 impl ::serde::Serialize for $Type {
53 fn serialize<S>(&self, serializer: S) -> ::std::result::Result<S::Ok, S::Error>
54 where
55 S: ::serde::Serializer,
56 {
57 serializer.serialize_str(self.code())
58 }
59 }
60
61 impl<'de> ::serde::Deserialize<'de> for $Type {
62 fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
63 where
64 D: ::serde::Deserializer<'de>,
65 {
66 let raw = <String as ::serde::Deserialize>::deserialize(deserializer)?;
67 Self::from_str(&raw).map_err(::serde::de::Error::custom)
68 }
69 }
70
71 impl ::std::str::FromStr for $Type {
72 type Err = $crate::error::PaftError;
73
74 fn from_str(input: &str) -> ::std::result::Result<Self, Self::Err> {
75 let trimmed = input.trim();
76 if trimmed.is_empty() {
77 return Err($crate::error::PaftError::InvalidEnumValue {
78 enum_name: $enum_name,
79 value: input.to_string(),
80 });
81 }
82 let token = paft_utils::canonicalize(trimmed);
83 let parsed = match token.as_ref() {
84 $( $alias => $variant, )*
85 _ => {
86 return Err($crate::error::PaftError::InvalidEnumValue {
87 enum_name: $enum_name,
88 value: input.to_string(),
89 });
90 }
91 };
92 Ok(parsed)
93 }
94 }
95
96 impl ::std::convert::TryFrom<String> for $Type {
97 type Error = $crate::error::PaftError;
98
99 fn try_from(value: String) -> ::std::result::Result<Self, Self::Error> {
100 Self::from_str(&value)
101 }
102 }
103
104 impl ::std::convert::From<$Type> for String {
105 fn from(v: $Type) -> Self { v.code().to_string() }
106 }
107
108 impl $Type {
109 #[doc(hidden)]
110 pub const __ALIASES: &'static [(&'static str, $Type)] = &[
111 $( ($alias, $variant) ),*
112 ];
113 }
114 };
115
116 (
117 $Type:ident, $enum_name:literal, other($OtherVariant:path), {
118 $( $alias:literal => $variant:path ),+ $(,)?
119 }
120 ) => {
121 impl paft_utils::StringCode for $Type {
122 fn code(&self) -> &str { $Type::code(self) }
123 fn is_canonical(&self) -> bool { $Type::is_canonical(self) }
124 }
125
126 impl $Type {
127 #[must_use]
129 pub const fn is_canonical(&self) -> bool { !matches!(self, $OtherVariant(_)) }
130 }
131
132 impl ::serde::Serialize for $Type {
133 fn serialize<S>(&self, serializer: S) -> ::std::result::Result<S::Ok, S::Error>
134 where
135 S: ::serde::Serializer,
136 {
137 serializer.serialize_str(self.code())
138 }
139 }
140
141 impl<'de> ::serde::Deserialize<'de> for $Type {
142 fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
143 where
144 D: ::serde::Deserializer<'de>,
145 {
146 let raw = <String as ::serde::Deserialize>::deserialize(deserializer)?;
147 Self::from_str(&raw).map_err(::serde::de::Error::custom)
148 }
149 }
150
151 impl ::std::str::FromStr for $Type {
152 type Err = $crate::error::PaftError;
153
154 fn from_str(input: &str) -> ::std::result::Result<Self, Self::Err> {
155 let trimmed = input.trim();
156 if trimmed.is_empty() {
157 return Err($crate::error::PaftError::InvalidEnumValue {
158 enum_name: $enum_name,
159 value: input.to_string(),
160 });
161 }
162 let token = paft_utils::canonicalize(trimmed);
163 let parsed = match token.as_ref() {
164 $( $alias => $variant, )*
165 _ => {
166 let canon = paft_utils::Canonical::try_new(trimmed)
167 .map_err(|_| $crate::error::PaftError::InvalidEnumValue {
168 enum_name: $enum_name,
169 value: input.to_string(),
170 })?;
171 return Ok($OtherVariant(canon));
172 }
173 };
174 Ok(parsed)
175 }
176 }
177
178 impl ::std::convert::TryFrom<String> for $Type {
179 type Error = $crate::error::PaftError;
180
181 fn try_from(value: String) -> ::std::result::Result<Self, Self::Error> {
182 Self::from_str(&value)
183 }
184 }
185
186 impl ::std::convert::From<$Type> for String {
187 fn from(v: $Type) -> Self { v.code().to_string() }
188 }
189
190 impl $Type {
191 #[doc(hidden)]
192 pub const __ALIASES: &'static [(&'static str, $Type)] = &[
193 $( ($alias, $variant) ),*
194 ];
195 }
196 };
197}
198
199#[doc(hidden)]
201#[macro_export]
202macro_rules! impl_display_via_code {
203 ( $Type:ident ) => {
204 impl ::std::fmt::Display for $Type {
205 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
206 f.write_str(self.code())
207 }
208 }
209 };
210}
211
212#[doc(hidden)]
214#[macro_export]
215macro_rules! string_enum_with_code {
216 (
217 $Type:ident, $Other:ident, $enum_name:literal,
218 { $( $canon_token:literal => $canon_variant:path ),+ $(,)? },
219 { $( $alias:literal => $variant:path ),* $(,)? }
220 ) => {
221 impl $Type {
222 #[must_use]
224 pub fn code(&self) -> &str {
225 match self {
226 $( $canon_variant => $canon_token, )+
227 $Type::$Other(s) => s.as_ref(),
228 }
229 }
230 }
231
232 $crate::__string_enum_base! {
233 $Type, $enum_name, other($Type::$Other),
234 { $( $canon_token => $canon_variant ),+ $(, $alias => $variant )* }
235 }
236 };
237
238 (
239 $Type:ident, $Other:ident, $enum_name:literal,
240 { $( $canon_token:literal => $canon_variant:path ),+ $(,)? }
241 ) => {
242 $crate::string_enum_with_code!(
243 $Type, $Other, $enum_name,
244 { $( $canon_token => $canon_variant ),+ },
245 {}
246 );
247 };
248}
249
250#[doc(hidden)]
252#[macro_export]
253macro_rules! string_enum_closed_with_code {
254 (
255 $Type:ident, $enum_name:literal,
256 { $( $canon_token:literal => $canon_variant:path ),+ $(,)? },
257 { $( $alias:literal => $variant:path ),* $(,)? }
258 ) => {
259 impl $Type {
260 #[must_use]
262 pub const fn code(&self) -> &str {
263 match self {
264 $( $canon_variant => $canon_token, )+
265 }
266 }
267 }
268
269 $crate::__string_enum_base! {
270 $Type, $enum_name, error,
271 { $( $canon_token => $canon_variant ),+ $(, $alias => $variant )* }
272 }
273 };
274
275 (
276 $Type:ident, $enum_name:literal,
277 { $( $canon_token:literal => $canon_variant:path ),+ $(,)? }
278 ) => {
279 $crate::string_enum_closed_with_code!(
280 $Type, $enum_name,
281 { $( $canon_token => $canon_variant ),+ },
282 {}
283 );
284 };
285}
286
287pub use error::PaftError;