1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
// devela::sys::os::term::grid::style
//
//! Defines [`TermStyle`] and [`TermStyleExt`].
//
crate::set! {
#[doc = crate::_tags!(term text set)]
/// A compact set of broadly supported terminal text styles.
#[doc = crate::_doc_meta!{
location("sys/os/term"),
test_size_of(TermStyle = 1|8),
}]
///
/// Its bits occupy the low byte of [`TermStyleExt`] with the same meanings.
#[must_use]
pub struct TermStyle(u8) {
/// Increased intensity.
BOLD = 0;
/// Italic text.
ITALIC = 1;
/// Underlined text.
UNDERLINE = 2;
/// Reduced intensity.
DIM = 3;
/// Blinking text.
BLINK = 4;
/// Swapped foreground and background.
INVERSE = 5;
/// Concealed text.
HIDDEN = 6;
/// Crossed-out text.
CROSSED = 7;
}
impl {
/// Returns the corresponding extended style set.
pub const fn extended(self) -> TermStyleExt {
TermStyleExt::from_bits(self.bits as u16)
}
}
}
crate::set! {
#[doc = crate::_tags!(term text set)]
/// A complete set of terminal text styles.
#[doc = crate::_doc_meta!{
location("sys/os/term"),
test_size_of(TermStyleExt = 2|16),
}]
///
/// The low byte has the same representation as [`TermStyle`].
/// Styles in the high byte generally have more limited terminal support.
#[must_use]
pub struct TermStyleExt(u16) {
/* common low byte */
/// Increased intensity.
BOLD = 0;
/// Italic text.
ITALIC = 1;
/// Underlined text.
UNDERLINE = 2;
/// Reduced intensity.
DIM = 3;
/// Blinking text.
BLINK = 4;
/// Swapped foreground and background.
INVERSE = 5;
/// Concealed text.
HIDDEN = 6;
/// Crossed-out text.
CROSSED = 7;
/* extended high byte */
/// Rapidly blinking text.
BLINK_FAST = 8;
/// Fraktur text.
FRAKTUR = 9;
/// Doubly underlined text.
UNDERLINE_DOUBLE = 10;
/// Framed text.
FRAMED = 11;
/// Encircled text.
ENCIRCLED = 12;
/// Overlined text.
OVERLINE = 13;
/// Superscript text.
SUPERSCRIPT = 14;
/// Subscript text.
SUBSCRIPT = 15;
/// All basic styles.
BASIC = 0..=7;
/// All extended styles.
EXTENDED = 8..=15;
}
impl {
/// Returns the basic styles.
pub const fn basic(self) -> TermStyle { TermStyle::from_bits(self.bits as u8) }
/// Returns whether no extended styles are enabled.
#[must_use]
pub const fn is_basic(self) -> bool { !self.intersects(Self::EXTENDED) }
/// Returns the basic style set when no extended styles are enabled.
#[must_use]
pub const fn try_basic(self) -> Option<TermStyle> {
if self.is_basic() { Some(self.basic()) } else { None }
}
/// Returns the enabled extended-only styles.
pub const fn extended_only(self) -> Self { self.intersection(Self::EXTENDED) }
}
}
impl From<TermStyle> for TermStyleExt {
fn from(style: TermStyle) -> Self {
style.extended()
}
}
#[cfg(test)]
mod tests {
use super::{TermStyle as S, TermStyleExt as X};
#[test]
fn sizes() {
assert_eq!(size_of::<S>(), 1);
assert_eq!(size_of::<X>(), 2);
assert_eq!(size_of::<Option<S>>(), 2);
assert_eq!(size_of::<Option<X>>(), 4);
}
#[test]
fn basic_layout_matches() {
assert_eq!(S::all().extended(), X::BASIC);
assert_eq!(S::BOLD.extended(), X::BOLD);
assert_eq!(S::CROSSED.extended(), X::CROSSED);
}
#[test]
fn set_operations() {
let style = S::BOLD | S::ITALIC | S::UNDERLINE;
assert!(style.contains(S::BOLD | S::ITALIC));
assert!(style.intersects(S::UNDERLINE));
assert!(!style.intersects(S::BLINK));
assert_eq!(style.without(S::ITALIC), S::BOLD | S::UNDERLINE);
}
#[test]
fn extended_partition() {
let style = X::BOLD | X::FRAMED | X::OVERLINE;
assert_eq!(style.basic(), S::BOLD);
assert_eq!(style.extended_only(), X::FRAMED | X::OVERLINE);
assert!(!style.is_basic());
assert_eq!(style.try_basic(), None);
let basic = X::BOLD | X::UNDERLINE;
assert!(basic.is_basic());
assert_eq!(basic.try_basic(), Some(S::BOLD | S::UNDERLINE));
}
#[test]
fn style_ranges() {
assert_eq!(X::BASIC.bits(), 0x00FF);
assert_eq!(X::EXTENDED.bits(), 0xFF00);
assert_eq!((X::BASIC | X::EXTENDED).bits(), u16::MAX);
assert!(!X::BASIC.intersects(X::EXTENDED));
}
}