Skip to main content

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}