1use serde::Serialize;
2use std::cell::RefCell;
3use std::fmt;
4
5thread_local! {
6 static DEBUG_SOURCE: RefCell<Option<&'static [u8]>> = const { RefCell::new(None) };
7}
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub struct LineInfo<'src> {
11 pub line: usize,
12 pub column: usize,
13 pub line_text: &'src [u8],
14}
15
16pub fn with_session_globals<F, R>(source: &[u8], f: F) -> R
19where
20 F: FnOnce() -> R,
21{
22 let source_static: &'static [u8] = unsafe { std::mem::transmute(source) };
26
27 DEBUG_SOURCE.with(|s| *s.borrow_mut() = Some(source_static));
28 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(f));
29 DEBUG_SOURCE.with(|s| *s.borrow_mut() = None);
30
31 match result {
32 Ok(r) => r,
33 Err(e) => std::panic::resume_unwind(e),
34 }
35}
36
37#[derive(Clone, Copy, PartialEq, Eq, Default, Hash, Serialize)]
38pub struct Span {
39 pub start: usize,
40 pub end: usize,
41}
42
43impl fmt::Debug for Span {
44 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45 let mut builder = f.debug_struct("Span");
46 builder.field("start", &self.start);
47 builder.field("end", &self.end);
48
49 DEBUG_SOURCE.with(|source_cell| {
50 if let Some(source) = *source_cell.borrow()
51 && self.start <= self.end
52 && self.end <= source.len()
53 {
54 let line = source[..self.start].iter().filter(|&&b| b == b'\n').count() + 1;
55 builder.field("line", &line);
56
57 let text = &source[self.start..self.end];
58 let text_str = String::from_utf8_lossy(text);
59 builder.field("text", &text_str);
60 }
61 });
62
63 builder.finish()
64 }
65}
66
67impl Span {
68 pub fn new(start: usize, end: usize) -> Self {
69 Self { start, end }
70 }
71
72 pub fn len(&self) -> usize {
73 self.end - self.start
74 }
75
76 pub fn is_empty(&self) -> bool {
77 self.start == self.end
78 }
79
80 pub fn line_info<'src>(&self, source: &'src [u8]) -> Option<LineInfo<'src>> {
81 if self.start > self.end || self.end > source.len() {
82 return None;
83 }
84
85 let line = source[..self.start].iter().filter(|&&b| b == b'\n').count() + 1;
86 let line_start = source[..self.start]
87 .iter()
88 .rposition(|b| *b == b'\n')
89 .map(|pos| pos + 1)
90 .unwrap_or(0);
91 let column = self.start - line_start + 1;
92
93 let line_end = source[self.start..]
94 .iter()
95 .position(|b| *b == b'\n')
96 .map(|pos| self.start + pos)
97 .unwrap_or(source.len());
98
99 Some(LineInfo {
100 line,
101 column,
102 line_text: &source[line_start..line_end],
103 })
104 }
105
106 pub fn as_str<'src>(&self, source: &'src [u8]) -> &'src [u8] {
110 &source[self.start..self.end]
111 }
112}