use std::{net::Ipv4Addr, time::Duration};
use kynos::{
Router,
middleware::{
cache::{
Cache,
store::{CacheStore, PrimaryKey, StoredResponse},
},
conditional::Conditional,
},
prelude::*,
response::headers::WithHeaders,
server::Server,
};
use serde::{Deserialize, Serialize};
struct MokaCache {
stored: moka::future::Cache<PrimaryKey, Vec<StoredResponse>>,
}
impl MokaCache {
fn new() -> Self {
Self {
stored: moka::future::Cache::builder()
.max_capacity(10_000)
.time_to_live(Duration::from_secs(600))
.build(),
}
}
}
impl<C: Sync> CacheStore<C> for MokaCache {
async fn get(&self, key: &PrimaryKey, _: &C) -> Vec<StoredResponse> {
self.stored.get(key).await.unwrap_or_default()
}
async fn put(&self, key: PrimaryKey, response: StoredResponse, _: &C) {
let mut variants = self.stored.get(&key).await.unwrap_or_default();
variants.retain(|stored| stored.vary() != response.vary());
variants.push(response);
self.stored.insert(key, variants).await;
}
async fn invalidate(&self, key: &PrimaryKey, _: &C) {
self.stored.invalidate(key).await;
}
}
#[derive(Schema, Serialize, Deserialize)]
struct Report {
id: u64,
title: String,
}
#[derive(Clone, Copy, Debug)]
struct Cacheable;
impl kynos::extract::params::header::HeaderParams for Cacheable {
const NAMES: &'static [&'static str] = &["cache-control"];
const DESCRIBED: bool = false;
}
impl kynos::extract::params::header::EncodeHeaders for Cacheable {
fn encode(&self) -> Vec<(kynos::http::HeaderName, kynos::http::HeaderValue)> {
vec![(
kynos::http::header::CACHE_CONTROL,
kynos::http::HeaderValue::from_static("public, s-maxage=60"),
)]
}
}
#[kynos::get("/reports/{id}")]
async fn report(Path(path): Path<ReportPath>) -> WithHeaders<Json<Report>, Cacheable> {
WithHeaders::new(
Json(Report {
id: path.id,
title: format!("Report {}", path.id),
}),
Cacheable,
)
}
#[derive(Schema, kynos::PathParams)]
struct ReportPath {
id: u64,
}
#[tokio::main]
async fn main() -> kynos::Result<()> {
let router = Router::<()>::new()
.mount(kynos::routes![report])
.intercept(Conditional::new())
.intercept(
Cache::new(MokaCache::new())
.namespace("v1")
.deriving_etags(),
);
println!("{}", router.openapi()?.to_json()?);
Server::new(router.build(())?)
.bind((Ipv4Addr::UNSPECIFIED, 3000))
.serve()
.await
}