Skip to main content

osom_lib_strings/immutable/serde/
seeded.rs

1#![allow(clippy::elidable_lifetime_names)]
2
3use osom_lib_alloc::traits::Allocator;
4use serde::de;
5
6use crate::immutable::ImmutableString;
7
8/// Represents a generic cache error.
9#[repr(transparent)]
10#[derive(Debug, PartialEq, Eq, Hash)]
11#[must_use]
12pub struct CacheError {
13    message: &'static str,
14}
15
16impl CacheError {
17    #[inline(always)]
18    pub const fn new(message: &'static str) -> Self {
19        Self { message }
20    }
21}
22
23impl core::fmt::Display for CacheError {
24    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
25        write!(f, "CacheError: {}", self.message)
26    }
27}
28
29impl AsRef<str> for CacheError {
30    fn as_ref(&self) -> &str {
31        self.message
32    }
33}
34
35/// Represents a trait for a cache of immutable strings.
36#[must_use]
37pub trait StringCache {
38    type TAllocator: Allocator;
39
40    /// Gets the immutable string from the cache and caches it if it is not present.
41    ///
42    /// # Errors
43    ///
44    /// For details see [`CacheError`].
45    fn get_and_cache(&mut self, value: &str) -> Result<ImmutableString<Self::TAllocator>, CacheError>;
46}
47
48/// The main seed for deserializing cached immutable strings.
49#[must_use]
50pub struct CachedStringSeed<'a, TStringCache: StringCache> {
51    cache: &'a mut TStringCache,
52}
53
54impl<'a, TStringCache: StringCache> CachedStringSeed<'a, TStringCache> {
55    pub fn new(cache: &'a mut TStringCache) -> Self {
56        Self { cache }
57    }
58}
59
60impl<'a, TStringCache: StringCache> de::Visitor<'_> for CachedStringSeed<'a, TStringCache> {
61    type Value = ImmutableString<TStringCache::TAllocator>;
62
63    fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
64        formatter.write_str("a string")
65    }
66
67    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
68    where
69        E: de::Error,
70    {
71        let value = self.cache.get_and_cache(v).map_err(E::custom)?;
72        Ok(value)
73    }
74
75    #[cfg(feature = "std")]
76    fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
77    where
78        E: de::Error,
79    {
80        let value = self.cache.get_and_cache(&v).map_err(E::custom)?;
81        Ok(value)
82    }
83
84    fn visit_borrowed_str<E>(self, v: &str) -> Result<Self::Value, E>
85    where
86        E: de::Error,
87    {
88        let value = self.cache.get_and_cache(v).map_err(E::custom)?;
89        Ok(value)
90    }
91}
92
93impl<'de, 'a, TStringCache: StringCache> de::DeserializeSeed<'de> for CachedStringSeed<'a, TStringCache> {
94    type Value = ImmutableString<TStringCache::TAllocator>;
95
96    fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
97    where
98        D: de::Deserializer<'de>,
99    {
100        let visitor = CachedStringSeed::new(self.cache);
101        let result = deserializer.deserialize_string(visitor)?;
102        Ok(result)
103    }
104}