Skip to main content

defuse_core/
lock.rs

1use std::io;
2
3use borsh::{BorshDeserialize, BorshSerialize};
4use defuse_borsh_utils::{AsWrap, BorshDeserializeAs, BorshSerializeAs};
5use serde::{Deserialize, Serialize};
6
7/// A persistent lock, which stores its state (whether it's locked or unlocked)
8/// on-chain, so that the inner value can be accessed depending on
9/// the current state of the lock.
10#[cfg_attr(feature = "schemars-v0_8", derive(::schemars::JsonSchema))]
11#[cfg_attr(feature = "borsh-schema", derive(::borsh::BorshSchema))]
12#[derive(
13    Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshSerialize, BorshDeserialize,
14)]
15pub struct Lock<T> {
16    #[serde(
17        default,
18        // do not serialize `false`
19        skip_serializing_if = "::core::ops::Not::not"
20    )]
21    locked: bool,
22    #[serde(flatten)]
23    value: T,
24}
25
26impl<T> Lock<T> {
27    #[must_use]
28    #[inline]
29    pub const fn new(locked: bool, value: T) -> Self {
30        Self { locked, value }
31    }
32
33    #[must_use]
34    #[inline]
35    pub const fn unlocked(value: T) -> Self {
36        Self::new(false, value)
37    }
38
39    #[must_use]
40    #[inline]
41    pub const fn locked(value: T) -> Self {
42        Self::new(true, value)
43    }
44
45    #[inline]
46    pub const fn set_locked(&mut self, locked: bool) -> &mut Self {
47        self.locked = locked;
48        self
49    }
50
51    /// # Safety
52    /// This method bypasses lock state checks. Use only when you need to access
53    /// the inner value regardless of lock state, such as for read operations
54    /// or when implementing higher-level locking logic.
55    #[inline]
56    pub const fn as_inner_unchecked(&self) -> &T {
57        &self.value
58    }
59
60    /// # Safety
61    /// This method bypasses lock state checks. Use only when you need mutable access
62    /// to the inner value regardless of lock state. Misuse can compromise locking semantics.
63    #[inline]
64    pub const fn as_inner_unchecked_mut(&mut self) -> &mut T {
65        &mut self.value
66    }
67
68    #[inline]
69    pub fn into_inner_unchecked(self) -> T {
70        self.value
71    }
72
73    #[must_use]
74    #[inline]
75    pub const fn is_locked(&self) -> bool {
76        self.locked
77    }
78
79    #[must_use]
80    #[inline]
81    pub const fn as_locked(&self) -> Option<&T> {
82        if !self.is_locked() {
83            return None;
84        }
85        Some(self.as_inner_unchecked())
86    }
87
88    #[must_use]
89    #[inline]
90    pub const fn as_locked_mut(&mut self) -> Option<&mut T> {
91        if !self.is_locked() {
92            return None;
93        }
94        Some(self.as_inner_unchecked_mut())
95    }
96
97    #[must_use]
98    #[inline]
99    pub const fn as_locked_mut_maybe_forced(&mut self, force: bool) -> Option<&mut T> {
100        if force {
101            Some(self.as_inner_unchecked_mut())
102        } else {
103            self.as_locked_mut()
104        }
105    }
106
107    #[must_use]
108    #[inline]
109    pub fn into_locked(self) -> Option<T> {
110        if !self.is_locked() {
111            return None;
112        }
113        Some(self.value)
114    }
115
116    #[must_use]
117    #[inline]
118    pub const fn lock(&mut self) -> Option<&mut T> {
119        if self.is_locked() {
120            return None;
121        }
122        self.locked = true;
123        Some(self.as_inner_unchecked_mut())
124    }
125
126    #[inline]
127    pub const fn force_lock(&mut self) -> &mut T {
128        self.locked = true;
129        self.as_inner_unchecked_mut()
130    }
131
132    #[must_use]
133    #[inline]
134    pub const fn get(&self) -> Option<&T> {
135        if self.is_locked() {
136            return None;
137        }
138        Some(self.as_inner_unchecked())
139    }
140
141    #[must_use]
142    #[inline]
143    pub const fn get_mut(&mut self) -> Option<&mut T> {
144        if self.is_locked() {
145            return None;
146        }
147        Some(self.as_inner_unchecked_mut())
148    }
149
150    #[must_use]
151    #[inline]
152    pub const fn get_mut_maybe_forced(&mut self, force: bool) -> Option<&mut T> {
153        if force {
154            Some(self.as_inner_unchecked_mut())
155        } else {
156            self.get_mut()
157        }
158    }
159
160    #[must_use]
161    #[inline]
162    pub fn into_unlocked(self) -> Option<T> {
163        if self.is_locked() {
164            return None;
165        }
166        Some(self.value)
167    }
168
169    #[must_use]
170    #[inline]
171    pub const fn unlock(&mut self) -> Option<&mut T> {
172        if !self.is_locked() {
173            return None;
174        }
175        self.locked = false;
176        Some(self.as_inner_unchecked_mut())
177    }
178
179    #[inline]
180    pub const fn force_unlock(&mut self) -> &mut T {
181        self.locked = false;
182        self.as_inner_unchecked_mut()
183    }
184
185    #[inline]
186    pub const fn as_ref(&self) -> Lock<&T> {
187        Lock::new(self.is_locked(), self.as_inner_unchecked())
188    }
189
190    #[inline]
191    pub const fn as_mut(&mut self) -> Lock<&mut T> {
192        Lock::new(self.is_locked(), self.as_inner_unchecked_mut())
193    }
194
195    #[inline]
196    pub fn map_inner_unchecked<U, F>(self, f: F) -> Lock<U>
197    where
198        F: FnOnce(T) -> U,
199    {
200        Lock::new(self.is_locked(), f(self.into_inner_unchecked()))
201    }
202}
203
204impl<T> From<T> for Lock<T> {
205    #[inline]
206    fn from(value: T) -> Self {
207        Self::unlocked(value)
208    }
209}
210
211impl<T, As> BorshSerializeAs<Lock<T>> for Lock<As>
212where
213    As: BorshSerializeAs<T>,
214{
215    #[inline]
216    fn serialize_as<W>(source: &Lock<T>, writer: &mut W) -> io::Result<()>
217    where
218        W: io::Write,
219    {
220        Lock {
221            locked: source.locked,
222            value: AsWrap::<&T, &As>::new(&source.value),
223        }
224        .serialize(writer)
225    }
226}
227
228impl<T, As> BorshDeserializeAs<Lock<T>> for Lock<As>
229where
230    As: BorshDeserializeAs<T>,
231{
232    #[inline]
233    fn deserialize_as<R>(reader: &mut R) -> io::Result<Lock<T>>
234    where
235        R: io::Read,
236    {
237        Lock::<AsWrap<T, As>>::deserialize_reader(reader).map(|v| Lock {
238            locked: v.locked,
239            value: v.value.into_inner(),
240        })
241    }
242}
243
244#[cfg(test)]
245#[test]
246fn test() {
247    let mut a = Lock::new(false, 0);
248
249    assert!(!a.is_locked());
250    assert_eq!(a.unlock(), None);
251
252    assert_eq!(a.get().copied(), Some(0));
253    *a.get_mut().unwrap() += 1;
254    assert_eq!(*a.as_inner_unchecked(), 1);
255
256    assert_eq!(a.lock().copied(), Some(1));
257    assert!(a.is_locked());
258
259    assert_eq!(a.as_locked().copied(), Some(1));
260    *a.as_locked_mut().unwrap() += 1;
261    assert_eq!(*a.as_inner_unchecked(), 2);
262}