forked from rust-lang/rust
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlength_limit.rs
More file actions
94 lines (81 loc) · 2.83 KB
/
length_limit.rs
File metadata and controls
94 lines (81 loc) · 2.83 KB
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
//! See [`HtmlWithLimit`].
use std::fmt::Write;
use std::ops::ControlFlow;
use crate::html::escape::Escape;
/// A buffer that allows generating HTML with a length limit.
///
/// This buffer ensures that:
///
/// * all tags are closed,
/// * tags are closed in the reverse order of when they were opened (i.e., the correct HTML order),
/// * no tags are left empty (e.g., `<em></em>`) due to the length limit being reached,
/// * all text is escaped.
#[derive(Debug)]
pub(super) struct HtmlWithLimit {
buf: String,
len: usize,
limit: usize,
/// A list of tags that have been requested to be opened via [`Self::open_tag()`]
/// but have not actually been pushed to `buf` yet. This ensures that tags are not
/// left empty (e.g., `<em></em>`) due to the length limit being reached.
queued_tags: Vec<&'static str>,
/// A list of all tags that have been opened but not yet closed.
unclosed_tags: Vec<&'static str>,
}
impl HtmlWithLimit {
/// Create a new buffer, with a limit of `length_limit`.
pub(super) fn new(length_limit: usize) -> Self {
Self {
buf: String::new(),
len: 0,
limit: length_limit,
unclosed_tags: Vec::new(),
queued_tags: Vec::new(),
}
}
/// Finish using the buffer and get the written output.
/// This function will close all unclosed tags for you.
pub(super) fn finish(mut self) -> String {
self.close_all_tags();
self.buf
}
/// Write some plain text to the buffer, escaping as needed.
///
/// This function skips writing the text if the length limit was reached
/// and returns [`ControlFlow::Break`].
pub(super) fn push(&mut self, text: &str) -> ControlFlow<(), ()> {
if self.len + text.len() > self.limit {
return ControlFlow::BREAK;
}
self.flush_queue();
write!(self.buf, "{}", Escape(text)).unwrap();
self.len += text.len();
ControlFlow::CONTINUE
}
/// Open an HTML tag.
pub(super) fn open_tag(&mut self, tag_name: &'static str) {
self.queued_tags.push(tag_name);
}
/// Close the most recently opened HTML tag.
pub(super) fn close_tag(&mut self) {
let tag_name = self.unclosed_tags.pop().unwrap();
self.buf.push_str("</");
self.buf.push_str(tag_name);
self.buf.push('>');
}
/// Write all queued tags and add them to the `unclosed_tags` list.
fn flush_queue(&mut self) {
for tag_name in self.queued_tags.drain(..) {
self.buf.push('<');
self.buf.push_str(tag_name);
self.buf.push('>');
self.unclosed_tags.push(tag_name);
}
}
/// Close all unclosed tags.
fn close_all_tags(&mut self) {
while !self.unclosed_tags.is_empty() {
self.close_tag();
}
}
}