Skip to main content

ecat_data/
cache.rs

1// Copyright (c) 2026 erik <erik@erik.xyz> — https://erik.xyz
2use async_trait::async_trait;
3use ecat_errors::{Error, ErrorCode};
4use std::time::Duration;
5
6#[async_trait]
7pub trait Cache: Send + Sync {
8    async fn get(&self, key: &str) -> Result<Option<Vec<u8>>, Error>;
9    async fn set(&self, key: &str, value: &[u8], ttl: Duration) -> Result<(), Error>;
10    async fn delete(&self, key: &str) -> Result<(), Error>;
11
12    /// Atomically increment the value at `key` by `delta`, returning the new value.
13    /// Backends that cannot increment return an error.
14    async fn increment(&self, _key: &str, _delta: i64) -> Result<i64, Error> {
15        Err(Error::new(
16            ErrorCode::Internal,
17            "cache",
18            "increment not supported by this backend",
19        ))
20    }
21
22    /// Returns the remaining time-to-live of `key`, or `None` if the key does not exist.
23    async fn ttl(&self, _key: &str) -> Result<Option<Duration>, Error> {
24        Err(Error::new(
25            ErrorCode::Internal,
26            "cache",
27            "ttl not supported by this backend",
28        ))
29    }
30
31    /// Fetch multiple keys in one round trip. Missing keys yield `None`.
32    async fn multi_get(&self, _keys: &[&str]) -> Result<Vec<Option<Vec<u8>>>, Error> {
33        Err(Error::new(
34            ErrorCode::Internal,
35            "cache",
36            "multi_get not supported by this backend",
37        ))
38    }
39}
40
41#[cfg(test)]
42mod tests {
43    use super::*;
44
45    /// 只实现核心三操作的后端:可选操作必须走默认实现的报错路径。
46    struct MinimalCache;
47
48    #[async_trait]
49    impl Cache for MinimalCache {
50        async fn get(&self, _key: &str) -> Result<Option<Vec<u8>>, Error> {
51            Ok(None)
52        }
53        async fn set(&self, _key: &str, _value: &[u8], _ttl: Duration) -> Result<(), Error> {
54            Ok(())
55        }
56        async fn delete(&self, _key: &str) -> Result<(), Error> {
57            Ok(())
58        }
59    }
60
61    #[tokio::test]
62    async fn optional_ops_default_to_not_supported_error() {
63        let cache = MinimalCache;
64        let err = cache.increment("k", 1).await.unwrap_err();
65        assert!(
66            err.to_string().contains("increment not supported"),
67            "got: {err}"
68        );
69        let err = cache.ttl("k").await.unwrap_err();
70        assert!(err.to_string().contains("ttl not supported"), "got: {err}");
71        let err = cache.multi_get(&["a", "b"]).await.unwrap_err();
72        assert!(
73            err.to_string().contains("multi_get not supported"),
74            "got: {err}"
75        );
76    }
77}