Skip to main content

rust_i18n_support/
atomic_str.rs

1use std::fmt;
2use std::ops::Deref;
3
4use arc_swap::{ArcSwapAny, Guard};
5use triomphe::Arc;
6
7/// A thread-safe atomically reference-counting string.
8pub struct AtomicStr(ArcSwapAny<Arc<String>>);
9
10/// A thread-safe view the string that was stored when `AtomicStr::as_str()` was called.
11struct GuardedStr(Guard<Arc<String>>);
12
13impl Deref for GuardedStr {
14    type Target = str;
15
16    #[inline]
17    fn deref(&self) -> &Self::Target {
18        self.0.as_str()
19    }
20}
21
22impl AtomicStr {
23    /// Create a new `AtomicStr` with the given value.
24    pub fn new(value: &str) -> Self {
25        let arced = Arc::new(value.into());
26        Self(ArcSwapAny::new(arced))
27    }
28
29    /// Get the string slice.
30    #[inline]
31    pub fn as_str(&self) -> impl Deref<Target = str> {
32        GuardedStr(self.0.load())
33    }
34
35    /// Replaces the value at self with src.
36    pub fn replace(&self, src: impl Into<String>) {
37        let arced = Arc::new(src.into());
38        self.0.store(arced);
39    }
40}
41
42impl From<&str> for AtomicStr {
43    fn from(value: &str) -> Self {
44        Self::new(value)
45    }
46}
47
48impl fmt::Display for AtomicStr {
49    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50        f.write_str(&self.as_str())
51    }
52}
53
54#[cfg(test)]
55mod tests {
56    use super::*;
57
58    fn test_str(s: &str) {
59        assert_eq!(s, "hello");
60    }
61
62    #[test]
63    fn test_atomic_str() {
64        let s = AtomicStr::from("hello");
65        test_str(&s.as_str());
66    }
67}