1
//! Helper type for disabling memory tracking when not wanted
2

            
3
use 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)]
14
pub 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

            
23
impl Eq for EnabledToken {}
24

            
25
impl 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
8
    pub const fn new() -> Self {
30
8
        EnabledToken { _hidden: () }
31
8
    }
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
883160
    pub const fn new_if_compiled_in() -> Option<Self> {
37
883160
        Some(EnabledToken {
38
883160
            _hidden: (),
39
883160

            
40
883160
            #[cfg(not(feature = "memquota"))]
41
883160
            _forbid: return None,
42
883160
        })
43
883160
    }
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)]
57
pub(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

            
69
use IfEnabled::*;
70

            
71
impl<T> IfEnabled<T> {
72
    /// Convert to `Option`: return `Some` if this is `Enabled`
73
232910
    pub(crate) fn into_enabled(self) -> Option<T> {
74
232910
        match self {
75
232910
            Enabled(y, _e) => Some(y),
76
            Noop => None,
77
        }
78
232910
    }
79

            
80
    /// Take reference; analogous to `Option::as_ref`
81
192742
    pub(crate) fn as_ref(&self) -> IfEnabled<&T> {
82
192742
        match self {
83
192742
            Enabled(y, e) => Enabled(y, *e),
84
            Noop => Noop,
85
        }
86
192742
    }
87

            
88
    /// Take reference and convert to `Option`
89
    ///
90
    /// Convenience helper equivalent to `.as_ref().into_enabled()`.
91
192604
    pub(crate) fn as_enabled(&self) -> Option<&T> {
92
192604
        self.as_ref().into_enabled()
93
192604
    }
94

            
95
    /// Return the contents of the `Enabled`, or declare it a [`Bug`]
96
    #[track_caller]
97
124
    pub(crate) fn enabled_or_bug(self) -> Result<T, Bug> {
98
124
        match self {
99
124
            Enabled(y, _e) => Ok(y),
100
            Noop => Err(internal!("IfEnabled unexpectedly Noop")),
101
        }
102
124
    }
103
}