#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Slab {
pub text: String,
pub start: usize,
pub end: usize,
pub char_start: Option<usize>,
pub char_end: Option<usize>,
pub index: usize,
}
impl Slab {
#[must_use]
pub fn new(text: impl Into<String>, start: usize, end: usize, index: usize) -> Self {
debug_assert!(
start <= end,
"Slab start ({start}) must not exceed end ({end})"
);
Self {
text: text.into(),
start,
end,
char_start: None,
char_end: None,
index,
}
}
#[must_use]
pub fn with_char_offsets(mut self, char_start: usize, char_end: usize) -> Self {
self.char_start = Some(char_start);
self.char_end = Some(char_end);
self
}
#[must_use]
pub fn len(&self) -> usize {
self.text.len()
}
#[must_use]
pub fn char_len(&self) -> usize {
self.text.chars().count()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.text.is_empty()
}
#[must_use]
pub fn span(&self) -> std::ops::Range<usize> {
self.start..self.end
}
#[must_use]
pub fn char_span(&self) -> Option<std::ops::Range<usize>> {
match (self.char_start, self.char_end) {
(Some(s), Some(e)) => Some(s..e),
_ => None,
}
}
}
pub fn compute_char_offsets(text: &str, slabs: &mut [Slab]) {
if slabs.is_empty() {
return;
}
let mut byte_to_char = vec![0usize; text.len() + 1];
for (char_idx, (byte_idx, _)) in text.char_indices().enumerate() {
byte_to_char[byte_idx] = char_idx;
}
byte_to_char[text.len()] = text.chars().count();
for slab in slabs.iter_mut() {
slab.char_start = Some(byte_to_char[slab.start]);
slab.char_end = Some(byte_to_char[slab.end]);
}
}
impl std::fmt::Display for Slab {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let (Some(cs), Some(ce)) = (self.char_start, self.char_end) {
write!(
f,
"Slab {{ index: {}, bytes: {}..{}, chars: {}..{}, len: {} }}",
self.index,
self.start,
self.end,
cs,
ce,
self.len()
)
} else {
write!(
f,
"Slab {{ index: {}, span: {}..{}, len: {} }}",
self.index,
self.start,
self.end,
self.len()
)
}
}
}