tor_memquota/if_enabled.rs
1//! Helper type for disabling memory tracking when not wanted
2
3use crate::internal_prelude::*;
4
5/// Token indicating that memory quota tracking is enabled, at both compile and runtime
6///
7/// If support is compiled in this is a unit.
8///
9/// If the `memquota` cargo feature is not enabled, this type is uninhabited.
10/// Scattering values of this type around in relevant data structures
11/// and parameters lists
12/// allows the compiler to eliminate the unwanted code.
13#[derive(Clone, Copy, Debug, PartialEq)]
14pub struct EnabledToken {
15 /// Make non-exhaustive even within the crate
16 _hidden: (),
17
18 /// Uninhabited if the feature isn't enabled.
19 #[cfg(not(feature = "memquota"))]
20 _forbid: Void,
21}
22
23impl Eq for EnabledToken {}
24
25impl EnabledToken {
26 /// Obtain an `EnabledToken` (only available if tracking is compiled in)
27 #[allow(clippy::new_without_default)] // a conditional Default impl would be rather odd
28 #[cfg(feature = "memquota")]
29 pub const fn new() -> Self {
30 EnabledToken { _hidden: () }
31 }
32
33 /// Obtain an `EnabledToken` if memory-tracking is compiled in, or `None` otherwise
34 #[allow(clippy::unnecessary_wraps)] // Will be None if compiled out
35 #[allow(unreachable_code)]
36 pub const fn new_if_compiled_in() -> Option<Self> {
37 Some(EnabledToken {
38 _hidden: (),
39
40 #[cfg(not(feature = "memquota"))]
41 _forbid: return None,
42 })
43 }
44}
45
46/// Either `T`, if we're enabled, or nothing if we're no-op
47///
48/// Used for runtime control of whether the memory quota is enabled:
49/// we support explicitly creating a no-op tracker
50/// with [`MemoryQuotaTracker::new_noop`](crate::MemoryQuotaTracker::new_noop).
51///
52/// We use this rather than just `Option` because we also have data structures
53/// (trackers, `Account`s and so on)
54/// which have been torn down, or are "dummy" or "dangling",
55/// which are supposed to return errors rather than no-op successes.
56#[derive(Clone, Debug, Eq, PartialEq)]
57pub(crate) enum IfEnabled<T> {
58 /// We're enabled, and supposed to be tracking memory
59 ///
60 /// The 2nd member causes this variant to prove that tracking is enabled.
61 /// If tracking is disabled at compile time, this variant is uninhabited
62 /// and the whole `IfEnabled` becomes a unit.
63 Enabled(T, EnabledToken),
64
65 /// We're inenabled and everything should be a lightweight no-op
66 Noop,
67}
68
69use IfEnabled::*;
70
71impl<T> IfEnabled<T> {
72 /// Convert to `Option`: return `Some` if this is `Enabled`
73 pub(crate) fn into_enabled(self) -> Option<T> {
74 match self {
75 Enabled(y, _e) => Some(y),
76 Noop => None,
77 }
78 }
79
80 /// Take reference; analogous to `Option::as_ref`
81 pub(crate) fn as_ref(&self) -> IfEnabled<&T> {
82 match self {
83 Enabled(y, e) => Enabled(y, *e),
84 Noop => Noop,
85 }
86 }
87
88 /// Take reference and convert to `Option`
89 ///
90 /// Convenience helper equivalent to `.as_ref().into_enabled()`.
91 pub(crate) fn as_enabled(&self) -> Option<&T> {
92 self.as_ref().into_enabled()
93 }
94
95 /// Return the contents of the `Enabled`, or declare it a [`Bug`]
96 #[track_caller]
97 pub(crate) fn enabled_or_bug(self) -> Result<T, Bug> {
98 match self {
99 Enabled(y, _e) => Ok(y),
100 Noop => Err(internal!("IfEnabled unexpectedly Noop")),
101 }
102 }
103}