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}