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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
/*!
Semester is a declarative CSS conditional class name joiner, in the style of
[React]'s [classnames]. It's intended for use in web frameworks (like [Yew])
and HTML template engines (like [horrorshow]) as an efficient and compile-time
checked way to conditionally activate or deactivate CSS classes on HTML
elements.
Semester provides two similar macros, [`classes`] and [`static_classes`], for
creating sets of classes. Each one has a slightly different mode of operation,
but they both use the same simple syntax and perform the same checks and
guarantees:
- The macro takes a list of CSS classes as input, and returns an
`impl `[`Classes`]:
```rust
use semester::{classes, Classes as _};
let classes = classes!(
"class1",
"class2",
"class3"
);
assert_eq!(classes.render(), "class1 class2 class3");
```
- Each class may optionally include a condition:
```rust
use semester::{classes, Classes as _};
let classes = classes!(
"always",
"yup": 10 == 10,
"nope": 10 == 15,
);
assert_eq!(classes.render(), "always yup");
```
`semester` will render all of the enabled classes in declaration order,
separated by a single space. It will do its best to pre-compute parts of the
output (for instance, by concatenating all the consecutive unconditional
classes), and you can go further and use [`static_classes`], which pre-computes
every possible combination of classes at compile time.
Besides `render`, `semester` provides several other ways to access the class
set, so you can use whichever one makes the most sense for your use case. See
the [`Classes`] and [`StaticClasses`] traits for details. `semester` is
`no_std` and will generally only allocate on specific methods like `render`
and `to_string`.
Additionally, `semester` performs several compile time correctness checks on
your classes:
- Classes must be made up of ascii printable characters:
```compile_fail
use semester::classes;
classes!("null\0class")
```
- Classes must not have any whitespace:
```compile_fail
use semester::classes;
classes!("class pair")
```
- Classes must not be empty:
```compile_fail
use semester::classes;
classes!("")
```
- Classes should exclude the HTML unsafe characters: `<` `>` `&` `'` `"`
```compile_fail
use semester::classes;
classes!("<injected-class>")
```
- Classes may not duplicate. Note that `semester` can't detect mutually
exclusive conditions, so it prevents duplicates unconditionally.
```compile_fail
use semester::classes;
let x = 10;
classes!(
"class1": x == 10,
"class1": x != 10,
);
```
[React]: https://reactjs.org/
[classnames]: https://jedwatson.github.io/classnames/
[yew]: https://docs.rs/yew/
[horrorshow]: https://docs.rs/horrorshow/
*/
extern crate alloc;
use ;
use Cow;
pub use ;
/**
Create a set of classes dynamically.
The `classes` macro creates a set of classes dynamically; it's similar to
a series of consecutive `.push_str` calls on a string. When called, it eagerly
evaluates all the conditions for its classes and stores them (as `bool`s) in an
`impl `[`Classes`], and when that `Classes` is rendered (via
[`render`][Classes::render], [`Display`], etc), the list of conditions is
consulted to create the final rendered output.
This macro will try to do as much work ahead of time as possible; in particular,
it will concatenate all consecutive *unconditional* classes into a
`&'static str` during compilation, and it will return an
`impl `[`StaticClasses`] if it is *only* given unconditional classes. See
also [`static_classes`], which *always* produces an `&'static str`.
# Example
```rust
use semester::{classes, Classes};
fn get_classes(b1: bool, b2: bool) -> impl Classes {
classes!(
"class1": b1,
"class2": b2,
"both": b1 && b2
)
}
assert_eq!(get_classes(false, false).render(), "");
assert_eq!(get_classes(false, false).try_as_str(), Some(""));
assert_eq!(get_classes(false, true).render(), "class2");
assert_eq!(get_classes(true, true).render(), "class1 class2 both");
```
*/
}
/**
Create a set of classes statically.
The `static_classes` macro creates a set of classes as a `&'static str` by
pre-computing every possible combination of the conditional classes. When
called, it eagerly evaluates all the conditions for its classes and selects
a pre-rendered string containing all the enabled classes and returns them in
an `impl `[`StaticClasses`] (which automatically also implements [`Classes`]).
Note that every additional class will *double* the number of pre-computed class
strings, especially because `semester` can't reason about mutually exclusive
or other unreachable combinations, so you should only use it if you have a small
number of conditional classes, or when every single possible combination of
classes is viable.
# Example
```rust
use semester::{static_classes, StaticClasses};
fn get_classes(b1: bool, b2: bool, b3: bool) -> impl StaticClasses {
static_classes!(
"always1",
"always2",
"class1": b1,
"class2": b2,
"class3": b3,
"all3": b1 && b2 && b3,
)
}
assert_eq!(
get_classes(false, false, false).as_str(),
"always1 always2"
);
assert_eq!(
get_classes(true, false, true).as_str(),
"always1 always2 class1 class3"
);
assert_eq!(
get_classes(true, true, true).as_str(),
"always1 always2 class1 class2 class3 all3"
);
```
*/
}
/**
A `Classes` is a dynamically computed set of CSS classes.
Note that, in addition to the methods here, `Classes` types also implement
`Display`, plus a handful of other common traits.
See also [`StaticClasses`], which is returned when the set of classes is
known unconditionally at compile time and provides additional infallible
methods.
*/
/**
A `StaticClasses` is a pre-computed set of CSS classes that is available
unconditionally as a `&'static str`. All `StaticClasses` types automatically
implement `Classes`.
*/
+ Classes