litcheck_lit/suite/
set.rs1use std::{borrow::Borrow, fmt, path::Path, sync::Arc};
2
3use intrusive_collections::{intrusive_adapter, RBTreeAtomicLink};
4use serde::Deserialize;
5
6use super::TestSuite;
7
8pub type TestSuiteCursor<'a> = intrusive_collections::rbtree::Cursor<'a, TestSuiteAdapter>;
9
10intrusive_adapter!(pub TestSuiteAdapter = Arc<TestSuite>: TestSuite { link: RBTreeAtomicLink });
11impl<'a> intrusive_collections::KeyAdapter<'a> for TestSuiteAdapter {
12 type Key = TestSuiteKey;
13
14 #[inline]
15 fn get_key(&self, suite: &'a TestSuite) -> Self::Key {
16 suite.id()
17 }
18}
19
20#[derive(Default)]
21pub struct TestSuiteSet {
22 len: usize,
23 suites: intrusive_collections::RBTree<TestSuiteAdapter>,
24}
25impl TestSuiteSet {
26 pub fn is_empty(&self) -> bool {
27 self.suites.is_empty()
28 }
29
30 #[allow(unused)]
31 pub fn len(&self) -> usize {
32 self.len
33 }
34
35 pub fn iter(&self) -> Iter<'_> {
36 Iter::new(self.suites.front())
37 }
38
39 pub fn insert(&mut self, suite: Arc<TestSuite>) -> bool {
40 use intrusive_collections::rbtree::Entry;
41
42 match self.suites.entry(&suite.id()) {
43 Entry::Occupied(_) => {
44 log::debug!(
45 "attempted to register multiple test suites with the same key: {:?}",
46 suite.id()
47 );
48 false
49 }
50 Entry::Vacant(entry) => {
51 entry.insert(suite);
52 self.len += 1;
53 true
54 }
55 }
56 }
57
58 pub fn get<Q>(&self, key: &Q) -> Option<Arc<TestSuite>>
59 where
60 Q: Ord + ?Sized,
61 TestSuiteKey: Borrow<Q>,
62 {
63 let cursor = self.suites.find(key);
64 cursor.clone_pointer()
65 }
66
67 pub fn clear(&mut self) {
68 self.suites.clear();
69 self.len = 0;
70 }
71}
72
73pub struct Iter<'a> {
74 cursor: TestSuiteCursor<'a>,
75}
76impl<'a> Iter<'a> {
77 pub fn new(cursor: TestSuiteCursor<'a>) -> Self {
78 Self { cursor }
79 }
80}
81impl<'a> core::iter::FusedIterator for Iter<'a> {}
82impl<'a> Iterator for Iter<'a> {
83 type Item = Arc<TestSuite>;
84
85 fn next(&mut self) -> Option<Self::Item> {
86 let suite = self.cursor.clone_pointer();
87 if suite.is_some() {
88 self.cursor.move_next();
89 }
90 suite
91 }
92}
93
94#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize)]
95pub struct TestSuiteKey {
96 name: toml::Spanned<Arc<str>>,
98 #[serde(skip, default)]
100 pub path: Option<Arc<Path>>,
101}
102impl litcheck::diagnostics::Spanned for TestSuiteKey {
103 fn span(&self) -> litcheck::diagnostics::SourceSpan {
104 litcheck::diagnostics::SourceSpan::from(self.name.span())
105 }
106}
107impl TestSuiteKey {
108 pub fn new(name: toml::Spanned<Arc<str>>, path: Option<Arc<Path>>) -> Self {
109 Self { name, path }
110 }
111
112 #[inline]
114 pub fn name(&self) -> &str {
115 self.name.as_ref()
116 }
117
118 pub fn path(&self) -> &Path {
120 self.path.as_deref().unwrap_or(Path::new(""))
121 }
122}
123impl fmt::Display for TestSuiteKey {
124 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
125 let name = self.name();
126 let path = self.path();
127 match std::env::current_dir() {
128 Ok(cwd) => {
129 let path = path.strip_prefix(cwd).unwrap_or(path);
130 write!(f, "{name} @ {}", path.display())
131 }
132 Err(_) => {
133 write!(f, "{name} @ {}", path.display())
134 }
135 }
136 }
137}