1 use std::fmt::{self, Write};
3 use rustc_span::Symbol;
5 /// A builder that allows efficiently and easily constructing the part of a URL
6 /// after the domain: `nightly/core/str/struct.Bytes.html`.
8 /// This type is a wrapper around the final `String` buffer,
9 /// but its API is like that of a `Vec` of URL components.
11 crate struct UrlPartsBuilder {
15 impl UrlPartsBuilder {
16 /// Create an empty buffer.
18 crate fn new() -> Self {
19 Self { buf: String::new() }
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) }
27 /// Create a buffer with one URL component.
33 /// ```ignore (private-type)
34 /// let builder = UrlPartsBuilder::singleton("core");
35 /// assert_eq!(builder.finish(), "core");
38 /// Adding more components afterward.
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");
46 crate fn singleton(part: &str) -> Self {
47 Self { buf: part.to_owned() }
50 /// Push a component onto the buffer.
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");
63 crate fn push(&mut self, part: &str) {
64 if !self.buf.is_empty() {
67 self.buf.push_str(part);
70 /// Push a component onto the buffer, using [`format!`]'s formatting syntax.
74 /// Basic usage (equivalent to the example for [`UrlPartsBuilder::push`]):
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");
83 crate fn push_fmt(&mut self, args: fmt::Arguments<'_>) {
84 if !self.buf.is_empty() {
87 self.buf.write_fmt(args).unwrap()
90 /// Push a component onto the front of the buffer.
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");
104 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);
109 self.buf.insert(part.len(), '/');
113 /// Get the final `String` buffer.
114 crate fn finish(self) -> String {
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].
123 /// The value `8` was chosen for two main reasons:
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.
130 /// [estimating item path lengths]: estimate_item_path_byte_length
131 const AVG_PART_LENGTH: usize = 8;
133 /// Estimate the number of bytes in an item's path, based on how many segments it has.
135 /// **Note:** This is only to be used with, e.g., [`String::with_capacity()`];
136 /// the return value is just a rough estimate.
137 crate const fn estimate_item_path_byte_length(segment_count: usize) -> usize {
138 AVG_PART_LENGTH * segment_count
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));
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));
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()));
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()));