use std::{borrow::Borrow, fmt, path::Path, sync::Arc};
use intrusive_collections::{intrusive_adapter, RBTreeAtomicLink};
use serde::Deserialize;
use super::TestSuite;
pub type TestSuiteCursor<'a> = intrusive_collections::rbtree::Cursor<'a, TestSuiteAdapter>;
intrusive_adapter!(pub TestSuiteAdapter = Arc<TestSuite>: TestSuite { link: RBTreeAtomicLink });
impl<'a> intrusive_collections::KeyAdapter<'a> for TestSuiteAdapter {
type Key = TestSuiteKey;
#[inline]
fn get_key(&self, suite: &'a TestSuite) -> Self::Key {
suite.id()
}
}
#[derive(Default)]
pub struct TestSuiteSet {
len: usize,
suites: intrusive_collections::RBTree<TestSuiteAdapter>,
}
impl TestSuiteSet {
pub fn is_empty(&self) -> bool {
self.suites.is_empty()
}
#[allow(unused)]
pub fn len(&self) -> usize {
self.len
}
pub fn iter(&self) -> Iter<'_> {
Iter::new(self.suites.front())
}
pub fn insert(&mut self, suite: Arc<TestSuite>) -> bool {
use intrusive_collections::rbtree::Entry;
match self.suites.entry(&suite.id()) {
Entry::Occupied(_) => {
log::debug!(
"attempted to register multiple test suites with the same key: {:?}",
suite.id()
);
false
}
Entry::Vacant(entry) => {
entry.insert(suite);
self.len += 1;
true
}
}
}
pub fn get<Q>(&self, key: &Q) -> Option<Arc<TestSuite>>
where
Q: Ord + ?Sized,
TestSuiteKey: Borrow<Q>,
{
let cursor = self.suites.find(key);
cursor.clone_pointer()
}
pub fn clear(&mut self) {
self.suites.clear();
self.len = 0;
}
}
pub struct Iter<'a> {
cursor: TestSuiteCursor<'a>,
}
impl<'a> Iter<'a> {
pub fn new(cursor: TestSuiteCursor<'a>) -> Self {
Self { cursor }
}
}
impl<'a> core::iter::FusedIterator for Iter<'a> {}
impl<'a> Iterator for Iter<'a> {
type Item = Arc<TestSuite>;
fn next(&mut self) -> Option<Self::Item> {
let suite = self.cursor.clone_pointer();
if suite.is_some() {
self.cursor.move_next();
}
suite
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize)]
pub struct TestSuiteKey {
name: toml::Spanned<Arc<str>>,
#[serde(skip, default)]
pub path: Option<Arc<Path>>,
}
impl litcheck::diagnostics::Spanned for TestSuiteKey {
fn span(&self) -> litcheck::diagnostics::SourceSpan {
litcheck::diagnostics::SourceSpan::from(self.name.span())
}
}
impl TestSuiteKey {
pub fn new(name: toml::Spanned<Arc<str>>, path: Option<Arc<Path>>) -> Self {
Self { name, path }
}
#[inline]
pub fn name(&self) -> &str {
self.name.as_ref()
}
pub fn path(&self) -> &Path {
self.path.as_deref().unwrap_or(Path::new(""))
}
}
impl fmt::Display for TestSuiteKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let name = self.name();
let path = self.path();
match std::env::current_dir() {
Ok(cwd) => {
let path = path.strip_prefix(cwd).unwrap_or(path);
write!(f, "{name} @ {}", path.display())
}
Err(_) => {
write!(f, "{name} @ {}", path.display())
}
}
}
}