Skip to main content

eui_core/host/
host_params.rs

1//! VAPP-206 (spec docs/spec/apps.md §4.1): the HOST-PROVIDED binding params,
2//! the twin of `packages/exponential-ui/src/host/host-params.ts` (read its
3//! header). `${today}`, `${now}` and `${month}` in the team zone, read at each
4//! bind; every host adds them to its `applyTemplate` and `bindDataModel`
5//! messages (`hostParams`) and the pure router fills them.
6
7use serde_json::{json, Map, Value};
8
9use super::sources::encode_component;
10use crate::cel::{evaluate_expression, ExpressionEnv};
11
12/// The params at `now` (epoch ms; `None` = the wall clock) in the team zone
13/// (minutes east of UTC at an instant; `None` = UTC).
14pub fn host_params(now: Option<f64>, offset: Option<&(dyn Fn(f64) -> i32 + Send + Sync)>) -> Value {
15    let env = ExpressionEnv { now, offset, max_steps: None };
16    let vars = Map::new();
17    let read = |expr: &str| evaluate_expression(expr, &vars, &env).ok().and_then(|v| v.as_str().map(str::to_string)).unwrap_or_default();
18    let today = read("today()");
19    let month: String = today.chars().take(7).collect();
20    json!({"today": today, "now": read("now()"), "month": month})
21}
22
23/// A source's `${today}`, `${now}`, `${month}` from the host's params
24/// (percent-encoded like a query value). Without them it stays as written.
25pub fn fill_host_params(source: &str, params: Option<&Value>) -> String {
26    let mut out = source.to_string();
27    let Some(params) = params.and_then(Value::as_object) else { return out };
28    for name in ["today", "now", "month"] {
29        if let Some(v) = params.get(name).and_then(Value::as_str) {
30            out = out.replace(&format!("${{{name}}}"), &encode_component(v));
31        }
32    }
33    out
34}
35
36/// The message with `hostParams` in its `applyTemplate` or `bindDataModel`
37/// body; any other message as it is.
38pub fn with_host_params(message: &Value, params: &Value) -> Value {
39    let mut out = message.clone();
40    for kind in ["applyTemplate", "bindDataModel"] {
41        if let Some(body) = out.get_mut(kind).and_then(Value::as_object_mut) {
42            body.insert("hostParams".into(), params.clone());
43            return out;
44        }
45    }
46    out
47}