Skip to main content

coil_cache/
types.rs

1use std::fmt;
2
3use crate::CacheModelError;
4use serde::{Deserialize, Serialize};
5
6pub(crate) fn require_non_empty(
7    field: &'static str,
8    value: String,
9) -> Result<String, CacheModelError> {
10    let trimmed = value.trim();
11    if trimmed.is_empty() {
12        Err(CacheModelError::EmptyField { field })
13    } else {
14        Ok(trimmed.to_string())
15    }
16}
17
18pub(crate) fn validate_token(
19    field: &'static str,
20    value: String,
21) -> Result<String, CacheModelError> {
22    let trimmed = require_non_empty(field, value)?;
23    if trimmed
24        .chars()
25        .all(|ch| ch.is_ascii_alphanumeric() || matches!(ch, '-' | '_' | '.' | ':' | '/'))
26    {
27        Ok(trimmed)
28    } else {
29        Err(CacheModelError::InvalidToken {
30            field,
31            value: trimmed,
32        })
33    }
34}
35
36#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
37pub struct VariationKey(String);
38
39impl VariationKey {
40    pub fn new(value: impl Into<String>) -> Self {
41        Self(value.into())
42    }
43
44    pub fn as_str(&self) -> &str {
45        &self.0
46    }
47}
48
49impl fmt::Display for VariationKey {
50    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51        f.write_str(&self.0)
52    }
53}
54
55#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
56pub struct InvalidationTag(String);
57
58impl InvalidationTag {
59    pub fn new(value: impl Into<String>) -> Result<Self, CacheModelError> {
60        Ok(Self(validate_token("invalidation_tag", value.into())?))
61    }
62
63    pub fn as_str(&self) -> &str {
64        &self.0
65    }
66}
67
68impl fmt::Display for InvalidationTag {
69    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70        f.write_str(&self.0)
71    }
72}
73
74#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
75pub struct CacheNamespace(String);
76
77impl CacheNamespace {
78    pub fn new(value: impl Into<String>) -> Result<Self, CacheModelError> {
79        Ok(Self(validate_token("cache_namespace", value.into())?))
80    }
81
82    pub fn as_str(&self) -> &str {
83        &self.0
84    }
85}
86
87impl fmt::Display for CacheNamespace {
88    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89        f.write_str(&self.0)
90    }
91}
92
93#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
94pub struct CacheKey {
95    namespace: CacheNamespace,
96    resource: String,
97    variation: Option<VariationKey>,
98}
99
100impl CacheKey {
101    pub fn new(
102        namespace: CacheNamespace,
103        resource: impl Into<String>,
104        variation: Option<VariationKey>,
105    ) -> Result<Self, CacheModelError> {
106        Ok(Self {
107            namespace,
108            resource: require_non_empty("cache_resource", resource.into())?,
109            variation,
110        })
111    }
112
113    pub fn namespace(&self) -> &CacheNamespace {
114        &self.namespace
115    }
116
117    pub fn resource(&self) -> &str {
118        &self.resource
119    }
120
121    pub fn variation(&self) -> Option<&VariationKey> {
122        self.variation.as_ref()
123    }
124}
125
126impl fmt::Display for CacheKey {
127    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128        match &self.variation {
129            Some(variation) => write!(f, "{}:{}|{}", self.namespace, self.resource, variation),
130            None => write!(f, "{}:{}", self.namespace, self.resource),
131        }
132    }
133}
134
135#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
136pub struct EntityTag(String);
137
138impl EntityTag {
139    pub fn new(value: impl Into<String>) -> Result<Self, CacheModelError> {
140        Ok(Self(validate_token("etag", value.into())?))
141    }
142
143    pub fn as_str(&self) -> &str {
144        &self.0
145    }
146}
147
148impl fmt::Display for EntityTag {
149    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150        f.write_str(&self.0)
151    }
152}
153
154#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
155pub struct CacheInstant(u64);
156
157impl CacheInstant {
158    pub const fn from_unix_seconds(seconds: u64) -> Self {
159        Self(seconds)
160    }
161
162    pub const fn as_unix_seconds(self) -> u64 {
163        self.0
164    }
165}