use std::fmt;
use std::fmt::Display;
use std::fmt::Formatter;
use crate::codemap::FileSpan;
use crate::fast_string;
use crate::fast_string::CharIndex;
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct Frame {
pub name: String,
pub location: Option<FileSpan>,
}
impl Display for Frame {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str(&self.name)?;
if let Some(loc) = &self.location {
write!(f, " (called from {})", loc)?;
}
Ok(())
}
}
fn truncate_snippet(snippet: &str, max_len: usize) -> (&str, &str) {
let ddd = "...";
assert!(max_len >= ddd.len());
match fast_string::split_at(snippet, CharIndex(max_len - ddd.len())) {
None => (snippet, ""),
Some((_, b)) if b.chars().nth(3).is_none() => (snippet, ""),
Some((a, _)) => (a, "..."),
}
}
impl Frame {
pub fn write_two_lines(
&self,
indent: &str,
caller: &str,
write: &mut dyn fmt::Write,
) -> fmt::Result {
if let Some(location) = &self.location {
let line = location
.file
.source_line_at_pos(location.span.begin())
.trim();
let (line, ddd) = truncate_snippet(line, 80);
writeln!(
write,
"{}* {}, in {}",
indent,
location.resolve().begin_file_line(),
caller,
)?;
writeln!(write, "{} {}{}", indent, line, ddd)?;
} else {
writeln!(write, "{}File <builtin>, in {}", indent, caller)?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use crate::frame::truncate_snippet;
#[test]
fn test_truncate_snippet() {
assert_eq!(("", ""), truncate_snippet("", 5));
assert_eq!(("a", ""), truncate_snippet("a", 5));
assert_eq!(("ab", ""), truncate_snippet("ab", 5));
assert_eq!(("abc", ""), truncate_snippet("abc", 5));
assert_eq!(("abcd", ""), truncate_snippet("abcd", 5));
assert_eq!(("abcde", ""), truncate_snippet("abcde", 5));
assert_eq!(("ab", "..."), truncate_snippet("abcdef", 5));
assert_eq!(("ab", "..."), truncate_snippet("abcdefg", 5));
assert_eq!(("ab", "..."), truncate_snippet("abcdefgh", 5));
assert_eq!(("ab", "..."), truncate_snippet("abcdefghi", 5));
assert_eq!(("Київ", ""), truncate_snippet("Київ", 5));
assert_eq!(("па", "..."), truncate_snippet("паляниця", 5));
}
}