use std::mem;
#[cfg(not(debug_assertions))]
use konst::eq_str;
use pest::{RuleType, error::LineColLocation};
use rootasrole_core::util::escape_parser_string;
#[cfg(not(test))]
pub const RAR_CFG_PATH: &str = env!("RAR_CFG_PATH");
#[cfg(test)]
pub const RAR_CFG_PATH: &str = "target/rootasrole.json";
#[cfg(not(test))]
pub const RAR_CFG_DATA_PATH: &str = env!("RAR_CFG_DATA_PATH");
#[cfg(test)]
pub const RAR_CFG_DATA_PATH: &str = "target/rootasrole.json";
#[cfg(debug_assertions)]
pub const RAR_CFG_IMMUTABLE: bool = false;
#[cfg(not(debug_assertions))]
pub const RAR_CFG_IMMUTABLE: bool = eq_str(env!("RAR_CFG_IMMUTABLE"), "true");
const fn start<R>(error: &pest::error::Error<R>) -> (usize, usize)
where
R: RuleType,
{
match error.line_col {
LineColLocation::Pos(line_col) => line_col,
LineColLocation::Span(start_line_col, _) => start_line_col,
}
}
pub fn underline<R>(error: &pest::error::Error<R>) -> String
where
R: RuleType,
{
let mut underline = String::new();
let mut start = start(error).1;
let end = match error.line_col {
LineColLocation::Span(_, (_, mut end)) => {
let inverted_cols = start > end;
if inverted_cols {
mem::swap(&mut start, &mut end);
start -= 1;
end += 1;
}
Some(end)
}
LineColLocation::Pos(_) => None,
};
let offset = start - 1;
let line_chars = error.line().chars();
for c in line_chars.take(offset) {
match c {
'\t' => underline.push('\t'),
_ => underline.push(' '),
}
}
if let Some(end) = end {
underline.push('^');
if end - start > 1 {
for _ in 2..(end - start) {
underline.push('-');
}
underline.push('^');
}
} else {
underline.push_str("^---");
}
underline
}
pub fn escape_parser_string_vec<S, I>(s: I) -> String
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
s.into_iter()
.map(|s| escape_parser_string(s))
.collect::<Vec<String>>()
.join(" ")
}
#[cfg(test)]
mod tests {
use super::*;
use pest::error::{Error, ErrorVariant};
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
enum TestRule {}
#[test]
fn test_underline_with_pos() {
let input = "hello world";
let error = Error::<TestRule>::new_from_pos(
ErrorVariant::CustomError {
message: "test error".to_string(),
},
pest::Position::new(input, 6).unwrap(),
);
let result = underline(&error);
assert_eq!(result, " ^---");
}
#[test]
fn test_underline_with_span() {
let input = "hello world";
let error = Error::<TestRule>::new_from_span(
ErrorVariant::CustomError {
message: "test error".to_string(),
},
pest::Span::new(input, 6, 11).unwrap(), );
let result = underline(&error);
assert_eq!(result, " ^---^");
}
#[test]
fn test_underline_with_span_single_char() {
let input = "hello world";
let error = Error::<TestRule>::new_from_span(
ErrorVariant::CustomError {
message: "test error".to_string(),
},
pest::Span::new(input, 6, 7).unwrap(), );
let result = underline(&error);
assert_eq!(result, " ^");
}
#[test]
fn test_underline_with_tabs() {
let input = "\t\thello world";
let error = Error::<TestRule>::new_from_pos(
ErrorVariant::CustomError {
message: "test error".to_string(),
},
pest::Position::new(input, 8).unwrap(), );
let result = underline(&error);
assert_eq!(result, "\t\t ^---");
}
#[test]
fn test_underline_at_beginning() {
let input = "hello world";
let error = Error::<TestRule>::new_from_pos(
ErrorVariant::CustomError {
message: "test error".to_string(),
},
pest::Position::new(input, 0).unwrap(), );
let result = underline(&error);
assert_eq!(result, "^---");
}
#[test]
fn test_escape_parser_string_vec_empty() {
let result = escape_parser_string_vec(std::iter::empty::<String>());
assert_eq!(result, "");
}
#[test]
fn test_escape_parser_string_vec_single() {
let input = vec!["hello"];
let result = escape_parser_string_vec(input);
assert_eq!(result, "hello");
}
#[test]
fn test_escape_parser_string_vec_multiple() {
let input = vec!["hello", "world", "test"];
let result = escape_parser_string_vec(input);
assert_eq!(result, "hello world test");
}
#[test]
fn test_escape_parser_string_vec_with_quotes() {
let input = vec!["\"hello\"", "'world'", "test"];
let result = escape_parser_string_vec(input);
assert_eq!(result, "hello world test");
}
#[test]
fn test_escape_parser_string_vec_with_nested_quotes() {
let input = vec!["\"'hello'\"", "\"test\""];
let result = escape_parser_string_vec(input);
assert_eq!(result, "hello test");
}
#[test]
fn test_escape_parser_string_vec_different_types() {
let input = vec!["hello".to_string(), "world".to_string()];
let result = escape_parser_string_vec(input);
assert_eq!(result, "hello world");
let input = vec!["hello", "world"];
let result = escape_parser_string_vec(input);
assert_eq!(result, "hello world");
}
#[test]
fn test_start_with_pos() {
let input = "hello world";
let error = Error::<TestRule>::new_from_pos(
ErrorVariant::CustomError {
message: "test error".to_string(),
},
pest::Position::new(input, 6).unwrap(),
);
let (line, col) = start(&error);
assert_eq!(line, 1); assert_eq!(col, 7); }
#[test]
fn test_start_with_span() {
let input = "hello world";
let error = Error::<TestRule>::new_from_span(
ErrorVariant::CustomError {
message: "test error".to_string(),
},
pest::Span::new(input, 6, 11).unwrap(),
);
let (line, col) = start(&error);
assert_eq!(line, 1); assert_eq!(col, 7); }
}