1 //! Defines the macros for repetitive styling implementations
2 
3 // There's a single core set of macros structure here that's essentially repeated twice; once for
4 // implementing `Styler` and once for `Colorize`. We'll go through `Styler` as the example, knowing
5 // that `Colorize` works in precisely the same manner.
6 //
7 // There are four macros in each group. For `Styler`, they are:
8 //  * def_attr_base,
9 //  * def_attr_generic,
10 //  * impl_styler_callback,
11 //  * impl_styler
12 //
13 // Fundamentally, any implementation works in a similar fashion; many methods with near-identical
14 // bodies are grouped together. There are additionally two types of implementors: so-called "base"
15 // implementors (`char`, `String`, etc.) and a single "generic" implementor - 'StyledContent<D>'.
16 //
17 // We can visualize the macro expansion with a sort of pipeline:
18 //
19 //                                    /--------> def_attr_base
20 //   [impl_styler ->] impl_styler_callback
21 //                                    \--------> def_attr_generic
22 //
23 // The code-gen starts at 'impl_styler' for "base" implementors, and at 'impl_styler_callback' for
24 // `StyledContent<D>`. From there, 'impl_styler_callback' either repeatedly calls 'def_attr_base'
25 // or 'def_attr_generic' - this is determined by the 'callback' argument.
26 //
27 // 'def_attr_base' is used to provide the method bodies for base types, and 'def_attr_generic'
28 // provides the method bodies for 'StyledContent<D>'.
29 
30 ////////////////////////////////////////////////////////////////////////////////
31 // `Styler` macros                                                            //
32 ////////////////////////////////////////////////////////////////////////////////
33 
34 // Produces a single method for a "base" Styler implementation
35 //
36 // The first argument is the type for which Styler is being implemented. Because this is the same
37 // for all "base" types, we can collase them into a single macro.
38 macro_rules! def_attr_base {
39     ($impl_ty:ty, $name:ident => $attr:path) => {
40         fn $name(self) -> StyledContent<$impl_ty> {
41             StyledContent::new(
42                 ContentStyle {
43                     attributes: $attr.into(),
44                     ..Default::default()
45                 },
46                 self,
47             )
48         }
49     };
50 }
51 
52 // Produces a single method within an implementation of Styler for 'StyledContent<D>'
53 //
54 // We give it an empty argument at the start so that it has the same "signature" as
55 // 'def_attr_base', which takes a type as its first argument
56 macro_rules! def_attr_generic {
57     ((), $name:ident => $attr:path) => {
58         fn $name(self) -> StyledContent<D> {
59             self.attribute($attr)
60         }
61     };
62 }
63 
64 // Produces the set of methods inside the implementation, but not the outer block itself
65 //
66 // 'callback' should be either one of 'def_attr_base' or 'def_attr_generic'. Each expansion of
67 // 'callback' produces a single method with the name given by the second argument.
68 macro_rules! impl_styler_callback {
69     ($callback:ident!($args:tt)) => {
70         $callback!($args, reset => Attribute::Reset);
71         $callback!($args, bold => Attribute::Bold);
72         $callback!($args, underlined => Attribute::Underlined);
73         $callback!($args, reverse => Attribute::Reverse);
74         $callback!($args, dim => Attribute::Dim);
75         $callback!($args, italic => Attribute::Italic);
76         $callback!($args, negative => Attribute::Reverse);
77         $callback!($args, slow_blink => Attribute::SlowBlink);
78         $callback!($args, rapid_blink => Attribute::RapidBlink);
79         $callback!($args, hidden => Attribute::Hidden);
80         $callback!($args, crossed_out => Attribute::CrossedOut);
81     }
82 }
83 
84 // Produces the full implementation of Styler for "base" types
85 //
86 // This macro is mostly here for convenience; it's nice to not require writing out the
87 // `impl Styler<..> for ..` for each base type.
88 macro_rules! impl_styler {
89     ($impl_ty:ty) => {
90         impl Styler<$impl_ty> for $impl_ty {
91             impl_styler_callback!(def_attr_base!($impl_ty));
92         }
93     };
94 }
95 
96 ////////////////////////////////////////////////////////////////////////////////
97 // `Colorize` macros                                                          //
98 //                                                                            //
99 // These are effectively the same as the `Styler` macros described above, so  //
100 // not much detail is repeated here. Where above we have 'def_attr_*', there  //
101 // is 'def_color_*' here, and 'impl_colorize' takes the place of              //
102 // 'impl_styler'.                                                             //
103 ////////////////////////////////////////////////////////////////////////////////
104 
105 macro_rules! def_color_base {
106     ($color_ty:ty, $side:ident: $name:ident => $color:path) => {
107         fn $name(self) -> StyledContent<$color_ty> {
108             StyledContent::new(
109                 ContentStyle {
110                     $side: Some($color),
111                     ..Default::default()
112                 },
113                 self,
114             )
115         }
116     };
117 }
118 
119 macro_rules! def_color_generic {
120     ((), $side:ident: $name:ident => $color:path) => {
121         fn $name(self) -> StyledContent<D> {
122             StyledContent::new(
123                 ContentStyle {
124                     $side: Some($color),
125                     ..self.style
126                 },
127                 self.content,
128             )
129         }
130     };
131 }
132 
133 macro_rules! impl_colorize_callback {
134     ($callback:ident!($args:tt)) => {
135         // foreground colors
136         $callback!($args, foreground_color: black => Color::Black);
137         $callback!($args, foreground_color: dark_grey => Color::DarkGrey);
138         $callback!($args, foreground_color: red => Color::Red);
139         $callback!($args, foreground_color: dark_red => Color::DarkRed);
140         $callback!($args, foreground_color: green => Color::Green);
141         $callback!($args, foreground_color: dark_green => Color::DarkGreen);
142         $callback!($args, foreground_color: yellow => Color::Yellow);
143         $callback!($args, foreground_color: dark_yellow => Color::DarkYellow);
144         $callback!($args, foreground_color: blue => Color::Blue);
145         $callback!($args, foreground_color: dark_blue => Color::DarkBlue);
146         $callback!($args, foreground_color: magenta => Color::Magenta);
147         $callback!($args, foreground_color: dark_magenta => Color::DarkMagenta);
148         $callback!($args, foreground_color: cyan => Color::Cyan);
149         $callback!($args, foreground_color: dark_cyan => Color::DarkCyan);
150         $callback!($args, foreground_color: white => Color::White);
151         $callback!($args, foreground_color: grey => Color::Grey);
152 
153         // background colors
154         $callback!($args, background_color: on_black => Color::Black);
155         $callback!($args, background_color: on_dark_grey => Color::DarkGrey);
156         $callback!($args, background_color: on_red => Color::Red);
157         $callback!($args, background_color: on_dark_red => Color::DarkRed);
158         $callback!($args, background_color: on_green => Color::Green);
159         $callback!($args, background_color: on_dark_green => Color::DarkGreen);
160         $callback!($args, background_color: on_yellow => Color::Yellow);
161         $callback!($args, background_color: on_dark_yellow => Color::DarkYellow);
162         $callback!($args, background_color: on_blue => Color::Blue);
163         $callback!($args, background_color: on_dark_blue => Color::DarkBlue);
164         $callback!($args, background_color: on_magenta => Color::Magenta);
165         $callback!($args, background_color: on_dark_magenta => Color::DarkMagenta);
166         $callback!($args, background_color: on_cyan => Color::Cyan);
167         $callback!($args, background_color: on_dark_cyan => Color::DarkCyan);
168         $callback!($args, background_color: on_white => Color::White);
169         $callback!($args, background_color: on_grey => Color::Grey);
170     };
171 }
172 
173 macro_rules! impl_colorize {
174     ($impl_ty:ty) => {
175         impl Colorize<$impl_ty> for $impl_ty {
176             impl_colorize_callback!(def_color_base!($impl_ty));
177         }
178     };
179 }
180