Skip to main content

gosuto_livekit/room/utils/
take_cell.rs

1// Copyright 2025 LiveKit, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use parking_lot::RwLock;
16use std::{fmt::Debug, sync::Arc};
17
18/// A thread-safe cell that holds a value which can be taken exactly once.
19/// After taking the value, subsequent attempts to take it will return `None`.
20pub struct TakeCell<T> {
21    value: Arc<RwLock<Option<T>>>,
22}
23
24impl<T> TakeCell<T> {
25    pub(crate) fn new(value: T) -> Self {
26        Self { value: Arc::new(RwLock::new(Some(value))) }
27    }
28
29    /// Take ownership of the value in the cell if it matches some predicate.
30    ///
31    /// This method will only take the value if the provided predicate returns `true` when called with the current value.
32    /// If the predicate returns `false` or the value has already been taken, this method returns `None`.
33    pub(crate) fn take_if_raw(&self, predicate: impl FnOnce(&T) -> bool) -> Option<T> {
34        if self.value.read().as_ref().map_or(false, |v| predicate(v)) {
35            self.take()
36        } else {
37            None
38        }
39    }
40
41    /// Take ownership of the value in the cell. If the value has,
42    /// already been taken, the result is `None`.
43    pub fn take(&self) -> Option<T> {
44        self.value.write().take()
45    }
46
47    /// Returns whether or not the value has been taken.
48    pub fn is_taken(&self) -> bool {
49        self.value.read().is_none()
50    }
51}
52
53impl<T> Debug for TakeCell<T>
54where
55    T: Debug,
56{
57    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58        let inner = self.value.read();
59        f.debug_struct("TakeCell")
60            .field(
61                "value",
62                &inner.as_ref().map(|v| v as &dyn Debug).unwrap_or(&"<taken>" as &dyn Debug),
63            )
64            .finish()
65    }
66}
67
68impl<T> Clone for TakeCell<T> {
69    fn clone(&self) -> Self {
70        Self { value: self.value.clone() }
71    }
72}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77    #[test]
78    fn test_take() {
79        let cell = TakeCell::new(1);
80        assert_eq!(cell.is_taken(), false);
81        assert_eq!(cell.take(), Some(1));
82        assert_eq!(cell.take(), None);
83        assert_eq!(cell.is_taken(), true);
84    }
85
86    #[test]
87    fn test_take_if_raw() {
88        let cell = TakeCell::new(1);
89        assert_eq!(cell.take_if_raw(|value| *value == 2), None);
90        assert_eq!(cell.take_if_raw(|value| *value == 1), Some(1));
91        assert_eq!(cell.take_if_raw(|value| *value == 1), None);
92    }
93
94    #[test]
95    fn test_debug() {
96        let cell = TakeCell::new(1);
97        assert_eq!(format!("{:?}", cell), "TakeCell { value: 1 }");
98
99        cell.take();
100        assert_eq!(format!("{:?}", cell), "TakeCell { value: \"<taken>\" }");
101    }
102}