]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/html/url_parts_builder.rs
Auto merge of #107738 - matthiaskrgr:rollup-o18lzi8, r=matthiaskrgr
[rust.git] / src / librustdoc / html / url_parts_builder.rs
1 use std::fmt::{self, Write};
2
3 use rustc_span::Symbol;
4
5 /// A builder that allows efficiently and easily constructing the part of a URL
6 /// after the domain: `nightly/core/str/struct.Bytes.html`.
7 ///
8 /// This type is a wrapper around the final `String` buffer,
9 /// but its API is like that of a `Vec` of URL components.
10 #[derive(Debug)]
11 pub(crate) struct UrlPartsBuilder {
12     buf: String,
13 }
14
15 impl UrlPartsBuilder {
16     /// Create an empty buffer.
17     #[allow(dead_code)]
18     pub(crate) fn new() -> Self {
19         Self { buf: String::new() }
20     }
21
22     /// Create an empty buffer with capacity for the specified number of bytes.
23     fn with_capacity_bytes(count: usize) -> Self {
24         Self { buf: String::with_capacity(count) }
25     }
26
27     /// Create a buffer with one URL component.
28     ///
29     /// # Examples
30     ///
31     /// Basic usage:
32     ///
33     /// ```ignore (private-type)
34     /// let builder = UrlPartsBuilder::singleton("core");
35     /// assert_eq!(builder.finish(), "core");
36     /// ```
37     ///
38     /// Adding more components afterward.
39     ///
40     /// ```ignore (private-type)
41     /// let mut builder = UrlPartsBuilder::singleton("core");
42     /// builder.push("str");
43     /// builder.push_front("nightly");
44     /// assert_eq!(builder.finish(), "nightly/core/str");
45     /// ```
46     pub(crate) fn singleton(part: &str) -> Self {
47         Self { buf: part.to_owned() }
48     }
49
50     /// Push a component onto the buffer.
51     ///
52     /// # Examples
53     ///
54     /// Basic usage:
55     ///
56     /// ```ignore (private-type)
57     /// let mut builder = UrlPartsBuilder::new();
58     /// builder.push("core");
59     /// builder.push("str");
60     /// builder.push("struct.Bytes.html");
61     /// assert_eq!(builder.finish(), "core/str/struct.Bytes.html");
62     /// ```
63     pub(crate) fn push(&mut self, part: &str) {
64         if !self.buf.is_empty() {
65             self.buf.push('/');
66         }
67         self.buf.push_str(part);
68     }
69
70     /// Push a component onto the buffer, using [`format!`]'s formatting syntax.
71     ///
72     /// # Examples
73     ///
74     /// Basic usage (equivalent to the example for [`UrlPartsBuilder::push`]):
75     ///
76     /// ```ignore (private-type)
77     /// let mut builder = UrlPartsBuilder::new();
78     /// builder.push("core");
79     /// builder.push("str");
80     /// builder.push_fmt(format_args!("{}.{}.html", "struct", "Bytes"));
81     /// assert_eq!(builder.finish(), "core/str/struct.Bytes.html");
82     /// ```
83     pub(crate) fn push_fmt(&mut self, args: fmt::Arguments<'_>) {
84         if !self.buf.is_empty() {
85             self.buf.push('/');
86         }
87         self.buf.write_fmt(args).unwrap()
88     }
89
90     /// Push a component onto the front of the buffer.
91     ///
92     /// # Examples
93     ///
94     /// Basic usage:
95     ///
96     /// ```ignore (private-type)
97     /// let mut builder = UrlPartsBuilder::new();
98     /// builder.push("core");
99     /// builder.push("str");
100     /// builder.push_front("nightly");
101     /// builder.push("struct.Bytes.html");
102     /// assert_eq!(builder.finish(), "nightly/core/str/struct.Bytes.html");
103     /// ```
104     pub(crate) fn push_front(&mut self, part: &str) {
105         let is_empty = self.buf.is_empty();
106         self.buf.reserve(part.len() + if !is_empty { 1 } else { 0 });
107         self.buf.insert_str(0, part);
108         if !is_empty {
109             self.buf.insert(part.len(), '/');
110         }
111     }
112
113     /// Get the final `String` buffer.
114     pub(crate) fn finish(self) -> String {
115         self.buf
116     }
117 }
118
119 /// This is just a guess at the average length of a URL part,
120 /// used for [`String::with_capacity`] calls in the [`FromIterator`]
121 /// and [`Extend`] impls, and for [estimating item path lengths].
122 ///
123 /// The value `8` was chosen for two main reasons:
124 ///
125 /// * It seems like a good guess for the average part length.
126 /// * jemalloc's size classes are all multiples of eight,
127 ///   which means that the amount of memory it allocates will often match
128 ///   the amount requested, avoiding wasted bytes.
129 ///
130 /// [estimating item path lengths]: estimate_item_path_byte_length
131 const AVG_PART_LENGTH: usize = 8;
132
133 /// Estimate the number of bytes in an item's path, based on how many segments it has.
134 ///
135 /// **Note:** This is only to be used with, e.g., [`String::with_capacity()`];
136 /// the return value is just a rough estimate.
137 pub(crate) const fn estimate_item_path_byte_length(segment_count: usize) -> usize {
138     AVG_PART_LENGTH * segment_count
139 }
140
141 impl<'a> FromIterator<&'a str> for UrlPartsBuilder {
142     fn from_iter<T: IntoIterator<Item = &'a str>>(iter: T) -> Self {
143         let iter = iter.into_iter();
144         let mut builder = Self::with_capacity_bytes(AVG_PART_LENGTH * iter.size_hint().0);
145         iter.for_each(|part| builder.push(part));
146         builder
147     }
148 }
149
150 impl<'a> Extend<&'a str> for UrlPartsBuilder {
151     fn extend<T: IntoIterator<Item = &'a str>>(&mut self, iter: T) {
152         let iter = iter.into_iter();
153         self.buf.reserve(AVG_PART_LENGTH * iter.size_hint().0);
154         iter.for_each(|part| self.push(part));
155     }
156 }
157
158 impl FromIterator<Symbol> for UrlPartsBuilder {
159     fn from_iter<T: IntoIterator<Item = Symbol>>(iter: T) -> Self {
160         // This code has to be duplicated from the `&str` impl because of
161         // `Symbol::as_str`'s lifetimes.
162         let iter = iter.into_iter();
163         let mut builder = Self::with_capacity_bytes(AVG_PART_LENGTH * iter.size_hint().0);
164         iter.for_each(|part| builder.push(part.as_str()));
165         builder
166     }
167 }
168
169 impl Extend<Symbol> for UrlPartsBuilder {
170     fn extend<T: IntoIterator<Item = Symbol>>(&mut self, iter: T) {
171         // This code has to be duplicated from the `&str` impl because of
172         // `Symbol::as_str`'s lifetimes.
173         let iter = iter.into_iter();
174         self.buf.reserve(AVG_PART_LENGTH * iter.size_hint().0);
175         iter.for_each(|part| self.push(part.as_str()));
176     }
177 }
178
179 #[cfg(test)]
180 mod tests;