blob: b5f054347c9c49e877da3e3e4a3c28097dd99f84 [file] [log] [blame]
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<Self> { 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<Self, Self::Err> {
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)
}
}
};
}