safe_chains/pathctx/
item_shape.rs1use std::cell::RefCell;
8use std::marker::PhantomData;
9use std::ops::BitOr;
10
11#[derive(Debug, PartialEq, Eq)]
14pub struct Facts<T>(u8, PhantomData<T>);
15
16impl<T> Clone for Facts<T> {
17 fn clone(&self) -> Self {
18 *self
19 }
20}
21
22impl<T> Copy for Facts<T> {}
23
24impl<T> Default for Facts<T> {
25 fn default() -> Self {
26 Self::NONE
27 }
28}
29
30impl<T> Facts<T> {
31 pub const NONE: Self = Self(0, PhantomData);
32
33 #[must_use]
34 pub const fn bit(n: u8) -> Self {
35 Self(1 << n, PhantomData)
36 }
37
38 #[must_use]
39 pub const fn union(self, other: Self) -> Self {
40 Self(self.0 | other.0, PhantomData)
41 }
42
43 pub fn has(self, fact: Self) -> bool {
44 self.0 & fact.0 != 0
45 }
46
47 pub fn set(&mut self, fact: Self, on: bool) {
48 if on {
49 self.0 |= fact.0;
50 }
51 }
52
53 pub fn clear(&mut self, fact: Self) {
54 self.0 &= !fact.0;
55 }
56
57 #[must_use]
58 pub fn when(fact: Self, on: bool) -> Self {
59 if on { fact } else { Self::NONE }
60 }
61}
62
63impl<T> BitOr for Facts<T> {
64 type Output = Self;
65 fn bitor(self, other: Self) -> Self {
66 self.union(other)
67 }
68}
69
70#[derive(Debug, PartialEq, Eq)]
72pub enum Items {}
73
74pub type ItemShape = Facts<Items>;
75
76pub const LEAD: ItemShape = Facts::bit(0);
78pub const SPLIT: ItemShape = Facts::bit(1);
80pub const EMPTY: ItemShape = Facts::bit(2);
82pub const UNKNOWN: ItemShape = LEAD.union(SPLIT).union(EMPTY);
84
85thread_local! {
86 static LOOP_SHAPES: RefCell<Vec<(String, ItemShape)>> = const { RefCell::new(Vec::new()) };
87 static STDIN_SHAPES: RefCell<Vec<ItemShape>> = const { RefCell::new(Vec::new()) };
88}
89
90pub struct ItemShapeGuard(bool);
91
92impl Drop for ItemShapeGuard {
93 fn drop(&mut self) {
94 if self.0 {
95 LOOP_SHAPES.with(|v| v.borrow_mut().pop());
96 } else {
97 STDIN_SHAPES.with(|v| v.borrow_mut().pop());
98 }
99 }
100}
101
102#[must_use]
103pub fn enter_loop_shape(name: String, shape: ItemShape) -> ItemShapeGuard {
104 LOOP_SHAPES.with(|v| v.borrow_mut().push((name, shape)));
105 ItemShapeGuard(true)
106}
107
108pub fn loop_shape(name: &str) -> ItemShape {
110 LOOP_SHAPES.with(|v| v.borrow().iter().rev().find(|(n, _)| n == name).map_or(UNKNOWN, |(_, s)| *s))
111}
112
113#[must_use]
114pub fn enter_stdin_shape(shape: ItemShape) -> ItemShapeGuard {
115 STDIN_SHAPES.with(|v| v.borrow_mut().push(shape));
116 ItemShapeGuard(false)
117}
118
119pub fn stdin_shape() -> ItemShape {
121 STDIN_SHAPES.with(|v| v.borrow().last().copied().unwrap_or(UNKNOWN))
122}
123
124pub enum Binding {
127 Loop,
128 Value(String),
129 Unbound,
130}
131
132pub fn binding(name: &str) -> Binding {
133 if super::LOOP_VARS.with(|v| v.borrow().iter().any(|l| l.name == name)) {
134 return Binding::Loop;
135 }
136 super::VARS.with(|v| {
137 v.borrow()
138 .iter()
139 .rev()
140 .find(|b| b.name == name)
141 .map_or(Binding::Unbound, |b| Binding::Value(b.value.clone()))
142 })
143}
144
145pub fn bound_names() -> Vec<String> {
147 let loops: Vec<String> = super::LOOP_VARS.with(|v| v.borrow().iter().map(|l| l.name.clone()).collect());
148 super::VARS.with(|v| loops.into_iter().chain(v.borrow().iter().map(|b| b.name.clone())).collect())
149}
150
151#[cfg(test)]
152mod tests {
153 use super::*;
154
155 #[test]
156 fn unbound_shapes_are_unknown_and_guards_unwind() {
157 let safe = ItemShape::NONE;
158 assert_eq!(loop_shape("f"), UNKNOWN);
159 assert_eq!(stdin_shape(), UNKNOWN);
160 {
161 let _outer = enter_loop_shape("f".into(), safe);
162 let _stdin = enter_stdin_shape(safe);
163 assert_eq!(loop_shape("f"), safe);
164 assert_eq!(loop_shape("g"), UNKNOWN);
165 assert_eq!(stdin_shape(), safe);
166 {
167 let _inner = enter_loop_shape("f".into(), UNKNOWN);
168 assert_eq!(loop_shape("f"), UNKNOWN);
169 }
170 assert_eq!(loop_shape("f"), safe);
171 }
172 assert_eq!(loop_shape("f"), UNKNOWN);
173 assert_eq!(stdin_shape(), UNKNOWN);
174 }
175
176 #[test]
177 fn facts_combine_and_clear() {
178 let mut f = ItemShape::NONE;
179 assert!(!f.has(LEAD));
180 f.set(LEAD, false);
181 assert_eq!(f, ItemShape::NONE);
182 f.set(LEAD, true);
183 assert!(f.has(LEAD) && !f.has(SPLIT));
184 assert_eq!(LEAD | SPLIT | EMPTY, UNKNOWN);
185 f.clear(LEAD);
186 assert_eq!(f, ItemShape::NONE);
187 assert_eq!(ItemShape::when(SPLIT, true), SPLIT);
188 assert_eq!(ItemShape::when(SPLIT, false), ItemShape::NONE);
189 }
190}