ogeom_algo/derived.rs
1//! Whether a read of a shape made from the reads kept of other shapes
2//! holding the same faces ([`ogeom_topo::Model::kept_reads`]) is checked
3//! against the same read made afresh: always in a debug build, and in any
4//! build inside `derived_reads_checked`, with the `test-hooks` feature.
5
6use std::cell::Cell;
7
8thread_local! {
9 /// The faces whose reads were taken from kept reads, counted while
10 /// `derived_reads_checked` runs.
11 static CHECKED: Cell<Option<usize>> = const { Cell::new(None) };
12}
13
14/// Whether a read taken in part from kept reads on this thread is to be
15/// compared with the same read made afresh, and a difference panicked on.
16#[doc(hidden)]
17#[must_use]
18pub fn checks_derived_reads() -> bool {
19 cfg!(debug_assertions) || CHECKED.get().is_some()
20}
21
22/// Count `faces` whose read was taken from a kept read.
23#[doc(hidden)]
24pub fn note_derived(faces: usize) {
25 if let Some(n) = CHECKED.get() {
26 CHECKED.set(Some(n + faces));
27 }
28}
29
30/// Run `f` with every read taken in part from kept reads on this thread
31/// compared with the same read made afresh, panicking where they differ;
32/// and how many faces' reads were taken so.
33#[cfg(feature = "test-hooks")]
34pub fn derived_reads_checked<R>(f: impl FnOnce() -> R) -> (R, usize) {
35 struct Restore(Option<usize>);
36 impl Drop for Restore {
37 fn drop(&mut self) {
38 CHECKED.set(self.0);
39 }
40 }
41 let restore = Restore(CHECKED.replace(Some(0)));
42 let out = f();
43 let faces = CHECKED.get().unwrap_or(0);
44 drop(restore);
45 (out, faces)
46}