just/src/misc.rs

179 lines
4.0 KiB
Rust
Raw Normal View History

2017-11-16 23:30:08 -08:00
use common::*;
use unicode_width::UnicodeWidthChar;
pub fn show_whitespace(text: &str) -> String {
text
.chars()
.map(|c| match c {
'\t' => '␉',
' ' => '␠',
_ => c,
})
.collect()
2017-11-16 23:30:08 -08:00
}
pub fn default<T: Default>() -> T {
Default::default()
}
pub fn empty<T, C: iter::FromIterator<T>>() -> C {
iter::empty().collect()
}
pub fn ticks<T: Display>(ts: &[T]) -> Vec<Tick<T>> {
ts.iter().map(Tick).collect()
}
pub fn maybe_s(n: usize) -> &'static str {
if n == 1 {
""
} else {
"s"
}
}
pub fn conjoin<T: Display>(
f: &mut fmt::Formatter,
values: &[T],
2017-11-16 23:30:08 -08:00
conjunction: &str,
) -> Result<(), fmt::Error> {
match values.len() {
0 => {}
1 => write!(f, "{}", values[0])?,
2 => write!(f, "{} {} {}", values[0], conjunction, values[1])?,
_ => {
for (i, item) in values.iter().enumerate() {
2017-11-16 23:30:08 -08:00
write!(f, "{}", item)?;
if i == values.len() - 1 {
} else if i == values.len() - 2 {
write!(f, ", {} ", conjunction)?;
} else {
write!(f, ", ")?
}
}
2017-11-16 23:30:08 -08:00
}
}
Ok(())
2017-11-16 23:30:08 -08:00
}
pub fn write_error_context(
f: &mut fmt::Formatter,
text: &str,
index: usize,
line: usize,
2017-11-16 23:30:08 -08:00
column: usize,
width: Option<usize>,
2017-11-16 23:30:08 -08:00
) -> Result<(), fmt::Error> {
let line_number = line + 1;
let red = Color::fmt(f).error();
match text.lines().nth(line) {
Some(line) => {
let mut i = 0;
let mut space_column = 0;
let mut space_line = String::new();
let mut space_width = 0;
2017-11-16 23:30:08 -08:00
for c in line.chars() {
if c == '\t' {
space_line.push_str(" ");
if i < column {
space_column += 4;
}
if i >= column && i < column + width.unwrap_or(1) {
space_width += 4;
}
} else {
if i < column {
space_column += UnicodeWidthChar::width(c).unwrap_or(0);
}
if i >= column && i < column + width.unwrap_or(1) {
space_width += UnicodeWidthChar::width(c).unwrap_or(0);
}
space_line.push(c);
}
i += c.len_utf8();
}
let line_number_width = line_number.to_string().len();
2018-08-27 18:36:40 -07:00
writeln!(f, "{0:1$} |", "", line_number_width)?;
writeln!(f, "{} | {}", line_number, space_line)?;
2017-11-16 23:30:08 -08:00
write!(f, "{0:1$} |", "", line_number_width)?;
if width == None {
write!(
f,
" {0:1$}{2}^{3}",
"",
space_column,
red.prefix(),
red.suffix()
)?;
2017-11-16 23:30:08 -08:00
} else {
write!(
f,
" {0:1$}{2}{3:^<4$}{5}",
"",
space_column,
red.prefix(),
"",
space_width,
red.suffix()
)?;
2017-11-16 23:30:08 -08:00
}
}
None => {
if index != text.len() {
write!(
f,
"internal error: Error has invalid line number: {}",
line_number
)?
}
}
2017-11-16 23:30:08 -08:00
}
Ok(())
}
pub struct And<'a, T: 'a + Display>(pub &'a [T]);
impl<'a, T: Display> Display for And<'a, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
conjoin(f, self.0, "and")
}
}
pub struct Or<'a, T: 'a + Display>(pub &'a [T]);
2017-11-16 23:30:08 -08:00
impl<'a, T: Display> Display for Or<'a, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
conjoin(f, self.0, "or")
}
}
pub struct Tick<'a, T: 'a + Display>(pub &'a T);
impl<'a, T: Display> Display for Tick<'a, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "`{}`", self.0)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn conjoin_or() {
assert_eq!("1", Or(&[1]).to_string());
assert_eq!("1 or 2", Or(&[1, 2]).to_string());
assert_eq!("1, 2, or 3", Or(&[1, 2, 3]).to_string());
assert_eq!("1, 2, 3, or 4", Or(&[1, 2, 3, 4]).to_string());
2017-11-16 23:30:08 -08:00
}
#[test]
fn conjoin_and() {
assert_eq!("1", And(&[1]).to_string());
assert_eq!("1 and 2", And(&[1, 2]).to_string());
assert_eq!("1, 2, and 3", And(&[1, 2, 3]).to_string());
assert_eq!("1, 2, 3, and 4", And(&[1, 2, 3, 4]).to_string());
2017-11-16 23:30:08 -08:00
}
}