#![warn(missing_docs)]
mod peek;
mod fallible_itertools;
use std::{error::Error, fmt::Display, marker::PhantomData, ops::Range, str::Chars};
use itertools::Itertools;
pub use peek::Peek;
pub use fallible_itertools::FallibleItertools;
#[must_use]
pub fn rem_first_and_last_char(value: &str) -> String {
_rem_last_char(_rem_first_char(value))
}
#[must_use]
pub fn rem_first_char(value: &str) -> String {
_rem_first_char(value).as_str().to_string()
}
#[must_use]
pub fn rem_last_char(value: &str) -> String {
_rem_last_char(value.chars())
}
#[must_use]
pub fn rem_first_char_n(value: &str, n: usize) -> String {
_rem_first_char_n(value, n).as_str().to_string()
}
#[must_use]
pub fn remove_whitespace(mut s: String) -> String {
s.retain(|c| !c.is_whitespace());
s
}
pub fn split_to_twople(s: &str, split: char) -> Result<(String, String), SplitError> {
let mut splitted = s.split(split);
if let Some(left) = splitted.next() {
if let Some(right) = splitted.next() {
return Ok((left.trim().to_string(), right.trim().to_string()))
}
}
Err(SplitError(s.to_owned(), split))
}
#[derive(Eq, PartialEq, Debug)]
pub struct SplitError(String, char);
impl SplitError {
#[must_use]
pub fn get_input(self) -> String {
self.0
}
#[must_use]
pub fn get_split(self) -> char {
self.1
}
#[must_use]
pub fn decompose(self) -> (String, char) {
(self.0, self.1)
}
}
fn _rem_first_char(value: &str) -> Chars {
let mut chars = value.chars();
chars.next();
chars
}
fn _rem_last_char(mut chars: Chars) -> String {
chars.next_back();
return chars.as_str().to_string();
}
fn _rem_first_char_n(value: &str, n: usize) -> Chars {
let mut chars = value.chars();
let mut i = 0;
while i < n {
i += 1;
chars.next();
}
chars
}
pub fn comma_separated_with_or(strings: &[String]) -> String {
strings.last().map_or_else(String::new, |last| {
let len = strings.len();
if len == 1 {
last.clone()
} else {
let joined = strings.iter().take(len - 1).map(String::as_str).collect::<Vec<&str>>().join(", ");
format!("{joined} or {last}")
}
})
}
pub fn comma_separated_with_or_str(strings: &[&str]) -> String {
strings.last().map_or_else(String::new, |last| {
let len = strings.len();
if len == 1 {
(*last).to_string()
} else {
let joined = strings.iter().take(len - 1).copied().collect::<Vec<&str>>().join(", ");
format!("{joined} or {last}")
}
})
}
#[must_use]
pub fn comma_separated<T: std::fmt::Display>(input: &[T]) -> String {
input.iter().join(",")
}
#[must_use]
pub fn normalize_to_probabilities<T: std::ops::Div<Output = T> + for<'a> std::iter::Sum<&'a T>>(input: &Vec<T>) -> Vec<T> where for<'a> &'a T: std::ops::Div<Output = T> {
let mut output = Vec::with_capacity(input.len());
let sum: T = input.iter().sum();
for val in input {
output.push(val / &sum);
}
output
}
#[macro_export]
macro_rules! string_vec {
( $( $x:expr ),* ) => {
vec![$($x.to_string(),)*]
};
}
pub trait ResultNext<T, E>: Iterator<Item = Result<T,E>> {
fn next_result(&mut self) -> Result<T, E>;
}
pub trait ResultPeek<T, E>: Peek + Iterator<Item = Result<T, E>> {
fn peek_result(&mut self) -> Result<&T, E>;
}
pub struct UnexpectedEnd;
impl<T, E: From<UnexpectedEnd>, U: Iterator<Item = Result<T, E>>> ResultNext<T, E> for U {
fn next_result(&mut self) -> Result<T, E> {
match self.next() {
Some(Ok(x)) => Ok(x),
Some(Err(e)) => Err(e),
None => Err(UnexpectedEnd.into())
}
}
}
impl<T, E, U: Peek + Iterator<Item = Result<T, E>>> ResultPeek<T, E> for U
where for<'a> E: From<UnexpectedEnd> + Clone + 'a
{
fn peek_result(&mut self) -> Result<&T, E> {
match self.peek() {
None => Err(UnexpectedEnd.into()),
Some(Ok(ref x)) => Ok(x),
Some(Err(e)) => Err(e.clone())
}
}
}
pub trait UnsafeNext<T, E: std::fmt::Debug>: Iterator<Item = Result<T, E>> {
#[allow(clippy::panic)]
fn next_unwrap(&mut self) -> T {
match self.next() {
Some(Ok(x)) => x,
Some(Err(e)) => panic!("Got error: {e:?}"),
None => panic!("Expected a value, but failed")
}
}
}
pub trait UnsafePeek<'a, T, E: std::fmt::Debug + 'a>: Peek + Iterator<Item = Result<T, E>> {
#[allow(clippy::panic)]
fn peek_unwrap(&'a mut self) -> &T {
match self.peek() {
Some(Ok(ref x)) => x,
Some(Err(ref e)) => panic!("Got error: {e:?}"),
None => panic!("Expected a value, but failed")
}
}
}
#[derive(Debug)]
pub struct ConversionError<T, U> {
subject: T,
to: PhantomData<U>
}
impl<T: Display, U> Display for ConversionError<T, U> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Failed to convert {} from type {} to value variant {}", self.subject, std::any::type_name::<T>(), std::any::type_name::<U>())
}
}
impl<T: Display + std::fmt::Debug, U: std::fmt::Debug> Error for ConversionError<T, U> {}
impl<T, U> ConversionError<T, U> {
pub const fn new(subject: T) -> Self {
Self {subject, to: PhantomData}
}
pub fn convert<V: From<U>>(self) -> ConversionError<T, V> {
ConversionError::<T, V>::new(self.subject)
}
}
pub type Span = Range<usize>;
pub trait ErrorReport: Error {
fn report(self) -> ((String, String), Span, Option<String>) where Self: Sized {
let msg = self.msg();
let source = ErrorReport::source(&self);
let span = self.span();
(msg, span, source)
}
fn span(self) -> Span;
fn msg(&self) -> (String, String);
fn source(&self) -> Option<String> {
None
}
}
pub trait Spannable {
fn span(&self) -> Span;
fn set_span(&mut self, _span: Span) {}
}
#[cfg(feature = "error_printing")]
pub fn handle_error<T: ErrorReport>(err: Vec<T>, file_path: &'static str, file: &str, offset: usize) -> Result<(), std::io::Error> {
use ariadne::{ColorGenerator, Label, Report, ReportKind};
let mut colors = ColorGenerator::new();
let a = colors.next();
let mut builder = Report::build(ReportKind::Error, file_path, offset);
let mut sources = std::collections::HashMap::new();
for e in err {
let ((head, msg), span, source) = e.report();
let data = source.map_or(file.to_string(), |data| data);
sources.insert(file_path, data);
builder = builder.with_message(head)
.with_label(
Label::new((file_path, span))
.with_message(msg)
.with_color(a),
);
}
builder.finish().eprint(ariadne::sources(sources))
}