// Copyright 2017 The UNIC Project Developers. // // See the COPYRIGHT file at the top-level directory of this distribution. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. /// Macro for declaring a character property. /// /// # Syntax (Enumerated Property) /// /// ``` /// #[macro_use] /// extern crate unic_char_property; /// /// // First we define the type itself. /// char_property! { /// /// This is the enum type created for the character property. /// pub enum MyProp { /// abbr => "AbbrPropName"; /// long => "Long_Property_Name"; /// human => "Human-Readable Property Name"; /// /// /// Zero or more documentation or other attributes. /// RustName { /// abbr => AbbrName, /// long => Long_Name, /// human => "&'static str that is a nicer presentation of the name", /// } /// } /// /// /// Module aliasing property value abbreviated names. /// pub mod abbr_names for abbr; /// /// /// Module aliasing property value long names. /// pub mod long_names for long; /// } /// /// // We also need to impl `PartialCharProperty` or `TotalCharProperty` manually. /// # impl unic_char_property::PartialCharProperty for MyProp { /// # fn of(_: char) -> Option { None } /// # } /// # /// # fn main() {} /// ``` /// /// # Syntax (Binary Property) /// /// ``` /// #[macro_use] extern crate unic_char_property; /// # #[macro_use] extern crate unic_char_range; /// /// char_property! { /// /// This is the newtype used for the character property. /// pub struct MyProp(bool) { /// abbr => "AbbrPropName"; /// long => "Long_Property_Name"; /// human => "Human-Readable Property Name"; /// /// // Unlike an enumerated property, a binary property will handle the table for you. /// data_table_path => "../tests/tables/property_table.rsv"; /// } /// /// /// A function that returns whether the given character has the property or not. /// pub fn is_prop(char) -> bool; /// } /// /// // You may also want to create a trait for easy access to the properties you define. /// # fn main() {} /// ``` /// /// # Effect /// /// - Implements the `CharProperty` trait and appropriate range trait /// - Implements `FromStr` accepting either the abbr or long name, ascii case insensitive /// - Implements `Display` using the `human` string /// - Populates the module `abbr_names` with `pub use` bindings of variants to their abbr names /// (Enumerated properties only) /// - Populates the module `long_names` with `pub use` bindings of variants to their long names /// (Enumerated properties only) /// - Maintains all documentation comments and other `#[attributes]` as would be expected /// (with some limitations, listed below) /// #[macro_export] macro_rules! char_property { // == Enumerated Property == // ( $(#[$prop_meta:meta])* pub enum $prop_name:ident { abbr => $prop_abbr:expr; long => $prop_long:expr; human => $prop_human:expr; $( $(#[$variant_meta:meta])* $variant_name:ident { abbr => $variant_abbr:ident, long => $variant_long:ident, human => $variant_human:expr, } )* } $(#[$abbr_mod_meta:meta])* pub mod $abbr_mod:ident for abbr; $(#[$long_mod_meta:meta])* pub mod $long_mod:ident for long; ) => { $(#[$prop_meta])* #[allow(bad_style)] #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] pub enum $prop_name { $( $(#[$variant_meta])* $variant_name, )* } $(#[$abbr_mod_meta])* #[allow(bad_style)] pub mod $abbr_mod { $( pub use super::$prop_name::$variant_name as $variant_abbr; )* } $(#[$long_mod_meta])* #[allow(bad_style)] pub mod $long_mod { $( pub use super::$prop_name::$variant_name as $variant_long; )* } char_property! { __impl FromStr for $prop_name; $( stringify!($variant_abbr) => $prop_name::$variant_name; stringify!($variant_long) => $prop_name::$variant_name; )* } char_property! { __impl CharProperty for $prop_name; $prop_abbr; $prop_long; $prop_human; } char_property! { __impl Display for $prop_name by EnumeratedCharProperty } impl $crate::EnumeratedCharProperty for $prop_name { fn all_values() -> &'static [$prop_name] { const VALUES: &[$prop_name] = &[ $( $prop_name::$variant_name, )* ]; VALUES } fn abbr_name(&self) -> &'static str { match *self { $( $prop_name::$variant_name => stringify!($variant_abbr), )* } } fn long_name(&self) -> &'static str { match *self { $( $prop_name::$variant_name => stringify!($variant_long), )* } } fn human_name(&self) -> &'static str { match *self { $( $prop_name::$variant_name => $variant_human, )* } } } }; // == Binary Property == // ( $(#[$prop_meta:meta])* pub struct $prop_name:ident(bool) { abbr => $prop_abbr:expr; long => $prop_long:expr; human => $prop_human:expr; data_table_path => $data_path:expr; } $(#[$is_fn_meta:meta])* pub fn $is_fn:ident(char) -> bool; ) => { $(#[$prop_meta])* #[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash)] pub struct $prop_name(bool); $(#[$is_fn_meta])* pub fn $is_fn(ch: char) -> bool { $prop_name::of(ch).as_bool() } impl $prop_name { /// Get (struct) property value of the character. pub fn of(ch: char) -> Self { use $crate::tables::CharDataTable; const TABLE: CharDataTable<()> = include!($data_path); $prop_name(TABLE.contains(ch)) } /// Get boolean property value of the character. pub fn as_bool(&self) -> bool { self.0 } } char_property! { __impl FromStr for $prop_name; // Yes "y" => $prop_name(true); "yes" => $prop_name(true); "t" => $prop_name(true); "true" => $prop_name(true); // No "n" => $prop_name(false); "no" => $prop_name(false); "f" => $prop_name(false); "false" => $prop_name(false); } char_property! { __impl CharProperty for $prop_name; $prop_abbr; $prop_long; $prop_human; } impl $crate::TotalCharProperty for $prop_name { fn of(ch: char) -> Self { Self::of(ch) } } impl $crate::BinaryCharProperty for $prop_name { fn as_bool(&self) -> bool { self.as_bool() } } impl From<$prop_name> for bool { fn from(prop: $prop_name) -> bool { prop.as_bool() } } char_property! { __impl Display for $prop_name by BinaryCharProperty } }; // == Shared == // ( __impl CharProperty for $prop_name:ident; $prop_abbr:expr; $prop_long:expr; $prop_human:expr; ) => { impl $crate::CharProperty for $prop_name { fn prop_abbr_name() -> &'static str { $prop_abbr } fn prop_long_name() -> &'static str { $prop_long } fn prop_human_name() -> &'static str { $prop_human } } }; ( __impl FromStr for $prop_name:ident; $( $id:expr => $value:expr; )* ) => { #[allow(unreachable_patterns)] impl $crate::__str::FromStr for $prop_name { type Err = (); fn from_str(s: &str) -> Result { match s { $( $id => Ok($value), )* $( s if s.eq_ignore_ascii_case($id) => Ok($value), )* _ => Err(()), } } } }; ( __impl Display for $prop_name:ident by $trait:ident ) => { impl $crate::__fmt::Display for $prop_name { fn fmt(&self, f: &mut $crate::__fmt::Formatter) -> $crate::__fmt::Result { $crate::$trait::human_name(self).fmt(f) } } }; }