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
use std::fmt::{self, Write};
use rustc_span::Symbol;
/// A builder that allows efficiently and easily constructing the part of a URL
/// after the domain: `nightly/core/str/struct.Bytes.html`.
///
/// This type is a wrapper around the final `String` buffer,
/// but its API is like that of a `Vec` of URL components.
#[derive(Debug)]
pub(crate) struct UrlPartsBuilder {
buf: String,
}
impl UrlPartsBuilder {
/// Create an empty buffer.
#[allow(dead_code)]
pub(crate) fn new() -> Self {
Self { buf: String::new() }
}
/// Create an empty buffer with capacity for the specified number of bytes.
fn with_capacity_bytes(count: usize) -> Self {
Self { buf: String::with_capacity(count) }
}
/// Create a buffer with one URL component.
///
/// # Examples
///
/// Basic usage:
///
/// ```ignore (private-type)
/// let builder = UrlPartsBuilder::singleton("core");
/// assert_eq!(builder.finish(), "core");
/// ```
///
/// Adding more components afterward.
///
/// ```ignore (private-type)
/// let mut builder = UrlPartsBuilder::singleton("core");
/// builder.push("str");
/// builder.push_front("nightly");
/// assert_eq!(builder.finish(), "nightly/core/str");
/// ```
pub(crate) fn singleton(part: &str) -> Self {
Self { buf: part.to_owned() }
}
/// Push a component onto the buffer.
///
/// # Examples
///
/// Basic usage:
///
/// ```ignore (private-type)
/// let mut builder = UrlPartsBuilder::new();
/// builder.push("core");
/// builder.push("str");
/// builder.push("struct.Bytes.html");
/// assert_eq!(builder.finish(), "core/str/struct.Bytes.html");
/// ```
pub(crate) fn push(&mut self, part: &str) {
if !self.buf.is_empty() {
self.buf.push('/');
}
self.buf.push_str(part);
}
/// Push a component onto the buffer, using [`format!`]'s formatting syntax.
///
/// # Examples
///
/// Basic usage (equivalent to the example for [`UrlPartsBuilder::push`]):
///
/// ```ignore (private-type)
/// let mut builder = UrlPartsBuilder::new();
/// builder.push("core");
/// builder.push("str");
/// builder.push_fmt(format_args!("{}.{}.html", "struct", "Bytes"));
/// assert_eq!(builder.finish(), "core/str/struct.Bytes.html");
/// ```
pub(crate) fn push_fmt(&mut self, args: fmt::Arguments<'_>) {
if !self.buf.is_empty() {
self.buf.push('/');
}
self.buf.write_fmt(args).unwrap()
}
/// Push a component onto the front of the buffer.
///
/// # Examples
///
/// Basic usage:
///
/// ```ignore (private-type)
/// let mut builder = UrlPartsBuilder::new();
/// builder.push("core");
/// builder.push("str");
/// builder.push_front("nightly");
/// builder.push("struct.Bytes.html");
/// assert_eq!(builder.finish(), "nightly/core/str/struct.Bytes.html");
/// ```
pub(crate) fn push_front(&mut self, part: &str) {
let is_empty = self.buf.is_empty();
self.buf.reserve(part.len() + if !is_empty { 1 } else { 0 });
self.buf.insert_str(0, part);
if !is_empty {
self.buf.insert(part.len(), '/');
}
}
/// Get the final `String` buffer.
pub(crate) fn finish(self) -> String {
self.buf
}
}
/// This is just a guess at the average length of a URL part,
/// used for [`String::with_capacity`] calls in the [`FromIterator`]
/// and [`Extend`] impls, and for [estimating item path lengths].
///
/// The value `8` was chosen for two main reasons:
///
/// * It seems like a good guess for the average part length.
/// * jemalloc's size classes are all multiples of eight,
/// which means that the amount of memory it allocates will often match
/// the amount requested, avoiding wasted bytes.
///
/// [estimating item path lengths]: estimate_item_path_byte_length
const AVG_PART_LENGTH: usize = 8;
/// Estimate the number of bytes in an item's path, based on how many segments it has.
///
/// **Note:** This is only to be used with, e.g., [`String::with_capacity()`];
/// the return value is just a rough estimate.
pub(crate) const fn estimate_item_path_byte_length(segment_count: usize) -> usize {
AVG_PART_LENGTH * segment_count
}
impl<'a> FromIterator<&'a str> for UrlPartsBuilder {
fn from_iter<T: IntoIterator<Item = &'a str>>(iter: T) -> Self {
let iter = iter.into_iter();
let mut builder = Self::with_capacity_bytes(AVG_PART_LENGTH * iter.size_hint().0);
iter.for_each(|part| builder.push(part));
builder
}
}
impl<'a> Extend<&'a str> for UrlPartsBuilder {
fn extend<T: IntoIterator<Item = &'a str>>(&mut self, iter: T) {
let iter = iter.into_iter();
self.buf.reserve(AVG_PART_LENGTH * iter.size_hint().0);
iter.for_each(|part| self.push(part));
}
}
impl FromIterator<Symbol> for UrlPartsBuilder {
fn from_iter<T: IntoIterator<Item = Symbol>>(iter: T) -> Self {
// This code has to be duplicated from the `&str` impl because of
// `Symbol::as_str`'s lifetimes.
let iter = iter.into_iter();
let mut builder = Self::with_capacity_bytes(AVG_PART_LENGTH * iter.size_hint().0);
iter.for_each(|part| builder.push(part.as_str()));
builder
}
}
impl Extend<Symbol> for UrlPartsBuilder {
fn extend<T: IntoIterator<Item = Symbol>>(&mut self, iter: T) {
// This code has to be duplicated from the `&str` impl because of
// `Symbol::as_str`'s lifetimes.
let iter = iter.into_iter();
self.buf.reserve(AVG_PART_LENGTH * iter.size_hint().0);
iter.for_each(|part| self.push(part.as_str()));
}
}
#[cfg(test)]
mod tests;