use std::fmt;
use unicode_width::UnicodeWidthStr;
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub struct Span {
pub start: u32,
pub end: u32,
}
impl Span {
#[inline]
pub const fn len(self) -> usize {
(self.end - self.start) as usize
}
#[inline]
pub const fn is_empty(self) -> bool {
self.start == self.end
}
}
pub struct FormatArena {
buf: String,
}
impl FormatArena {
pub fn with_capacity(cap: usize) -> Self {
Self {
buf: String::with_capacity(cap),
}
}
pub fn new() -> Self {
Self::with_capacity(4096)
}
pub fn format<F>(&mut self, f: F) -> (Span, usize)
where
F: FnOnce(&mut String) -> fmt::Result,
{
let start = self.buf.len();
f(&mut self.buf).expect("formatting to String cannot fail");
let end = self.buf.len();
let span = Span {
start: start as u32,
end: end as u32,
};
let width = self.buf[start..end].width();
(span, width)
}
pub fn push_str(&mut self, s: &str) -> (Span, usize) {
let start = self.buf.len();
self.buf.push_str(s);
let span = Span {
start: start as u32,
end: self.buf.len() as u32,
};
let width = s.width();
(span, width)
}
#[inline]
pub fn get(&self, span: Span) -> &str {
&self.buf[span.start as usize..span.end as usize]
}
pub const fn len(&self) -> usize {
self.buf.len()
}
pub const fn is_empty(&self) -> bool {
self.buf.is_empty()
}
}
impl Default for FormatArena {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use std::fmt::Write;
use super::*;
#[test]
fn test_format_simple() {
let mut arena = FormatArena::new();
let (span, width) = arena.format(|w| write!(w, "hello"));
assert_eq!(arena.get(span), "hello");
assert_eq!(width, 5);
}
#[test]
fn test_format_multiple() {
let mut arena = FormatArena::new();
let (s1, w1) = arena.format(|w| write!(w, "hello"));
let (s2, w2) = arena.format(|w| write!(w, "world"));
assert_eq!(arena.get(s1), "hello");
assert_eq!(arena.get(s2), "world");
assert_eq!(w1, 5);
assert_eq!(w2, 5);
assert_eq!(s1.end, s2.start);
}
#[test]
fn test_format_unicode() {
let mut arena = FormatArena::new();
let (span, width) = arena.format(|w| write!(w, "日本語"));
assert_eq!(arena.get(span), "日本語");
assert_eq!(width, 6);
let (span, width) = arena.format(|w| write!(w, "🦀"));
assert_eq!(arena.get(span), "🦀");
assert_eq!(width, 2); }
#[test]
fn test_push_str() {
let mut arena = FormatArena::new();
let (span, width) = arena.push_str("test");
assert_eq!(arena.get(span), "test");
assert_eq!(width, 4);
}
#[test]
fn test_span_len() {
let span = Span { start: 10, end: 20 };
assert_eq!(span.len(), 10);
assert!(!span.is_empty());
let empty = Span { start: 5, end: 5 };
assert_eq!(empty.len(), 0);
assert!(empty.is_empty());
}
}