mod id_gen;
mod toposort;
use std::{io::stderr, sync::OnceLock};
use anstream::adapter::strip_str;
pub use id_gen::{IdGenerator, NameGenerator};
use itertools::Itertools;
use regex::Regex;
pub use toposort::toposort;
use crate::Result;
pub trait OrMap<T> {
fn or_map<F>(self, b: Self, f: F) -> Self
where
F: FnOnce(T, T) -> T;
}
impl<T> OrMap<T> for Option<T> {
fn or_map<F>(self, b: Self, f: F) -> Self
where
F: FnOnce(T, T) -> T,
{
match (self, b) {
(Some(a), Some(b)) => Some(f(a, b)),
(a, None) => a,
(None, b) => b,
}
}
}
pub trait Pluck<T> {
fn pluck<R, F>(&mut self, f: F) -> Vec<R>
where
F: Fn(T) -> Result<R, T>;
}
impl<T> Pluck<T> for Vec<T> {
fn pluck<R, F>(&mut self, f: F) -> Vec<R>
where
F: Fn(T) -> Result<R, T>,
{
let mut matched = Vec::new();
let mut not_matched = Vec::new();
for transform in self.drain(..) {
match f(transform) {
Ok(t) => matched.push(t),
Err(transform) => not_matched.push(transform),
}
}
self.extend(not_matched);
matched
}
}
pub trait BreakUp<T> {
fn break_up<F>(self, f: F) -> (Vec<T>, Vec<T>)
where
F: FnMut(&T) -> bool;
}
impl<T> BreakUp<T> for Vec<T> {
fn break_up<F>(mut self, f: F) -> (Vec<T>, Vec<T>)
where
F: FnMut(&T) -> bool,
{
let position = self.iter().position(f).unwrap_or(self.len());
let after = self.drain(position..).collect_vec();
(self, after)
}
}
pub(crate) fn valid_ident() -> &'static Regex {
static VALID_IDENT: OnceLock<Regex> = OnceLock::new();
VALID_IDENT.get_or_init(|| {
Regex::new(r"^((\*)|(^[a-z_\$][a-z0-9_\$]*))$").unwrap()
})
}
fn should_use_color() -> bool {
match anstream::AutoStream::choice(&stderr()) {
anstream::ColorChoice::Auto => true,
anstream::ColorChoice::Always => true,
anstream::ColorChoice::AlwaysAnsi => true,
anstream::ColorChoice::Never => false,
}
}
pub fn maybe_strip_colors(s: &str) -> String {
if !should_use_color() {
strip_str(s).to_string()
} else {
s.to_string()
}
}
#[test]
fn test_write_ident_part() {
assert!(!valid_ident().is_match(""));
}