pub mod fast;
pub mod fast_v2;
pub mod fast_v3;
pub mod webui;
use crate::component_registry::Component;
use crate::{ParserOptions, Result};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AttributeAction {
Keep,
Skip,
SkipAndCountBinding,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ParserPluginArtifacts {
None,
ComponentTemplates(Vec<ComponentTemplateArtifact>),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum StateSurface {
None,
Keys(Vec<String>),
All,
}
impl StateSurface {
#[must_use]
pub fn keys(mut keys: Vec<String>) -> Self {
keys.sort_unstable();
keys.dedup();
if keys.is_empty() {
Self::None
} else {
Self::Keys(keys)
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ComponentTemplateArtifact {
pub tag_name: String,
pub template: String,
pub template_json: String,
pub template_functions: String,
pub hydration: StateSurface,
pub navigation: StateSurface,
pub template_roots: Vec<String>,
pub is_scripted: bool,
}
impl ComponentTemplateArtifact {
#[must_use]
pub fn template(tag_name: String, template: String) -> Self {
Self {
tag_name,
template,
template_json: String::new(),
template_functions: String::new(),
hydration: StateSurface::None,
navigation: StateSurface::None,
template_roots: Vec::new(),
is_scripted: false,
}
}
#[must_use]
pub fn webui(tag_name: String, template_json: String, template_functions: String) -> Self {
Self {
tag_name,
template: String::new(),
template_json,
template_functions,
hydration: StateSurface::None,
navigation: StateSurface::None,
template_roots: Vec::new(),
is_scripted: false,
}
}
#[must_use]
pub fn with_hydration(mut self, hydration: StateSurface) -> Self {
self.hydration = hydration;
self
}
#[must_use]
pub fn with_navigation(mut self, navigation: StateSurface) -> Self {
self.navigation = navigation;
self
}
#[must_use]
pub fn with_template_roots(mut self, roots: Vec<String>) -> Self {
self.template_roots = roots;
self
}
#[must_use]
pub fn scripted(mut self, is_scripted: bool) -> Self {
self.is_scripted = is_scripted;
self
}
}
pub trait ParserPlugin {
fn start_fragment(&mut self, _fragment_id: &str) {}
fn configure(&mut self, _options: &ParserOptions) {}
fn register_component_template(
&mut self,
tag_name: &str,
component: &Component,
processed_template: &str,
) -> Result<()>;
fn classify_attribute(&mut self, attr_name: &str) -> AttributeAction;
fn finish_element(&mut self, binding_attribute_count: u32) -> Option<Vec<u8>>;
fn into_artifacts(self: Box<Self>) -> Result<ParserPluginArtifacts> {
Ok(ParserPluginArtifacts::None)
}
}
#[cfg(test)]
mod artifact_tests {
use super::{ComponentTemplateArtifact, StateSurface};
#[test]
fn template_constructor_starts_with_no_hydration_surface() {
let artifact = ComponentTemplateArtifact::template("x-a".into(), "<p></p>".into());
assert_eq!(artifact.tag_name, "x-a");
assert_eq!(artifact.template, "<p></p>");
assert!(artifact.template_json.is_empty());
assert!(artifact.template_functions.is_empty());
assert_eq!(artifact.hydration, StateSurface::None);
assert_eq!(artifact.navigation, StateSurface::None);
}
#[test]
fn webui_constructor_starts_with_no_hydration_surface() {
let artifact =
ComponentTemplateArtifact::webui("x-b".into(), "{\"j\":1}".into(), "[]".into());
assert_eq!(artifact.tag_name, "x-b");
assert!(artifact.template.is_empty());
assert_eq!(artifact.template_json, "{\"j\":1}");
assert_eq!(artifact.template_functions, "[]");
assert_eq!(artifact.hydration, StateSurface::None);
assert_eq!(artifact.navigation, StateSurface::None);
}
#[test]
fn with_hydration_attaches_surface_fluently() {
let artifact = ComponentTemplateArtifact::webui("x-c".into(), "{}".into(), "[]".into())
.with_hydration(StateSurface::keys(vec!["count".into(), "name".into()]));
assert_eq!(
artifact.hydration,
StateSurface::Keys(vec!["count".into(), "name".into()])
);
}
#[test]
fn with_hydration_is_plugin_agnostic_over_template_payloads() {
let artifact = ComponentTemplateArtifact::template("x-d".into(), "<f-t></f-t>".into())
.with_hydration(StateSurface::keys(vec!["value".into()]));
assert_eq!(artifact.template, "<f-t></f-t>");
assert_eq!(artifact.hydration, StateSurface::Keys(vec!["value".into()]));
}
#[test]
fn with_navigation_attaches_partial_state_surface() {
let artifact = ComponentTemplateArtifact::webui("x-e".into(), "{}".into(), "[]".into())
.with_navigation(StateSurface::keys(vec!["items".into(), "title".into()]));
assert_eq!(
artifact.navigation,
StateSurface::Keys(vec!["items".into(), "title".into()])
);
assert_eq!(artifact.hydration, StateSurface::None);
}
}