refactor(codegen)!: Codegen::into_source_text consume Codegen (#6539)

Breaking change. `Codegen::into_source_text` consume `Codegen`, instead of taking the `CodeBuffer` and substituting an empty one.

Keeping the `Codegen` alive seems unintuitive, as its state is then out of sync. Consuming the `CodeBuffer` is also marginally cheaper.
This commit is contained in:
overlookmotel 2024-10-14 01:09:03 +00:00
parent 7e909a7e6c
commit c0e9d7eb77
2 changed files with 21 additions and 42 deletions

View file

@ -1,5 +1,3 @@
use std::mem;
use assert_unchecked::assert_unchecked;
/// A string builder for constructing source code.
@ -8,8 +6,7 @@ use assert_unchecked::assert_unchecked;
/// Essentially same as `String` but with additional methods.
///
/// Use one of the various `print_*` methods to add text into the buffer.
/// When you are done, call [`take_source_text`] or `String::from(code_buffer)`
/// to extract the final [`String`].
/// When you are done, call [`into_string`] to extract the final [`String`].
///
/// # Example
/// ```
@ -26,10 +23,10 @@ use assert_unchecked::assert_unchecked;
/// code.print_str(" console.log('Hello, world!');\n");
/// code.print_str("}\n");
///
/// let source = code.take_source_text();
/// let source = code.into_string();
/// ```
///
/// [`take_source_text`]: CodeBuffer::take_source_text
/// [`into_string`]: CodeBuffer::into_string
#[derive(Debug, Default, Clone)]
pub struct CodeBuffer {
/// INVARIANT: `buf` is a valid UTF-8 string.
@ -46,7 +43,7 @@ impl CodeBuffer {
///
/// // use `code` to build new source text
/// code.print_str("fn main() { println!(\"Hello, world!\"); }");
/// let source_text = code.take_source_text();
/// let source_text = code.into_string();
/// ```
#[inline]
pub fn new() -> Self {
@ -187,7 +184,7 @@ impl CodeBuffer {
/// code.print_ascii_byte(b'o');
/// code.print_ascii_byte(b'o');
///
/// let source = code.take_source_text();
/// let source = code.into_string();
/// assert_eq!(source, "foo");
/// ```
#[inline]
@ -212,7 +209,7 @@ impl CodeBuffer {
///
/// It is safe for a single call to temporarily result in invalid UTF-8, as long as
/// UTF-8 integrity is restored before calls to any other `print_*` method or
/// [`take_source_text`]. This lets you, for example, print an 4-byte Unicode character
/// [`into_string`]. This lets you, for example, print an 4-byte Unicode character
/// using 4 separate calls to this method. However, consider using [`print_bytes_unchecked`]
/// instead for that use case.
///
@ -237,7 +234,7 @@ impl CodeBuffer {
///
/// [`print_ascii_byte`]: CodeBuffer::print_ascii_byte
/// [`print_char`]: CodeBuffer::print_char
/// [`take_source_text`]: CodeBuffer::take_source_text
/// [`into_string`]: CodeBuffer::into_string
/// [`print_bytes_unchecked`]: CodeBuffer::print_bytes_unchecked
#[inline]
pub unsafe fn print_byte_unchecked(&mut self, byte: u8) {
@ -382,12 +379,7 @@ impl CodeBuffer {
&self.buf
}
/// Convert a buffer into a string of source code, leaving its internal buffer empty.
///
/// It is safe to re-use a `CodeBuffer` after calling this method, but there is little benefit
/// to doing so, as the `CodeBuffer` will be left in an empty state with no backing allocation.
///
/// You may alternatively use `String::from(code_buffer)`, which may be slightly more efficient.
/// Consume buffer and return source code as a `String`.
///
/// # Example
/// ```
@ -395,18 +387,17 @@ impl CodeBuffer {
/// let mut code = CodeBuffer::new();
/// code.print_str("console.log('foo');");
///
/// let source = code.take_source_text();
/// let source = code.into_string();
/// assert_eq!(source, "console.log('foo');");
/// assert!(code.is_empty());
/// ```
#[must_use]
#[inline]
pub fn take_source_text(&mut self) -> String {
pub fn into_string(self) -> String {
if cfg!(debug_assertions) {
String::from_utf8(mem::take(&mut self.buf)).unwrap()
String::from_utf8(self.buf).unwrap()
} else {
// SAFETY: All methods of `CodeBuffer` ensure `buf` is valid UTF-8
unsafe { String::from_utf8_unchecked(mem::take(&mut self.buf)) }
unsafe { String::from_utf8_unchecked(self.buf) }
}
}
}
@ -420,13 +411,8 @@ impl AsRef<[u8]> for CodeBuffer {
impl From<CodeBuffer> for String {
#[inline]
fn from(buffer: CodeBuffer) -> Self {
if cfg!(debug_assertions) {
String::from_utf8(buffer.buf).unwrap()
} else {
// SAFETY: All methods of `CodeBuffer` ensure `buf` is valid UTF-8
unsafe { String::from_utf8_unchecked(buffer.buf) }
}
fn from(code: CodeBuffer) -> Self {
code.into_string()
}
}
@ -452,20 +438,13 @@ mod test {
}
#[test]
fn into_source_string() {
fn into_string() {
let s = "Hello, world!";
let mut code = CodeBuffer::with_capacity(s.len());
code.print_str(s);
let source = code.take_source_text();
let source = code.into_string();
assert_eq!(source, s);
// buffer has been emptied
assert!(code.is_empty());
assert_eq!(code.len(), 0);
let empty_slice: &[u8] = &[];
assert_eq!(code.as_bytes(), empty_slice);
assert_eq!(String::from(code), "");
}
#[test]

View file

@ -151,13 +151,13 @@ impl<'a> Default for Codegen<'a> {
}
impl<'a> From<Codegen<'a>> for String {
fn from(mut val: Codegen<'a>) -> Self {
fn from(val: Codegen<'a>) -> Self {
val.into_source_text()
}
}
impl<'a> From<Codegen<'a>> for Cow<'a, str> {
fn from(mut val: Codegen<'a>) -> Self {
fn from(val: Codegen<'a>) -> Self {
Cow::Owned(val.into_source_text())
}
}
@ -215,14 +215,14 @@ impl<'a> Codegen<'a> {
}
program.print(&mut self, Context::default());
let code = self.into_source_text();
let code = self.code.into_string();
let map = self.sourcemap_builder.map(SourcemapBuilder::into_sourcemap);
CodegenReturn { code, map }
}
#[must_use]
pub fn into_source_text(&mut self) -> String {
self.code.take_source_text()
pub fn into_source_text(self) -> String {
self.code.into_string()
}
/// Push a single ASCII byte into the buffer.