use serde::Serialize;
use std::cell::RefCell;
use std::fmt;
thread_local! {
static DEBUG_SOURCE: RefCell<Option<&'static [u8]>> = const { RefCell::new(None) };
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LineInfo<'src> {
pub line: usize,
pub column: usize,
pub line_text: &'src [u8],
}
pub fn with_session_globals<F, R>(source: &[u8], f: F) -> R
where
F: FnOnce() -> R,
{
let source_static: &'static [u8] = unsafe { std::mem::transmute(source) };
DEBUG_SOURCE.with(|s| *s.borrow_mut() = Some(source_static));
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(f));
DEBUG_SOURCE.with(|s| *s.borrow_mut() = None);
match result {
Ok(r) => r,
Err(e) => std::panic::resume_unwind(e),
}
}
#[derive(Clone, Copy, PartialEq, Eq, Default, Hash, Serialize)]
pub struct Span {
pub start: usize,
pub end: usize,
}
impl fmt::Debug for Span {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut builder = f.debug_struct("Span");
builder.field("start", &self.start);
builder.field("end", &self.end);
DEBUG_SOURCE.with(|source_cell| {
if let Some(source) = *source_cell.borrow()
&& self.start <= self.end
&& self.end <= source.len()
{
let line = source[..self.start].iter().filter(|&&b| b == b'\n').count() + 1;
builder.field("line", &line);
let text = &source[self.start..self.end];
let text_str = String::from_utf8_lossy(text);
builder.field("text", &text_str);
}
});
builder.finish()
}
}
impl Span {
pub fn new(start: usize, end: usize) -> Self {
Self { start, end }
}
pub fn len(&self) -> usize {
self.end - self.start
}
pub fn is_empty(&self) -> bool {
self.start == self.end
}
pub fn line_info<'src>(&self, source: &'src [u8]) -> Option<LineInfo<'src>> {
if self.start > self.end || self.end > source.len() {
return None;
}
let line = source[..self.start].iter().filter(|&&b| b == b'\n').count() + 1;
let line_start = source[..self.start]
.iter()
.rposition(|b| *b == b'\n')
.map(|pos| pos + 1)
.unwrap_or(0);
let column = self.start - line_start + 1;
let line_end = source[self.start..]
.iter()
.position(|b| *b == b'\n')
.map(|pos| self.start + pos)
.unwrap_or(source.len());
Some(LineInfo {
line,
column,
line_text: &source[line_start..line_end],
})
}
pub fn as_str<'src>(&self, source: &'src [u8]) -> &'src [u8] {
&source[self.start..self.end]
}
}