language_code/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2
3pub extern crate alloc;
4
5//
6#[macro_export]
7macro_rules! language_code {
8    (
9        length = $length:tt;
10        $( #[$meta:meta] )*
11        $pub:vis enum $name:ident {
12            $(
13                $( #[$variant_meta:meta] )*
14                $variant:ident,
15            )+
16        }
17    ) => {
18        $(#[$meta])*
19        $pub enum $name {
20            $(
21                $( #[$variant_meta] )*
22                $variant,
23            )+
24            Other($crate::alloc::boxed::Box<str>),
25        }
26
27        //
28        impl $name {
29            pub const VARS: &'static [$name] = &[
30                $(
31                    $name::$variant,
32                )+
33            ];
34        }
35
36        //
37        paste::paste! {
38            impl ::core::str::FromStr for $name {
39                type Err = $crate::error::ParseError;
40
41                fn from_str(s: &str) -> Result<Self, Self::Err> {
42                    match s {
43                        $(
44                            ::core::stringify!($variant) | ::core::stringify!([<$variant:upper>]) => Ok(Self::$variant),
45                        )+
46                        s if s.len() == $length => Ok(Self::Other(s.into())),
47                        s => Err($crate::error::ParseError::Invalid(s.into()))
48                    }
49                }
50            }
51        }
52
53        //
54        impl ::core::fmt::Display for $name {
55            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
56                match self {
57                    $(
58                        Self::$variant => ::core::write!(f, "{}", ::core::stringify!($variant)),
59                    )+
60                    Self::Other(s) => ::core::write!(f, "{}", s)
61                }
62            }
63        }
64
65        //
66        impl ::core::cmp::PartialEq for $name {
67            fn eq(&self, other: &Self) -> bool {
68                $crate::alloc::format!("{}", self) == $crate::alloc::format!("{}", other)
69            }
70        }
71
72        impl ::core::cmp::Eq for $name {
73        }
74
75        //
76        impl_macros::impl_partial_eq_str_for_display! { str, $name }
77        impl_macros::impl_partial_eq_str_for_display! { &'a str, $name }
78        impl_macros::impl_partial_eq_str_for_display! { $crate::alloc::borrow::Cow<'a, str>, $name }
79        impl_macros::impl_partial_eq_str_for_display! { $crate::alloc::string::String, $name }
80
81        //
82        #[cfg(feature = "std")]
83        impl ::std::hash::Hash for $name {
84            fn hash<H: ::std::hash::Hasher>(&self, state: &mut H) {
85                $crate::alloc::format!("{}", self).hash(state);
86            }
87        }
88
89        //
90        #[cfg(feature = "serde")]
91        impl<'de> ::serde::Deserialize<'de> for $name {
92            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
93            where
94                D: ::serde::Deserializer<'de>,
95            {
96                use ::core::str::FromStr as _;
97
98                let s = $crate::alloc::boxed::Box::<str>::deserialize(deserializer)?;
99                Self::from_str(&s).map_err(::serde::de::Error::custom)
100            }
101        }
102
103        //
104        #[cfg(feature = "serde")]
105        impl ::serde::Serialize for $name {
106            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
107            where
108                S: ::serde::Serializer,
109            {
110                use $crate::alloc::string::ToString as _;
111
112                self.to_string().serialize(serializer)
113            }
114        }
115    };
116}
117
118//
119#[macro_export]
120macro_rules! language_tag {
121    (
122        $( #[$meta:meta] )*
123        $pub:vis struct $name:ident {
124            $( #[$language_code_meta:meta] )*
125            $language_code_pub:vis $language_code_name:ident : $language_code_ty:ty,
126            $( #[$country_code_meta:meta] )*
127            $country_code_pub:vis $country_code_name:ident : Option<$country_code_ty:ty>,
128        }
129    ) => {
130        $(#[$meta])*
131        $pub struct $name {
132            $( #[$language_code_meta:meta] )*
133            $language_code_pub $language_code_name: $language_code_ty,
134            $( #[$country_code_meta:meta] )*
135            $country_code_pub $country_code_name: Option<$country_code_ty>,
136        }
137
138        //
139        impl $name {
140            pub fn new(language_code: $language_code_ty, country_code: Option<$country_code_ty>) -> Self {
141                Self {
142                    language_code,
143                    country_code,
144                }
145            }
146        }
147
148        //
149        impl ::core::str::FromStr for $name {
150            type Err = $crate::error::LanguageTagParseError;
151
152            fn from_str(s: &str) -> Result<Self, Self::Err> {
153                let language_code_s = s.chars().take_while(|x| x != &'-' && x != &'_')
154                                                .collect::<$crate::alloc::string::String>();
155                let language_code = language_code_s.parse::<$language_code_ty>()
156                                                    .map_err(|_| $crate::error::LanguageTagParseError::LanguageCodeInvalid(language_code_s.as_str().into()))?;
157
158                let country_code = if s.len() > language_code_s.len() + 1 {
159                    let country_code_s = &s[language_code_s.len() + 1..];
160                    let country_code = country_code_s.parse::<$country_code_ty>()
161                                                    .map_err(|_| $crate::error::LanguageTagParseError::CountryCodeInvalid(country_code_s.into()))?;
162                    Some(country_code)
163                } else {
164                    None
165                };
166
167                Ok(Self::new(language_code, country_code))
168            }
169        }
170
171        //
172        impl ::core::fmt::Display for $name {
173            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
174                if let Some(country_code) = &self.country_code {
175                    ::core::write!(f, "{}-{}", &self.language_code, country_code)
176                } else {
177                    ::core::write!(f, "{}", &self.language_code)
178                }
179            }
180        }
181
182        //
183        impl ::core::cmp::PartialEq for $name {
184            fn eq(&self, other: &Self) -> bool {
185                $crate::alloc::format!("{}", self) == $crate::alloc::format!("{}", other)
186            }
187        }
188
189        impl ::core::cmp::Eq for $name {
190        }
191
192        //
193        impl_macros::impl_partial_eq_str_for_display! { str, $name }
194        impl_macros::impl_partial_eq_str_for_display! { &'a str, $name }
195        impl_macros::impl_partial_eq_str_for_display! { $crate::alloc::borrow::Cow<'a, str>, $name }
196        impl_macros::impl_partial_eq_str_for_display! { $crate::alloc::string::String, $name }
197
198        //
199        #[cfg(feature = "std")]
200        impl ::std::hash::Hash for $name {
201            fn hash<H: ::std::hash::Hasher>(&self, state: &mut H) {
202                $crate::alloc::format!("{}", self).hash(state);
203            }
204        }
205
206        //
207        #[cfg(feature = "serde")]
208        impl<'de> ::serde::Deserialize<'de> for $name {
209            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
210            where
211                D: ::serde::Deserializer<'de>,
212            {
213                use ::core::str::FromStr as _;
214
215                let s = $crate::alloc::boxed::Box::<str>::deserialize(deserializer)?;
216                Self::from_str(&s).map_err(::serde::de::Error::custom)
217            }
218        }
219
220        //
221        #[cfg(feature = "serde")]
222        impl ::serde::Serialize for $name {
223            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
224            where
225                S: ::serde::Serializer,
226            {
227                use $crate::alloc::string::ToString as _;
228
229                self.to_string().serialize(serializer)
230            }
231        }
232    };
233}
234
235//
236pub mod error;
237
238//
239pub mod iso639_1;
240
241pub use iso639_1::{LanguageCode, LanguageTag};