unic_char_property/
macros.rs

1// Copyright 2017 The UNIC Project Developers.
2//
3// See the COPYRIGHT file at the top-level directory of this distribution.
4//
5// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8// option. This file may not be copied, modified, or distributed
9// except according to those terms.
10
11/// Macro for declaring a character property.
12///
13/// # Syntax (Enumerated Property)
14///
15/// ```
16/// #[macro_use]
17/// extern crate unic_char_property;
18///
19/// // First we define the type itself.
20/// char_property! {
21///     /// This is the enum type created for the character property.
22///     pub enum MyProp {
23///         abbr => "AbbrPropName";
24///         long => "Long_Property_Name";
25///         human => "Human-Readable Property Name";
26///
27///         /// Zero or more documentation or other attributes.
28///         RustName {
29///             abbr => AbbrName,
30///             long => Long_Name,
31///             human => "&'static str that is a nicer presentation of the name",
32///         }
33///     }
34///
35///     /// Module aliasing property value abbreviated names.
36///     pub mod abbr_names for abbr;
37///
38///     /// Module aliasing property value long names.
39///     pub mod long_names for long;
40/// }
41///
42/// // We also need to impl `PartialCharProperty` or `TotalCharProperty` manually.
43/// # impl unic_char_property::PartialCharProperty for MyProp {
44/// #     fn of(_: char) -> Option<Self> { None }
45/// # }
46/// #
47/// # fn main() {}
48/// ```
49///
50/// # Syntax (Binary Property)
51///
52/// ```
53/// #[macro_use] extern crate unic_char_property;
54/// # #[macro_use] extern crate unic_char_range;
55///
56/// char_property! {
57///     /// This is the newtype used for the character property.
58///     pub struct MyProp(bool) {
59///         abbr => "AbbrPropName";
60///         long => "Long_Property_Name";
61///         human => "Human-Readable Property Name";
62///
63///         // Unlike an enumerated property, a binary property will handle the table for you.
64///         data_table_path => "../tests/tables/property_table.rsv";
65///     }
66///
67///     /// A function that returns whether the given character has the property or not.
68///     pub fn is_prop(char) -> bool;
69/// }
70///
71/// // You may also want to create a trait for easy access to the properties you define.
72/// # fn main() {}
73/// ```
74///
75/// # Effect
76///
77/// - Implements the `CharProperty` trait and appropriate range trait
78/// - Implements `FromStr` accepting either the abbr or long name, ascii case insensitive
79/// - Implements `Display` using the `human` string
80/// - Populates the module `abbr_names` with `pub use` bindings of variants to their abbr names
81///   (Enumerated properties only)
82/// - Populates the module `long_names` with `pub use` bindings of variants to their long names
83///   (Enumerated properties only)
84/// - Maintains all documentation comments and other `#[attributes]` as would be expected
85///   (with some limitations, listed below)
86///
87#[macro_export]
88macro_rules! char_property {
89
90    // == Enumerated Property == //
91
92    (
93        $(#[$prop_meta:meta])*
94        pub enum $prop_name:ident {
95            abbr => $prop_abbr:expr;
96            long => $prop_long:expr;
97            human => $prop_human:expr;
98
99            $(
100                $(#[$variant_meta:meta])*
101                $variant_name:ident {
102                    abbr => $variant_abbr:ident,
103                    long => $variant_long:ident,
104                    human => $variant_human:expr,
105                }
106            )*
107        }
108
109        $(#[$abbr_mod_meta:meta])*
110        pub mod $abbr_mod:ident for abbr;
111
112        $(#[$long_mod_meta:meta])*
113        pub mod $long_mod:ident for long;
114
115    ) => {
116        $(#[$prop_meta])*
117        #[allow(bad_style)]
118        #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
119        pub enum $prop_name {
120            $( $(#[$variant_meta])* $variant_name, )*
121        }
122
123        $(#[$abbr_mod_meta])*
124        #[allow(bad_style)]
125        pub mod $abbr_mod {
126            $( pub use super::$prop_name::$variant_name as $variant_abbr; )*
127        }
128
129        $(#[$long_mod_meta])*
130        #[allow(bad_style)]
131        pub mod $long_mod {
132            $( pub use super::$prop_name::$variant_name as $variant_long; )*
133        }
134
135        char_property! {
136            __impl FromStr for $prop_name;
137            $(
138                stringify!($variant_abbr) => $prop_name::$variant_name;
139                stringify!($variant_long) => $prop_name::$variant_name;
140            )*
141        }
142
143        char_property! {
144            __impl CharProperty for $prop_name;
145            $prop_abbr;
146            $prop_long;
147            $prop_human;
148        }
149
150        char_property! {
151            __impl Display for $prop_name by EnumeratedCharProperty
152        }
153
154        impl $crate::EnumeratedCharProperty for $prop_name {
155            fn all_values() -> &'static [$prop_name] {
156                const VALUES: &[$prop_name] = &[
157                    $( $prop_name::$variant_name, )*
158                ];
159                VALUES
160            }
161            fn abbr_name(&self) -> &'static str {
162                match *self {
163                    $( $prop_name::$variant_name => stringify!($variant_abbr), )*
164                }
165            }
166            fn long_name(&self) -> &'static str {
167                match *self {
168                    $( $prop_name::$variant_name => stringify!($variant_long), )*
169                }
170            }
171            fn human_name(&self) -> &'static str {
172                match *self {
173                    $( $prop_name::$variant_name => $variant_human, )*
174                }
175            }
176        }
177    };
178
179    // == Binary Property == //
180
181    (
182        $(#[$prop_meta:meta])*
183        pub struct $prop_name:ident(bool) {
184            abbr => $prop_abbr:expr;
185            long => $prop_long:expr;
186            human => $prop_human:expr;
187
188            data_table_path => $data_path:expr;
189        }
190
191        $(#[$is_fn_meta:meta])*
192        pub fn $is_fn:ident(char) -> bool;
193
194    ) => {
195        $(#[$prop_meta])*
196        #[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash)]
197        pub struct $prop_name(bool);
198
199        $(#[$is_fn_meta])*
200        pub fn $is_fn(ch: char) -> bool {
201            $prop_name::of(ch).as_bool()
202        }
203
204        impl $prop_name {
205            /// Get (struct) property value of the character.
206            pub fn of(ch: char) -> Self {
207                use $crate::tables::CharDataTable;
208                const TABLE: CharDataTable<()> = include!($data_path);
209                $prop_name(TABLE.contains(ch))
210            }
211
212            /// Get boolean property value of the character.
213            pub fn as_bool(&self) -> bool { self.0 }
214        }
215
216        char_property! {
217            __impl FromStr for $prop_name;
218            // Yes
219            "y" => $prop_name(true);
220            "yes" => $prop_name(true);
221            "t" => $prop_name(true);
222            "true" => $prop_name(true);
223            // No
224            "n" => $prop_name(false);
225            "no" => $prop_name(false);
226            "f" => $prop_name(false);
227            "false" => $prop_name(false);
228        }
229
230        char_property! {
231            __impl CharProperty for $prop_name;
232            $prop_abbr;
233            $prop_long;
234            $prop_human;
235        }
236
237        impl $crate::TotalCharProperty for $prop_name {
238            fn of(ch: char) -> Self { Self::of(ch) }
239        }
240
241        impl $crate::BinaryCharProperty for $prop_name {
242            fn as_bool(&self) -> bool { self.as_bool() }
243        }
244
245        impl From<$prop_name> for bool {
246            fn from(prop: $prop_name) -> bool { prop.as_bool() }
247        }
248
249        char_property! {
250            __impl Display for $prop_name by BinaryCharProperty
251        }
252    };
253
254    // == Shared == //
255
256    (
257        __impl CharProperty for $prop_name:ident;
258        $prop_abbr:expr;
259        $prop_long:expr;
260        $prop_human:expr;
261    ) => {
262        impl $crate::CharProperty for $prop_name {
263            fn prop_abbr_name() -> &'static str { $prop_abbr }
264            fn prop_long_name() -> &'static str { $prop_long }
265            fn prop_human_name() -> &'static str { $prop_human }
266        }
267    };
268
269    (
270        __impl FromStr for $prop_name:ident;
271        $( $id:expr => $value:expr; )*
272    ) => {
273        #[allow(unreachable_patterns)]
274        impl $crate::__str::FromStr for $prop_name {
275            type Err = ();
276            fn from_str(s: &str) -> Result<Self, Self::Err> {
277                match s {
278                    $( $id => Ok($value), )*
279                    $( s if s.eq_ignore_ascii_case($id) => Ok($value), )*
280                    _ => Err(()),
281                }
282            }
283        }
284    };
285
286    ( __impl Display for $prop_name:ident by $trait:ident ) => {
287        impl $crate::__fmt::Display for $prop_name {
288            fn fmt(&self, f: &mut $crate::__fmt::Formatter) -> $crate::__fmt::Result {
289                $crate::$trait::human_name(self).fmt(f)
290            }
291        }
292    };
293}