Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ resolver = "2"

[workspace.package]
edition = "2024"
rust-version = "1.88"
rust-version = "1.89"
license = "MIT OR Apache-2.0"
homepage = "https://cot.rs"
repository = "https://github.com/cot-rs/cot"
Expand Down Expand Up @@ -67,6 +67,7 @@ async-stream = "0.3"
async-trait = "0.1"
axum = { version = "0.8", default-features = false }
backtrace = "0.3.76"
blake3 = "1.8.3"
bytes = "1.11"
cargo_toml = "0.22"
chrono = { version = "0.4.43", default-features = false }
Expand All @@ -90,6 +91,7 @@ email_address = "0.2.9"
fake = "4"
fantoccini = "0.22"
form_urlencoded = "1"
fs4 = { version = "0.13.1", features = ["tokio", "sync"] }
futures = { version = "0.3", default-features = false }
futures-core = { version = "0.3", default-features = false }
futures-util = { version = "0.3", default-features = false }
Expand Down
13 changes: 12 additions & 1 deletion cot-macros/src/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub(super) fn fn_to_cache_test(test_fn: &ItemFn) -> TokenStream {
let test_fn_name = &test_fn.sig.ident;
let memory_ident = format_ident!("{}_memory", test_fn_name);
let redis_ident = format_ident!("{}_redis", test_fn_name);
let file_ident = format_ident!("{}_file", test_fn_name);

let result = quote! {
#[::cot::test]
Expand All @@ -28,7 +29,17 @@ pub(super) fn fn_to_cache_test(test_fn: &ItemFn) -> TokenStream {
cache.cleanup().await.unwrap_or_else(|err| panic!("Failed to cleanup: {err:?}"));

#test_fn
}
}

#[::cot::test]
async fn #file_ident() {
let mut cache = cot::test::TestCache::new_file().unwrap();
#test_fn_name(&mut cache).await;

cache.cleanup().await.unwrap_or_else(|err| panic!("Failed to cleanup file cache: {err:?}"));

#test_fn
}
};
result
}
4 changes: 3 additions & 1 deletion cot/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ aide = { workspace = true, optional = true }
askama = { workspace = true, features = ["std"] }
async-trait.workspace = true
axum = { workspace = true, features = ["http1", "tokio"] }
blake3.workspace = true
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically this should also be an optional dependency, but since we plan to switch static files to use blake3 instead of sha2, it's fine to leave this as-is.

bytes.workspace = true
chrono = { workspace = true, features = ["alloc", "serde", "clock"] }
chrono-tz.workspace = true
Expand All @@ -34,6 +35,7 @@ digest.workspace = true
email_address.workspace = true
fake = { workspace = true, optional = true, features = ["derive", "chrono"] }
form_urlencoded.workspace = true
fs4 = { workspace = true, optional = true }
futures-core.workspace = true
futures-util.workspace = true
hex.workspace = true
Expand Down Expand Up @@ -116,7 +118,7 @@ json = ["dep:serde_json", "cot_core/json"]
openapi = ["json", "dep:aide", "dep:schemars"]
swagger-ui = ["openapi", "dep:swagger-ui-redist"]
live-reload = ["dep:tower-livereload"]
cache = ["json"]
cache = ["json", "fs4"]
test = []

[lib]
Expand Down
40 changes: 38 additions & 2 deletions cot/src/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ use serde::Serialize;
use serde::de::DeserializeOwned;
use thiserror::Error;

use crate::cache::store::file::FileStore;
use crate::cache::store::memory::Memory;
#[cfg(feature = "redis")]
use crate::cache::store::redis::Redis;
Expand Down Expand Up @@ -786,9 +787,12 @@ impl Cache {
let redis_store = Redis::new(url, pool_size)?;
Self::new(redis_store, config.prefix.clone(), config.timeout)
}
_ => {
unimplemented!();
CacheStoreTypeConfig::File { ref path } => {
let file_store = FileStore::new(path.clone())?;
Self::new(file_store, config.prefix.clone(), config.timeout)
}
#[expect(unused)]
_ => unimplemented!(),
}
};

Expand Down Expand Up @@ -981,4 +985,36 @@ mod tests {
let result = Cache::from_config(&config).await;
assert!(result.is_ok());
}

#[cot::test]
async fn test_cache_from_config_file() {
use std::env;

use crate::config::{CacheConfig, CacheStoreConfig};

let temp_path =
env::temp_dir().join(format!("test_cot_cache_file_store_{}", std::process::id()));
let cloned_path = temp_path.clone();

let config = CacheConfig::builder()
.store(CacheStoreConfig {
store_type: CacheStoreTypeConfig::File { path: temp_path },
})
.prefix("test_file")
.timeout(Timeout::After(Duration::from_secs(60)))
.build();

let cache = Cache::from_config(&config)
.await
.expect("Failed to create cache from config");

cache
.insert("test_key", "test_value".to_string())
.await
.unwrap();
let retrieved = cache.get("test_key").await.unwrap();
assert_eq!(retrieved, Some("test_value".to_string()));

let _ = tokio::fs::remove_dir_all(&cloned_path).await;
}
}
1 change: 1 addition & 0 deletions cot/src/cache/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
//! provide a simple asynchronous interface for storing, retrieving, and
//! managing cached values, optionally with expiration policies.

pub mod file;
Copy link
Member

@m4tx m4tx Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For a full implementation, please take a look at config.rs whether it also needs to be modified, so that the users can easily switch between cache implementations in the config TOML file. This will also require a change here:

_ => {

pub mod memory;
#[cfg(feature = "redis")]
pub mod redis;
Expand Down
Loading