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    fn deref(&self) -> &Self::Target {
17        self.0.as_str()
18    }
19}
20
21impl AtomicStr {
22    /// Create a new `AtomicStr` with the given value.
23    pub fn new(value: &str) -> Self {
24        let arced = Arc::new(value.into());
25        Self(ArcSwapAny::new(arced))
26    }
27
28    /// Get the string slice.
29    pub fn as_str(&self) -> impl Deref<Target = str> {
30        GuardedStr(self.0.load())
31    }
32
33    /// Replaces the value at self with src.
34    pub fn replace(&self, src: impl Into<String>) {
35        let arced = Arc::new(src.into());
36        self.0.store(arced);
37    }
38}
39
40impl From<&str> for AtomicStr {
41    fn from(value: &str) -> Self {
42        Self::new(value)
43    }
44}
45
46impl fmt::Display for AtomicStr {
47    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48        f.write_str(&self.as_str())
49    }
50}
51
52#[cfg(test)]
53mod tests {
54    use super::*;
55
56    fn test_str(s: &str) {
57        assert_eq!(s, "hello");
58    }
59
60    #[test]
61    fn test_atomic_str() {
62        let s = AtomicStr::from("hello");
63        test_str(&s.as_str());
64    }
65}