1
1
use std:: borrow:: Cow ;
2
- use std:: collections:: BTreeSet ;
3
2
use std:: env;
4
3
use std:: fmt;
4
+ use std:: fmt:: { Debug , Formatter } ;
5
5
use std:: sync:: atomic:: { AtomicBool , Ordering } ;
6
6
7
7
use once_cell:: sync:: Lazy ;
@@ -116,32 +116,85 @@ impl Color {
116
116
117
117
/// A terminal style attribute.
118
118
#[ derive( Copy , Clone , Debug , PartialEq , Eq , Ord , PartialOrd ) ]
119
+ #[ repr( u16 ) ]
119
120
pub enum Attribute {
120
- Bold ,
121
- Dim ,
122
- Italic ,
123
- Underlined ,
124
- Blink ,
125
- BlinkFast ,
126
- Reverse ,
127
- Hidden ,
128
- StrikeThrough ,
121
+ // This mapping is important, it exactly matches ansi_num = (x as u16 + 1)
122
+ // See `ATTRIBUTES_LOOKUP` as well
123
+ Bold = 0 ,
124
+ Dim = 1 ,
125
+ Italic = 2 ,
126
+ Underlined = 3 ,
127
+ Blink = 4 ,
128
+ BlinkFast = 5 ,
129
+ Reverse = 6 ,
130
+ Hidden = 7 ,
131
+ StrikeThrough = 8 ,
129
132
}
130
133
131
- impl Attribute {
134
+ #[ derive( Clone , Copy , PartialEq , Eq ) ]
135
+ struct Attributes ( u16 ) ;
136
+
137
+ impl Attributes {
138
+ const ATTRIBUTES_LOOKUP : [ Attribute ; 9 ] = [
139
+ Attribute :: Bold ,
140
+ Attribute :: Dim ,
141
+ Attribute :: Italic ,
142
+ Attribute :: Underlined ,
143
+ Attribute :: Blink ,
144
+ Attribute :: BlinkFast ,
145
+ Attribute :: Reverse ,
146
+ Attribute :: Hidden ,
147
+ Attribute :: StrikeThrough ,
148
+ ] ;
149
+
132
150
#[ inline]
133
- fn ansi_num ( self ) -> usize {
134
- match self {
135
- Attribute :: Bold => 1 ,
136
- Attribute :: Dim => 2 ,
137
- Attribute :: Italic => 3 ,
138
- Attribute :: Underlined => 4 ,
139
- Attribute :: Blink => 5 ,
140
- Attribute :: BlinkFast => 6 ,
141
- Attribute :: Reverse => 7 ,
142
- Attribute :: Hidden => 8 ,
143
- Attribute :: StrikeThrough => 9 ,
151
+ const fn new ( ) -> Self {
152
+ Attributes ( 0 )
153
+ }
154
+
155
+ #[ inline]
156
+ #[ must_use]
157
+ const fn insert ( mut self , attr : Attribute ) -> Self {
158
+ let bit = attr as u16 ;
159
+ self . 0 |= 1 << bit;
160
+ self
161
+ }
162
+
163
+ #[ inline]
164
+ const fn bits ( self ) -> BitsIter {
165
+ BitsIter ( self . 0 )
166
+ }
167
+
168
+ #[ inline]
169
+ fn ansi_nums ( self ) -> impl Iterator < Item = u16 > {
170
+ // Per construction of the enum
171
+ self . bits ( ) . map ( |bit| bit + 1 )
172
+ }
173
+
174
+ #[ inline]
175
+ fn attrs ( self ) -> impl Iterator < Item = Attribute > {
176
+ self . bits ( ) . map ( |bit| Self :: ATTRIBUTES_LOOKUP [ bit as usize ] )
177
+ }
178
+ }
179
+
180
+ struct BitsIter ( u16 ) ;
181
+
182
+ impl Iterator for BitsIter {
183
+ type Item = u16 ;
184
+
185
+ fn next ( & mut self ) -> Option < Self :: Item > {
186
+ if self . 0 == 0 {
187
+ return None ;
144
188
}
189
+ let bit = self . 0 . trailing_zeros ( ) ;
190
+ self . 0 ^= ( 1 << bit) as u16 ;
191
+ Some ( bit as u16 )
192
+ }
193
+ }
194
+
195
+ impl Debug for Attributes {
196
+ fn fmt ( & self , f : & mut Formatter < ' _ > ) -> fmt:: Result {
197
+ f. debug_set ( ) . entries ( self . attrs ( ) ) . finish ( )
145
198
}
146
199
}
147
200
@@ -160,7 +213,7 @@ pub struct Style {
160
213
bg : Option < Color > ,
161
214
fg_bright : bool ,
162
215
bg_bright : bool ,
163
- attrs : BTreeSet < Attribute > ,
216
+ attrs : Attributes ,
164
217
force : Option < bool > ,
165
218
for_stderr : bool ,
166
219
}
@@ -179,7 +232,7 @@ impl Style {
179
232
bg : None ,
180
233
fg_bright : false ,
181
234
bg_bright : false ,
182
- attrs : BTreeSet :: new ( ) ,
235
+ attrs : Attributes :: new ( ) ,
183
236
force : None ,
184
237
for_stderr : false ,
185
238
}
@@ -291,7 +344,7 @@ impl Style {
291
344
/// Adds a attr.
292
345
#[ inline]
293
346
pub fn attr ( mut self , attr : Attribute ) -> Self {
294
- self . attrs . insert ( attr) ;
347
+ self . attrs = self . attrs . insert ( attr) ;
295
348
self
296
349
}
297
350
@@ -650,8 +703,8 @@ macro_rules! impl_fmt {
650
703
}
651
704
reset = true ;
652
705
}
653
- for attr in & self . style. attrs {
654
- write!( f, "\x1b [{}m" , attr . ansi_num( ) ) ?;
706
+ for ansi_num in self . style. attrs. ansi_nums ( ) {
707
+ write!( f, "\x1b [{}m" , ansi_num) ?;
655
708
reset = true ;
656
709
}
657
710
}
@@ -965,3 +1018,55 @@ fn test_pad_str_with() {
965
1018
"foo..."
966
1019
) ;
967
1020
}
1021
+
1022
+ #[ test]
1023
+ fn test_attributes_single ( ) {
1024
+ for attr in Attributes :: ATTRIBUTES_LOOKUP {
1025
+ let attrs = Attributes :: new ( ) . insert ( attr) ;
1026
+ assert_eq ! ( attrs. bits( ) . collect:: <Vec <_>>( ) , [ attr as u16 ] ) ;
1027
+ assert_eq ! ( attrs. ansi_nums( ) . collect:: <Vec <_>>( ) , [ attr as u16 + 1 ] ) ;
1028
+ assert_eq ! ( attrs. attrs( ) . collect:: <Vec <_>>( ) , [ attr] ) ;
1029
+ assert_eq ! ( format!( "{:?}" , attrs) , format!( "{{{:?}}}" , attr) ) ;
1030
+ }
1031
+ }
1032
+
1033
+ #[ test]
1034
+ fn test_attributes_many ( ) {
1035
+ let tests: [ & [ Attribute ] ; 3 ] = [
1036
+ & [
1037
+ Attribute :: Bold ,
1038
+ Attribute :: Underlined ,
1039
+ Attribute :: BlinkFast ,
1040
+ Attribute :: Hidden ,
1041
+ ] ,
1042
+ & [
1043
+ Attribute :: Dim ,
1044
+ Attribute :: Italic ,
1045
+ Attribute :: Blink ,
1046
+ Attribute :: Reverse ,
1047
+ Attribute :: StrikeThrough ,
1048
+ ] ,
1049
+ & Attributes :: ATTRIBUTES_LOOKUP ,
1050
+ ] ;
1051
+ for test_attrs in tests {
1052
+ let mut attrs = Attributes :: new ( ) ;
1053
+ for attr in test_attrs {
1054
+ attrs = attrs. insert ( * attr) ;
1055
+ }
1056
+ assert_eq ! (
1057
+ attrs. bits( ) . collect:: <Vec <_>>( ) ,
1058
+ test_attrs
1059
+ . iter( )
1060
+ . map( |attr| * attr as u16 )
1061
+ . collect:: <Vec <_>>( )
1062
+ ) ;
1063
+ assert_eq ! (
1064
+ attrs. ansi_nums( ) . collect:: <Vec <_>>( ) ,
1065
+ test_attrs
1066
+ . iter( )
1067
+ . map( |attr| * attr as u16 + 1 )
1068
+ . collect:: <Vec <_>>( )
1069
+ ) ;
1070
+ assert_eq ! ( & attrs. attrs( ) . collect:: <Vec <_>>( ) , test_attrs) ;
1071
+ }
1072
+ }
0 commit comments