immigrant_schema/
ids.rs

1use std::{
2	cell::{Cell, RefCell},
3	cmp::Ordering,
4	collections::{hash_map::Entry, HashMap},
5	fmt::{self, Debug, Display},
6	hash::{Hash, Hasher},
7	marker::PhantomData,
8};
9
10use derivative::Derivative;
11
12use crate::{names::UnknownKind, span::SimpleSpan};
13
14#[derive(Default)]
15pub struct CodeIdentAllocator {
16	ids: RefCell<HashMap<String, u16>>,
17	max_id: Cell<u16>,
18
19	#[cfg(feature = "strict")]
20	generation: Cell<u8>,
21	max_kind: Cell<u8>,
22}
23impl CodeIdentAllocator {
24	#[cfg(feature = "strict")]
25	pub fn next_generation(&self) {
26		let max_kind = self.max_kind.get();
27		self.generation.set(
28			self.generation
29				.get()
30				.checked_add(max_kind + 1)
31				.expect("out of generations"),
32		);
33		self.max_kind.set(0);
34	}
35	fn name(&self, id: u16) -> String {
36		let ids = self.ids.borrow();
37		for (k, v) in ids.iter() {
38			if *v == id {
39				return k.to_owned();
40			}
41		}
42		unreachable!("identifier with unknown owner")
43	}
44	fn ident<K: Kind>(&self, span: SimpleSpan, name: &str) -> Ident<K> {
45		let kind = K::id();
46		self.max_kind.set(self.max_kind.get().max(kind));
47		let kind = {
48			#[cfg(feature = "strict")]
49			{
50				self.generation
51					.get()
52					.checked_add(kind)
53					.expect("out of kinds")
54			}
55			#[cfg(not(feature = "strict"))]
56			{
57				0
58			}
59		};
60		let mut ids = self.ids.borrow_mut();
61		match ids.entry(name.to_owned()) {
62			Entry::Occupied(v) => {
63				return Ident {
64					#[cfg(feature = "strict")]
65					kind,
66					id: *v.get(),
67					span,
68					_marker: PhantomData,
69				}
70			}
71			Entry::Vacant(v) => {
72				let id = self.max_id.get();
73				self.max_id.set(id.checked_add(1).expect("out of ids"));
74				v.insert(id);
75				Ident {
76					#[cfg(feature = "strict")]
77					kind,
78					id,
79					span,
80					_marker: PhantomData,
81				}
82			}
83		}
84	}
85}
86
87pub trait Kind {
88	fn id() -> u8;
89}
90
91thread_local! {
92	static ALLOCATOR: (Cell<bool>, CodeIdentAllocator) = (Cell::new(false), CodeIdentAllocator::default());
93}
94pub fn in_allocator<T>(f: impl FnOnce() -> T) -> T {
95	ALLOCATOR.with(|a| {
96		assert!(!a.0.get(), "already in allocator");
97		a.0.set(true);
98	});
99	let v = f();
100	ALLOCATOR.with(|a| {
101		assert!(a.0.get(), "should be in allocator");
102		a.0.set(false);
103		#[cfg(feature = "strict")]
104		a.1.next_generation();
105	});
106	v
107}
108
109pub struct Ident<K> {
110	#[cfg(feature = "strict")]
111	kind: u8,
112	id: u16,
113	span: SimpleSpan,
114	_marker: PhantomData<fn() -> K>,
115}
116impl<K> Ident<K> {
117	pub fn name(&self) -> String {
118		ALLOCATOR.with(|a| a.1.name(self.id))
119	}
120	pub fn span(&self) -> SimpleSpan {
121		self.span
122	}
123}
124impl<K: Kind> Ident<K> {
125	pub fn alloc((span, name): (SimpleSpan, &str)) -> Self {
126		ALLOCATOR.with(|a| {
127			assert!(a.0.get(), "should be in allocator");
128			a.1.ident(span, name)
129		})
130	}
131	pub fn unchecked_cast<U: Kind>(v: Ident<U>) -> Self {
132		assert_eq!(
133			K::id(),
134			U::id(),
135			"types should be explicitly marked as compatible"
136		);
137		Ident {
138			#[cfg(feature = "strict")]
139			kind: v.kind,
140			id: v.id,
141			span: v.span,
142			_marker: PhantomData,
143		}
144	}
145	pub fn to_unknown(self) -> Ident<UnknownKind> {
146		// Not using unchecked_cast to skip compatibility check
147		Ident {
148			#[cfg(feature = "strict")]
149			kind: UnknownKind::id(),
150			id: self.id,
151			span: self.span,
152			_marker: PhantomData,
153		}
154	}
155}
156impl<K> Clone for Ident<K> {
157	fn clone(&self) -> Self {
158		*self
159	}
160}
161impl<K> Copy for Ident<K> {}
162impl<K> PartialEq for Ident<K> {
163	fn eq(&self, other: &Ident<K>) -> bool {
164		#[cfg(feature = "strict")]
165		assert_eq!(
166			self.kind, other.kind,
167			"comparing idents of a different generations"
168		);
169		self.id == other.id
170	}
171}
172impl<K> Eq for Ident<K> {}
173impl<K> Ord for Ident<K> {
174	fn cmp(&self, other: &Ident<K>) -> Ordering {
175		#[cfg(feature = "strict")]
176		assert_eq!(
177			self.kind, other.kind,
178			"comparing idents of a different generations"
179		);
180		self.id.cmp(&other.id)
181	}
182}
183impl<K> Hash for Ident<K> {
184	fn hash<H: Hasher>(&self, state: &mut H) {
185		self.id.hash(state);
186	}
187}
188impl<K> PartialOrd for Ident<K> {
189	fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
190		Some(self.cmp(other))
191	}
192}
193impl<K> Debug for Ident<K> {
194	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
195		let n = ALLOCATOR.with(|a| a.1.name(self.id));
196		write!(f, "{n}")
197	}
198}
199
200#[derive(Derivative)]
201#[derivative(Default(bound = ""), Ord(bound = ""))]
202pub struct DbIdent<K> {
203	id: String,
204	_marker: PhantomData<K>,
205}
206impl<K> PartialOrd for DbIdent<K> {
207	fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
208		Some(self.cmp(other))
209	}
210}
211impl<K> DbIdent<K> {
212	pub fn new(v: &str) -> Self {
213		assert_ne!(v, "");
214		Self {
215			id: v.to_owned(),
216			_marker: PhantomData,
217		}
218	}
219	pub fn raw(&self) -> &str {
220		&self.id
221	}
222}
223impl<K> PartialEq for DbIdent<K> {
224	fn eq(&self, other: &Self) -> bool {
225		self.id == other.id
226	}
227}
228impl<K> Eq for DbIdent<K> {}
229impl<K> Hash for DbIdent<K> {
230	fn hash<H: Hasher>(&self, state: &mut H) {
231		self.id.hash(state);
232	}
233}
234// impl<K> Display for DbIdent<K> {
235// 	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
236// 		self.assert_not_guard();
237// 		write!(f, "{}", self.id)
238// 	}
239// }
240impl<K> Debug for DbIdent<K> {
241	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
242		write!(f, "{:?}", self.id)
243	}
244}
245impl<K> Clone for DbIdent<K> {
246	fn clone(&self) -> Self {
247		Self {
248			id: self.id.clone(),
249			_marker: PhantomData,
250		}
251	}
252}
253
254impl<T> DbIdent<T> {
255	pub fn unchecked_from<U>(t: DbIdent<U>) -> Self {
256		DbIdent {
257			id: t.id,
258			_marker: PhantomData,
259		}
260	}
261}