pub mod fast;
pub mod fast_v2;
pub mod fast_v3;
pub mod webui;
use crate::{ResponseWriter, Result};
use webui_protocol::WebUIProtocol;
pub struct WebUiTemplatePayload<'a> {
pub tag_name: &'a str,
pub template_json: &'a str,
pub template_functions: &'a str,
}
pub struct BootstrapExtensionContext<'a> {
pub protocol: &'a WebUIProtocol,
pub components: &'a [String],
pub payloads: &'a [WebUiTemplatePayload<'a>],
pub nonce: Option<&'a str>,
}
pub trait HandlerPlugin: Send {
fn push_scope(&mut self);
fn pop_scope(&mut self);
fn on_binding_start(
&mut self,
name: &str,
raw: bool,
writer: &mut dyn ResponseWriter,
) -> Result<()>;
fn on_binding_end(
&mut self,
name: &str,
raw: bool,
writer: &mut dyn ResponseWriter,
) -> Result<()>;
fn on_for_start(&mut self, name: &str, writer: &mut dyn ResponseWriter) -> Result<()> {
self.on_binding_start(name, false, writer)
}
fn on_for_end(&mut self, name: &str, writer: &mut dyn ResponseWriter) -> Result<()> {
self.on_binding_end(name, false, writer)
}
fn on_if_start(&mut self, name: &str, writer: &mut dyn ResponseWriter) -> Result<()> {
self.on_binding_start(name, false, writer)
}
fn on_if_end(&mut self, name: &str, writer: &mut dyn ResponseWriter) -> Result<()> {
self.on_binding_end(name, false, writer)
}
fn on_repeat_item_start(&mut self, index: usize, writer: &mut dyn ResponseWriter)
-> Result<()>;
fn on_repeat_item_end(&mut self, index: usize, writer: &mut dyn ResponseWriter) -> Result<()>;
fn on_element_data(&mut self, data: &[u8], writer: &mut dyn ResponseWriter) -> Result<()>;
fn write_route_component_state(
&self,
_state: &serde_json::Value,
_writer: &mut dyn ResponseWriter,
) -> Result<()> {
Ok(())
}
fn emit_templates(
&self,
protocol: &WebUIProtocol,
components: &[String],
_nonce: Option<&str>,
writer: &mut dyn ResponseWriter,
) -> Result<()> {
emit_component_templates(protocol, components, writer)
}
fn collect_template_payloads<'a>(
&self,
_protocol: &'a WebUIProtocol,
_components: &[String],
) -> Option<Vec<WebUiTemplatePayload<'a>>> {
None
}
fn emit_templates_slice(
&self,
protocol: &WebUIProtocol,
tags: &[&str],
_nonce: Option<&str>,
writer: &mut dyn ResponseWriter,
) -> Result<()> {
emit_component_templates_slice(protocol, tags, writer)
}
fn collect_template_payloads_slice<'a>(
&self,
_protocol: &'a WebUIProtocol,
_tags: &[&str],
) -> Option<Vec<WebUiTemplatePayload<'a>>> {
None
}
fn emit_bootstrap_extension_payloads(
&self,
_payloads: &[WebUiTemplatePayload<'_>],
_nonce: Option<&str>,
_writer: &mut dyn ResponseWriter,
) -> Result<()> {
Ok(())
}
fn emit_bootstrap_extension(
&self,
_context: BootstrapExtensionContext<'_>,
_writer: &mut dyn ResponseWriter,
) -> Result<()> {
Ok(())
}
}
pub(crate) fn emit_component_templates(
protocol: &WebUIProtocol,
components: &[String],
writer: &mut dyn ResponseWriter,
) -> Result<()> {
for name in components {
if let Some(template) = protocol
.components
.get(name)
.map(|component| component.template.as_str())
.filter(|template| !template.is_empty())
{
writer.write(template)?;
}
}
Ok(())
}
pub(crate) fn emit_component_templates_slice(
protocol: &WebUIProtocol,
tags: &[&str],
writer: &mut dyn ResponseWriter,
) -> Result<()> {
for &name in tags {
if let Some(template) = protocol
.components
.get(name)
.map(|component| component.template.as_str())
.filter(|template| !template.is_empty())
{
writer.write(template)?;
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use std::cell::Cell;
use super::HandlerPlugin;
use crate::{ResponseWriter, Result};
#[derive(Default)]
struct SendOnlyPlugin {
scope_depth: Cell<usize>,
binding_calls: Cell<usize>,
raw_binding_calls: Cell<usize>,
}
struct TestWriter;
impl ResponseWriter for TestWriter {
fn write(&mut self, _content: &str) -> Result<()> {
Ok(())
}
fn end(&mut self) -> Result<()> {
Ok(())
}
}
impl HandlerPlugin for SendOnlyPlugin {
fn push_scope(&mut self) {
self.scope_depth
.set(self.scope_depth.get().saturating_add(1));
}
fn pop_scope(&mut self) {
self.scope_depth
.set(self.scope_depth.get().saturating_sub(1));
}
fn on_binding_start(
&mut self,
_name: &str,
raw: bool,
_writer: &mut dyn ResponseWriter,
) -> Result<()> {
self.binding_calls
.set(self.binding_calls.get().saturating_add(1));
if raw {
self.raw_binding_calls
.set(self.raw_binding_calls.get().saturating_add(1));
}
Ok(())
}
fn on_binding_end(
&mut self,
_name: &str,
raw: bool,
_writer: &mut dyn ResponseWriter,
) -> Result<()> {
self.binding_calls
.set(self.binding_calls.get().saturating_add(1));
if raw {
self.raw_binding_calls
.set(self.raw_binding_calls.get().saturating_add(1));
}
Ok(())
}
fn on_repeat_item_start(
&mut self,
_index: usize,
_writer: &mut dyn ResponseWriter,
) -> Result<()> {
Ok(())
}
fn on_repeat_item_end(
&mut self,
_index: usize,
_writer: &mut dyn ResponseWriter,
) -> Result<()> {
Ok(())
}
fn on_element_data(
&mut self,
_data: &[u8],
_writer: &mut dyn ResponseWriter,
) -> Result<()> {
Ok(())
}
}
#[test]
fn handler_plugin_is_send_but_does_not_require_sync() {
fn assert_send<T: Send + ?Sized>() {}
fn assert_plugin<T: HandlerPlugin>() {}
assert_send::<dyn HandlerPlugin>();
assert_plugin::<SendOnlyPlugin>();
}
#[test]
fn binding_hooks_receive_raw_range_flag() {
let mut plugin = SendOnlyPlugin::default();
let mut writer = TestWriter;
plugin
.on_binding_start("trusted_html", true, &mut writer)
.unwrap();
plugin
.on_binding_end("trusted_html", true, &mut writer)
.unwrap();
assert_eq!(plugin.binding_calls.get(), 2);
assert_eq!(plugin.raw_binding_calls.get(), 2);
}
}