From 10cb58062e2d01f7bd93400c2d570aa17a76e450 Mon Sep 17 00:00:00 2001 From: CAD97 Date: Sun, 23 Jul 2017 16:06:13 -0400 Subject: [PATCH 1/2] Add char_property macro Add char_property macro Add char_property macro Add char_property macro Signed-off-by: CAD97 Start of buffer muncher Signed-off-by: CAD97 squished the bug Signed-off-by: CAD97 Munch Display Signed-off-by: CAD97 Add user-facing macro Signed-off-by: CAD97 Remove feature gate openers and debug macro invocation Signed-off-by: CAD97 Simple test Signed-off-by: CAD97 Merge char_property macro/trait into unic-utils codepoints Change associated consts to abbr_names mod Document char_property macro Patch to work on stable Remove feature gate opening Why was that still there Actually let this work on stable now All values slice (Closes benham#66) Align all_values with behnam/#70 long_names module Long_Variant fallback is `Long Variant` Fix the merge... Use the real *CharProperty traits --- unic/utils/src/lib.rs | 2 + unic/utils/src/macros.rs | 341 ++++++++++++++++++++++++++++++++ unic/utils/tests/macro_tests.rs | 56 ++++++ 3 files changed, 399 insertions(+) create mode 100644 unic/utils/src/macros.rs create mode 100644 unic/utils/tests/macro_tests.rs diff --git a/unic/utils/src/lib.rs b/unic/utils/src/lib.rs index 9b87e8ce..c7b3ff36 100644 --- a/unic/utils/src/lib.rs +++ b/unic/utils/src/lib.rs @@ -7,6 +7,7 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. +//! Utilities for use in UNIC development. #![forbid(missing_docs)] @@ -29,6 +30,7 @@ pub const PKG_DESCRIPTION: &'static str = env!("CARGO_PKG_DESCRIPTION"); pub mod char_property; pub mod codepoints; +mod macros; pub mod tables; diff --git a/unic/utils/src/macros.rs b/unic/utils/src/macros.rs new file mode 100644 index 00000000..eaa62353 --- /dev/null +++ b/unic/utils/src/macros.rs @@ -0,0 +1,341 @@ +/// Macro for declaring a character property. +/// +/// # Syntax (Enumerated Property) +/// +/// ``` +/// # #[macro_use] extern crate unic_utils; +/// # fn main() {} +/// char_property! { +/// /// Zero or more attributes +/// pub enum PropertyName { +/// /// Exactly one attribute +/// RustName { +/// abbr => AbbrName, +/// long => Long_Name, +/// display => "&'static str that is a nicer presentation of the name", +/// } +/// +/// /// All annotations on the variant are optional* +/// Variant2 { +/// abbr => V2, // *abbr is required for Enumerated Properties +/// } +/// } +/// +/// /// Zero or more attributes +/// pub mod abbr_names; +/// +/// /// Zero or more attributes +/// pub mod long_names; +/// } +/// +/// // You must impl (Partial/Complete)CharProperty manually. +/// # impl unic_utils::char_property::PartialCharProperty for PropertyName { +/// # fn of(_: char) -> Option { None } +/// # } +/// ``` +/// +/// # Effect +/// +/// - Implements `CharProperty` with the `abbr` and `long` presented in the appropriate method +/// - Implements `FromStr` accepting any of the rust, abbr, or long names +/// - Implements `Display` using the given string, falling back when not provided on +/// the long name, the short name, and the rust name, in that order +/// - Populates the module `abbr_names` with `pub use` bindings of variants to their abbr names +/// - Populates the module `long_names` with `pub use` bindings of variants to their long names +/// - Maintains all documentation comments and other `#[attributes]` as would be expected +/// (with some caveats, listed below) +/// +/// # Limitations +/// +/// Due to [rust-lang/rust/#24189](https://github.com/rust-lang/rust/issues/24189), (fixed in +/// [rust-lang/rust/#42913](https://github.com/rust-lang/rust/pull/42913), landing in 1.20), +/// exactly one attribute line must be used on each variant. On 1.20 or higher, one or more may +/// be used, and the restriction can be relaxed back the intended zero or more by replacing +/// `$(#[$variant_meta:meta])+` with `$(#[$variant_meta:meta])*`, and +/// `$(#[$variant_meta])+` with `$(#[$variant_meta])*`, and +/// `$(#[$ident_meta:meta])+` with `$(#[$ident_meta:meta])*` and +/// `$(#[$ident_meta])+` with `$(#[$ident_meta])*`, and +/// `$(#[$rest_meta:meta])+` with `$(#[$rest_meta:meta])*`, and +/// `$(#[$rest_meta])+` with `$(#[$rest_meta])*`, and +/// `$(#[$queue_meta:meta])+` with `$(#[$queue_meta:meta])*`, and +/// `$(#[$queue_meta])+` with `$(#[$queue_meta])*` +// TODO: Once adopting 1.20, fix the macro to work with zero attributes on variants (see above) +#[macro_export] +macro_rules! char_property { + ( + $(#[$name_meta:meta])* pub enum $name:ident { + $( $(#[$variant_meta:meta])+ $variant:ident $tt:tt )* + } + + $(#[$abbr_names_meta:meta])* pub mod $abbr_names:ident; + $(#[$long_names_meta:meta])* pub mod $long_names:ident; + ) => { + __char_property_internal! { + $(#[$name_meta])* pub enum $name + $(#[$abbr_names_meta])* pub mod $abbr_names + $(#[$long_names_meta])* pub mod $long_names + + variant [ ] + abbr [ ] + long [ ] + display [ ] + + buffer [ ] + queue [ $( $(#[$variant_meta])+ $variant $tt )* ] + } + }; +} + +#[macro_export] +macro_rules! __char_property_internal { + // == Queue => Buffer == // + ( + $(#[$name_meta:meta])* pub enum $name:ident + $(#[$abbr_names_meta:meta])* pub mod $abbr_names:ident + $(#[$long_names_meta:meta])* pub mod $long_names:ident + + variant [ $( $(#[$variant_meta:meta])+ $variant:ident ; )* ] + abbr [ $( $abbr_variant:ident $abbr:ident ; )* ] + long [ $( $long_variant:ident $long:ident ; )* ] + display [ $( $display_variant:ident $display:expr ; )* ] + + buffer [ ] + queue [ + $(#[$ident_meta:meta])+ $ident:ident $ident_tt:tt + $( $(#[$rest_meta:meta])+ $rest:ident $rest_tt:tt )* + ] + ) => { + __char_property_internal! { + $(#[$name_meta])* pub enum $name + $(#[$abbr_names_meta])* pub mod $abbr_names + $(#[$long_names_meta])* pub mod $long_names + + variant [ + $( $(#[$variant_meta])+ $variant ; )* + $(#[$ident_meta])+ $ident ; + ] + abbr [ $( $abbr_variant $abbr ; )* ] + long [ $( $long_variant $long ; )* ] + display [ $( $display_variant $display ; )* ] + + buffer [ $ident $ident_tt ] + queue [ $( $(#[$rest_meta])+ $rest $rest_tt )* ] + } + }; + + // == Buffer -- Abbr Name == // + ( + $(#[$name_meta:meta])* pub enum $name:ident + $(#[$abbr_names_meta:meta])* pub mod $abbr_names:ident + $(#[$long_names_meta:meta])* pub mod $long_names:ident + + variant [ $( $(#[$variant_meta:meta])+ $variant:ident ; )* ] + abbr [ $( $abbr_variant:ident $abbr:ident ; )* ] + long [ $( $long_variant:ident $long:ident ; )* ] + display [ $( $display_variant:ident $display:expr ; )* ] + + buffer [ $ident:ident { + abbr => $ident_abbr:ident , + $( $rest:tt )* + } ] + queue [ $( $(#[$queue_meta:meta])+ $queue:ident $queue_tt:tt )* ] + ) => { + __char_property_internal! { + $(#[$name_meta])* pub enum $name + $(#[$abbr_names_meta])* pub mod $abbr_names + $(#[$long_names_meta])* pub mod $long_names + + variant [ $( $(#[$variant_meta])+ $variant ; )* ] + abbr [ + $( $abbr_variant $abbr ; )* + $ident $ident_abbr ; + ] + long [ $( $long_variant $long ; )* ] + display [ $( $display_variant $display ; )* ] + + buffer [ $ident { $( $rest )* } ] + queue [ $( $(#[$queue_meta])+ $queue $queue_tt )* ] + } + }; + + // == Buffer -- Long Name == // + ( + $(#[$name_meta:meta])* pub enum $name:ident + $(#[$abbr_names_meta:meta])* pub mod $abbr_names:ident + $(#[$long_names_meta:meta])* pub mod $long_names:ident + + variant [ $( $(#[$variant_meta:meta])+ $variant:ident ; )* ] + abbr [ $( $abbr_variant:ident $abbr:ident ; )* ] + long [ $( $long_variant:ident $long:ident ; )* ] + display [ $( $display_variant:ident $display:expr ; )* ] + + buffer [ $ident:ident { + long => $ident_long:ident , + $( $rest:tt )* + } ] + queue [ $( $(#[$queue_meta:meta])+ $queue:ident $queue_tt:tt )* ] + ) => { + __char_property_internal! { + $(#[$name_meta])* pub enum $name + $(#[$abbr_names_meta])* pub mod $abbr_names + $(#[$long_names_meta])* pub mod $long_names + + variant [ $( $(#[$variant_meta])+ $variant ; )* ] + abbr [ $( $abbr_variant $abbr ; )* ] + long [ + $( $long_variant $long ; )* + $ident $ident_long ; + ] + display [ $( $display_variant $display ; )* ] + + buffer [ $ident { $( $rest )* } ] + queue [ $( $(#[$queue_meta])+ $queue $queue_tt )* ] + } + }; + + // == Buffer -- Display // + ( + $(#[$name_meta:meta])* pub enum $name:ident + $(#[$abbr_names_meta:meta])* pub mod $abbr_names:ident + $(#[$long_names_meta:meta])* pub mod $long_names:ident + + variant [ $( $(#[$variant_meta:meta])+ $variant:ident ; )* ] + abbr [ $( $abbr_variant:ident $abbr:ident ; )* ] + long [ $( $long_variant:ident $long:ident ; )* ] + display [ $( $display_variant:ident $display:expr ; )* ] + + buffer [ $ident:ident { + display => $ident_display:expr , + $( $rest:tt )* + } ] + queue [ $( $(#[$queue_meta:meta])+ $queue:ident $queue_tt:tt )* ] + ) => { + __char_property_internal! { + $(#[$name_meta])* pub enum $name + $(#[$abbr_names_meta])* pub mod $abbr_names + $(#[$long_names_meta])* pub mod $long_names + + variant [ $( $(#[$variant_meta])+ $variant ; )* ] + abbr [ $( $abbr_variant $abbr ; )* ] + long [ $( $long_variant $long ; )* ] + display [ + $( $display_variant $display ; )* + $ident $ident_display ; + ] + + buffer [ $ident { $( $rest )* } ] + queue [ $( $(#[$queue_meta])+ $queue $queue_tt )* ] + } + }; + + // == Buffer -- Empty == // + ( + $(#[$name_meta:meta])* pub enum $name:ident + $(#[$abbr_names_meta:meta])* pub mod $abbr_names:ident + $(#[$long_names_meta:meta])* pub mod $long_names:ident + + variant [ $( $(#[$variant_meta:meta])+ $variant:ident ; )* ] + abbr [ $( $abbr_variant:ident $abbr:ident ; )* ] + long [ $( $long_variant:ident $long:ident ; )* ] + display [ $( $display_variant:ident $display:expr ; )* ] + + buffer [ $ident:ident {} ] + queue [ $( $(#[$queue_meta:meta])+ $queue:ident $queue_tt:tt )* ] + ) => { + __char_property_internal! { + $(#[$name_meta])* pub enum $name + $(#[$abbr_names_meta])* pub mod $abbr_names + $(#[$long_names_meta])* pub mod $long_names + + variant [ $( $(#[$variant_meta])+ $variant ; )* ] + abbr [ $( $abbr_variant $abbr ; )* ] + long [ $( $long_variant $long ; )* ] + display [ $( $display_variant $display ; )* ] + + buffer [ ] + queue [ $( $(#[$queue_meta])+ $queue $queue_tt )* ] + } + }; + + // == Final formatting == // + ( + $(#[$name_meta:meta])* pub enum $name:ident + $(#[$abbr_names_meta:meta])* pub mod $abbr_names:ident + $(#[$long_names_meta:meta])* pub mod $long_names:ident + + variant [ $( $(#[$variant_meta:meta])+ $variant:ident ; )* ] + abbr [ $( $abbr_variant:ident $abbr:ident ; )* ] + long [ $( $long_variant:ident $long:ident ; )* ] + display [ $( $display_variant:ident $display:expr ; )* ] + + buffer [ ] + queue [ ] + ) => { + $(#[$name_meta])* + #[allow(bad_style)] + #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] + pub enum $name { + $( $(#[$variant_meta])+ $variant, )* + } + + $(#[$abbr_names_meta])* + #[allow(bad_style)] + pub mod $abbr_names { + $( pub use super::$name::$abbr_variant as $abbr; )* + } + + $(#[$long_names_meta])* + #[allow(bad_style)] + pub mod $long_names { + $( pub use super::$name::$long_variant as $long; )* + } + + #[allow(bad_style)] + #[allow(unreachable_patterns)] + impl ::std::str::FromStr for $name { + type Err = (); + fn from_str(s: &str) -> Result { + match s { + $( stringify!($variant) => Ok($name::$variant), )* + $( stringify!($abbr) => Ok($name::$abbr_variant), )* + $( stringify!($long) => Ok($name::$long_variant), )* + _ => Err(()), + } + } + } + + #[allow(bad_style)] + #[allow(unreachable_patterns)] + impl ::std::fmt::Display for $name { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + match *self { + $( $name::$display_variant => write!(f, "{}", $display), )* + $( $name::$long_variant => write!(f, "{}", stringify!($long).replace('_', " ")), )* + _ => write!(f, "{}", match *self { + $( $name::$abbr_variant => stringify!($abbr), )* + $( $name::$variant => stringify!($variant), )* + }) + } + } + } + + #[allow(bad_style)] + #[allow(unreachable_patterns)] + impl $crate::char_property::EnumeratedCharProperty for $name { + fn abbr_name(&self) -> &'static str { + match *self { + $( $name::$abbr_variant => stringify!($abbr), )* + // No catch all variant + // Abbr name is required on Enumerated properties + } + } + + fn all_values() -> &'static [$name] { + const VALUES: &[$name] = &[ + $($name::$variant,)+ + ]; + VALUES + } + } + }; +} diff --git a/unic/utils/tests/macro_tests.rs b/unic/utils/tests/macro_tests.rs new file mode 100644 index 00000000..2824cbb4 --- /dev/null +++ b/unic/utils/tests/macro_tests.rs @@ -0,0 +1,56 @@ +#[macro_use] +extern crate unic_utils; + +use unic_utils::char_property::EnumeratedCharProperty; + +char_property! { + pub enum Property { + /// Required + AbbrVariant { + abbr => AV, + } + /// Required + LongVariant { + abbr => LV, + long => Long_Variant, + } + /// Required + DisplayVariant { + abbr => DV, + display => "The one and only DISPLAY VARIANT!!!11!", + } + /// Required + EmptyVariant { + abbr => EV, + } + } + + pub mod abbr_names; + pub mod long_names; +} + +impl unic_utils::char_property::PartialCharProperty for Property { + fn of(_: char) -> Option { + None + } +} + +#[test] +fn basic_macro_use() { + assert_eq!(Property::AbbrVariant, abbr_names::AV); + assert_eq!(Property::LongVariant, abbr_names::LV); + assert_eq!(Property::DisplayVariant, abbr_names::DV); + assert_eq!(Property::EmptyVariant, abbr_names::EV); + + assert_eq!(Property::LongVariant, long_names::Long_Variant); + + assert_eq!(Property::AbbrVariant.abbr_name(), "AV"); + assert_eq!(Property::LongVariant.abbr_name(), "LV"); + assert_eq!(Property::DisplayVariant.abbr_name(), "DV"); + assert_eq!(Property::EmptyVariant.abbr_name(), "EV"); + + assert_eq!(format!("{}", Property::AbbrVariant), "AV"); + assert_eq!(format!("{}", Property::LongVariant), "Long Variant"); + assert_eq!(format!("{}", Property::DisplayVariant), "The one and only DISPLAY VARIANT!!!11!"); + assert_eq!(format!("{}", Property::EmptyVariant), "EV"); +} From 6b606334e896291fd3640bda8a138fbe164c9ec3 Mon Sep 17 00:00:00 2001 From: CAD97 Date: Thu, 10 Aug 2017 00:38:20 -0400 Subject: [PATCH 2/2] Implement review feedback --- unic/utils/src/lib.rs | 1 - unic/utils/src/macros.rs | 9 ++++----- unic/utils/tests/macro_tests.rs | 8 ++++---- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/unic/utils/src/lib.rs b/unic/utils/src/lib.rs index c7b3ff36..8f4336c5 100644 --- a/unic/utils/src/lib.rs +++ b/unic/utils/src/lib.rs @@ -7,7 +7,6 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Utilities for use in UNIC development. #![forbid(missing_docs)] diff --git a/unic/utils/src/macros.rs b/unic/utils/src/macros.rs index eaa62353..f9f894e2 100644 --- a/unic/utils/src/macros.rs +++ b/unic/utils/src/macros.rs @@ -22,10 +22,10 @@ /// } /// /// /// Zero or more attributes -/// pub mod abbr_names; +/// pub mod abbr_names for abbr; /// /// /// Zero or more attributes -/// pub mod long_names; +/// pub mod long_names for long; /// } /// /// // You must impl (Partial/Complete)CharProperty manually. @@ -67,8 +67,8 @@ macro_rules! char_property { $( $(#[$variant_meta:meta])+ $variant:ident $tt:tt )* } - $(#[$abbr_names_meta:meta])* pub mod $abbr_names:ident; - $(#[$long_names_meta:meta])* pub mod $long_names:ident; + $(#[$abbr_names_meta:meta])* pub mod $abbr_names:ident for abbr; + $(#[$long_names_meta:meta])* pub mod $long_names:ident for long; ) => { __char_property_internal! { $(#[$name_meta])* pub enum $name @@ -320,7 +320,6 @@ macro_rules! __char_property_internal { } #[allow(bad_style)] - #[allow(unreachable_patterns)] impl $crate::char_property::EnumeratedCharProperty for $name { fn abbr_name(&self) -> &'static str { match *self { diff --git a/unic/utils/tests/macro_tests.rs b/unic/utils/tests/macro_tests.rs index 2824cbb4..26b4ab64 100644 --- a/unic/utils/tests/macro_tests.rs +++ b/unic/utils/tests/macro_tests.rs @@ -17,7 +17,7 @@ char_property! { /// Required DisplayVariant { abbr => DV, - display => "The one and only DISPLAY VARIANT!!!11!", + display => "The one and only DISPLAY VARIANT!", } /// Required EmptyVariant { @@ -25,8 +25,8 @@ char_property! { } } - pub mod abbr_names; - pub mod long_names; + pub mod abbr_names for abbr; + pub mod long_names for long; } impl unic_utils::char_property::PartialCharProperty for Property { @@ -51,6 +51,6 @@ fn basic_macro_use() { assert_eq!(format!("{}", Property::AbbrVariant), "AV"); assert_eq!(format!("{}", Property::LongVariant), "Long Variant"); - assert_eq!(format!("{}", Property::DisplayVariant), "The one and only DISPLAY VARIANT!!!11!"); + assert_eq!(format!("{}", Property::DisplayVariant), "The one and only DISPLAY VARIANT!"); assert_eq!(format!("{}", Property::EmptyVariant), "EV"); }