use std::sync::Arc;
use crate::backend::Store;
use crate::server_core::{
Admission, AllowAll, CanonicalRequest, Clock, Core, CoreError, CoreResponse, Journal, Log,
Policy,
};
use axum::body::Bytes;
use axum::http::HeaderName;
use axum::http::header::{AUTHORIZATION, IF_MATCH, IF_NONE_MATCH};
use axum::http::request::Parts;
use crate::server::config::ServerConfig;
struct TracingLog;
impl Log for TracingLog {
fn error(&self, message: &str) {
tracing::error!("{message}");
}
fn warn(&self, message: &str) {
tracing::warn!("{message}");
}
}
struct Inner {
core: Core,
config: ServerConfig,
}
#[derive(Clone)]
pub struct AppState(Arc<Inner>);
impl AppState {
pub fn new(
store: Arc<dyn Store>,
journal: Arc<dyn Journal>,
config: ServerConfig,
clock: Arc<dyn Clock>,
) -> anyhow::Result<AppState> {
config.validate()?;
let admission: Arc<dyn Admission> = Arc::new(AllowAll);
let policy = Policy {
admission,
idle_expiry_days: None,
limits: config.limits,
server: config.advertised_server(),
};
let core = Core::open(store, journal, policy, clock)?;
Ok(AppState::assemble(core, config))
}
pub fn from_core(core: Core, config: ServerConfig) -> AppState {
AppState::assemble(core, config)
}
fn assemble(core: Core, config: ServerConfig) -> AppState {
AppState(Arc::new(Inner {
core: core.with_log(Arc::new(TracingLog)),
config,
}))
}
pub fn config(&self) -> &ServerConfig {
&self.0.config
}
pub fn core(&self) -> &Core {
&self.0.core
}
pub(crate) async fn call(&self, parts: Parts, body: Bytes) -> CoreResponse {
let state = self.clone();
let dispatch = tracing::dispatcher::get_default(Clone::clone);
tokio::task::spawn_blocking(move || {
let header = |name: HeaderName| parts.headers.get(name).and_then(|v| v.to_str().ok());
let request = CanonicalRequest {
method: parts.method.as_str(),
path_and_query: parts.uri.path_and_query().map_or("/", |pq| pq.as_str()),
authorization: header(AUTHORIZATION),
if_match: header(IF_MATCH),
if_none_match: header(IF_NONE_MATCH),
body: &body,
};
tracing::dispatcher::with_default(&dispatch, || state.0.core.call(request))
})
.await
.unwrap_or_else(|_| CoreError::internal().to_response())
}
pub async fn journal_ready(&self) -> bool {
let state = self.clone();
tokio::task::spawn_blocking(move || state.0.core.journal_ready())
.await
.unwrap_or(false)
}
}