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