Skip to main content

coil_wasm/output/
cache.rs

1use std::collections::BTreeSet;
2
3use crate::error::WasmModelError;
4use crate::validation::validate_token;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum CacheVisibility {
8    Public,
9    Private,
10}
11
12#[derive(Debug, Clone, PartialEq, Eq)]
13pub struct TypedCacheHint {
14    pub visibility: CacheVisibility,
15    pub max_age_seconds: u64,
16    pub stale_while_revalidate_seconds: Option<u64>,
17    pub vary_by_locale: bool,
18    pub vary_by_user: bool,
19    pub vary_by_session: bool,
20    pub tags: BTreeSet<String>,
21}
22
23impl TypedCacheHint {
24    pub fn new(
25        visibility: CacheVisibility,
26        max_age_seconds: u64,
27        stale_while_revalidate_seconds: Option<u64>,
28        vary_by_locale: bool,
29        vary_by_user: bool,
30        vary_by_session: bool,
31        tags: impl IntoIterator<Item = impl Into<String>>,
32    ) -> Result<Self, WasmModelError> {
33        let tags = tags
34            .into_iter()
35            .map(|tag| validate_token("cache_tag", tag.into()))
36            .collect::<Result<BTreeSet<_>, _>>()?;
37        let hint = Self {
38            visibility,
39            max_age_seconds,
40            stale_while_revalidate_seconds,
41            vary_by_locale,
42            vary_by_user,
43            vary_by_session,
44            tags,
45        };
46        hint.validate()?;
47        Ok(hint)
48    }
49
50    pub(crate) fn validate(&self) -> Result<(), WasmModelError> {
51        if self.visibility == CacheVisibility::Public && (self.vary_by_user || self.vary_by_session)
52        {
53            return Err(WasmModelError::InvalidTypedReturn {
54                reason: "public cache hints cannot vary by user or session".to_string(),
55            });
56        }
57        if self
58            .stale_while_revalidate_seconds
59            .is_some_and(|value| value == 0)
60        {
61            return Err(WasmModelError::InvalidTypedReturn {
62                reason: "stale-while-revalidate must be greater than zero".to_string(),
63            });
64        }
65        for tag in &self.tags {
66            let _ = validate_token("cache_tag", tag.clone())?;
67        }
68        Ok(())
69    }
70
71    pub fn merge_from(&mut self, other: &Self) {
72        self.visibility = match (self.visibility, other.visibility) {
73            (CacheVisibility::Private, _) | (_, CacheVisibility::Private) => {
74                CacheVisibility::Private
75            }
76            _ => CacheVisibility::Public,
77        };
78        self.max_age_seconds = self.max_age_seconds.min(other.max_age_seconds);
79        self.stale_while_revalidate_seconds = match (
80            self.stale_while_revalidate_seconds,
81            other.stale_while_revalidate_seconds,
82        ) {
83            (Some(left), Some(right)) => Some(left.min(right)),
84            _ => None,
85        };
86        self.vary_by_locale |= other.vary_by_locale;
87        self.vary_by_user |= other.vary_by_user;
88        self.vary_by_session |= other.vary_by_session;
89        self.tags.extend(other.tags.iter().cloned());
90    }
91}