pub(crate) mod snapshot;
pub(crate) mod route;
mod context;
use std::collections::HashMap;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use axum::response::Response;
use serde::Serialize;
use serde::de::DeserializeOwned;
pub use context::LiveContext;
use crate::{Error, Result};
#[derive(Serialize)]
#[non_exhaustive]
pub struct Mounted {
pub name: &'static str,
pub id: String,
pub view: &'static str,
pub snapshot: String,
pub state: serde_json::Value,
pub data: serde_json::Value,
pub answer: bool,
}
pub fn arity(args: &[serde_json::Value], n: usize) -> Result {
if args.len() == n {
Ok(())
} else {
Err(Error::BadRequest(format!(
"expected {n} argument(s), got {}",
args.len()
)))
}
}
pub fn arg<T: DeserializeOwned>(args: &[serde_json::Value], i: usize) -> Result<T> {
let value = args
.get(i)
.ok_or_else(|| Error::BadRequest(format!("missing argument {i}")))?;
serde_json::from_value(value.clone())
.map_err(|e| Error::BadRequest(format!("argument {i}: {e}")))
}
pub(crate) async fn render<C: LiveComponent>(
ctx: &LiveContext,
c: &C,
id: String,
answer: bool,
) -> Result<Mounted> {
let state = serde_json::to_value(c).map_err(|e| Error::Internal(e.into()))?;
let data = c.data(ctx).await?;
let snapshot = snapshot::seal(
ctx.state.key.signing(),
ctx.state.config.live_snapshot_max,
C::NAME,
&id,
&state,
)?;
Ok(Mounted {
name: C::NAME,
id,
view: C::VIEW,
snapshot,
state,
data,
answer,
})
}
pub(crate) type LiveFn = Arc<
dyn Fn(
LiveContext,
String,
Vec<(String, String)>,
) -> Pin<Box<dyn Future<Output = Result<Response>> + Send>>
+ Send
+ Sync,
>;
pub(crate) type LiveMap = Arc<HashMap<&'static str, LiveFn>>;
pub(crate) fn handler<C: LiveComponent>() -> LiveFn {
Arc::new(|ctx, action, fields| Box::pin(route::run::<C>(ctx, action, fields)))
}
pub trait LiveComponent: Serialize + DeserializeOwned + Send + Sync + 'static {
const NAME: &'static str;
const VIEW: &'static str;
fn data(&self, ctx: &LiveContext) -> impl Future<Output = Result<serde_json::Value>> + Send {
let _ = ctx;
async { Ok(serde_json::Value::Object(Default::default())) }
}
fn call(
&mut self,
action: &str,
args: Vec<serde_json::Value>,
ctx: &mut LiveContext,
) -> impl Future<Output = Result> + Send;
}