use alloc::vec::Vec;
use vize_l0::{Span, String, ensure_sufficient_stack};
use super::L2Folio;
use crate::op::{Attribute, DynamicName, Namespace, Op, Region};
mod binding;
mod expr;
pub use binding::{
FolioBind, FolioBinding, FolioModel, FolioOn, FolioSlotContent, FolioVueCloak, FolioVueCssBind,
FolioVueDirective, FolioVueHtml, FolioVueMemo, FolioVueOnce, FolioVueShow, FolioVueSlotScope,
FolioVueSync, FolioVueText,
};
pub use expr::{FolioContract, FolioExpr, FolioForBinding};
use binding::own_binding;
use expr::own_expr;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum FolioOp {
Element(FolioElement),
Component(FolioComponent),
Text(FolioText),
Interpolation(FolioInterpolation),
Comment(FolioComment),
If(FolioIf),
For(FolioFor),
Slot(FolioSlot),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum FolioName {
Static(String),
Dynamic(FolioExpr),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FolioAttribute {
pub name: String,
pub value: Option<String>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FolioElement {
pub tag: String,
pub namespace: Namespace,
pub attributes: Vec<FolioAttribute>,
pub bindings: Vec<FolioBinding>,
pub children: Vec<FolioOp>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FolioComponent {
pub name: String,
pub attributes: Vec<FolioAttribute>,
pub bindings: Vec<FolioBinding>,
pub children: Vec<FolioOp>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FolioText {
pub content: String,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FolioInterpolation {
pub expression: FolioExpr,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FolioComment {
pub content: String,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FolioIf {
pub branches: Vec<FolioBranch>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FolioBranch {
pub condition: Option<FolioExpr>,
pub ops: Vec<FolioOp>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FolioFor {
pub binding: FolioForBinding,
pub ops: Vec<FolioOp>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FolioSlot {
pub name: FolioName,
pub attributes: Vec<FolioAttribute>,
pub bindings: Vec<FolioBinding>,
pub fallback: Vec<FolioOp>,
pub span: Span,
}
impl L2Folio {
#[must_use]
pub fn of(ops: &[Op<'_>]) -> Self {
ensure_sufficient_stack(|| Self { ops: own_ops(ops) })
}
#[must_use]
pub fn op_count(&self) -> u64 {
ensure_sufficient_stack(|| self.ops.iter().map(count_op).sum())
}
}
fn own_ops(ops: &[Op<'_>]) -> Vec<FolioOp> {
ops.iter()
.map(|op| ensure_sufficient_stack(|| own_op(op)))
.collect()
}
fn own_region(region: &Region<'_>) -> Vec<FolioOp> {
ensure_sufficient_stack(|| own_ops(®ion.ops))
}
fn own_op(op: &Op<'_>) -> FolioOp {
ensure_sufficient_stack(|| own_op_guarded(op))
}
fn own_op_guarded(op: &Op<'_>) -> FolioOp {
match op {
Op::Element(element) => FolioOp::Element(FolioElement {
tag: String::from(element.tag),
namespace: element.namespace,
attributes: element.attributes.iter().map(own_attribute).collect(),
bindings: element.bindings.iter().map(own_binding).collect(),
children: own_region(&element.children),
span: element.span,
}),
Op::Component(component) => FolioOp::Component(FolioComponent {
name: String::from(component.name),
attributes: component.attributes.iter().map(own_attribute).collect(),
bindings: component.bindings.iter().map(own_binding).collect(),
children: own_region(&component.children),
span: component.span,
}),
Op::Text(text) => FolioOp::Text(FolioText {
content: String::from(text.content),
span: text.span,
}),
Op::Interpolation(interpolation) => FolioOp::Interpolation(FolioInterpolation {
expression: own_expr(&interpolation.expression),
span: interpolation.span,
}),
Op::Comment(comment) => FolioOp::Comment(FolioComment {
content: String::from(comment.content),
span: comment.span,
}),
Op::If(if_op) => FolioOp::If(FolioIf {
branches: if_op
.branches
.iter()
.map(|branch| FolioBranch {
condition: branch.condition.as_ref().map(own_expr),
ops: own_region(&branch.region),
span: branch.span,
})
.collect(),
span: if_op.span,
}),
Op::For(for_op) => FolioOp::For(FolioFor {
binding: FolioForBinding {
source: own_expr(&for_op.binding.source),
value: own_expr(&for_op.binding.value),
key: for_op.binding.key.as_ref().map(own_expr),
index: for_op.binding.index.as_ref().map(own_expr),
},
ops: own_region(&for_op.region),
span: for_op.span,
}),
Op::Slot(slot) => FolioOp::Slot(FolioSlot {
name: own_name(&slot.name),
attributes: slot.attributes.iter().map(own_attribute).collect(),
bindings: slot.bindings.iter().map(own_binding).collect(),
fallback: own_region(&slot.fallback),
span: slot.span,
}),
}
}
pub(super) fn own_name(name: &DynamicName<'_>) -> FolioName {
match name {
DynamicName::Static(text) => FolioName::Static(String::from(*text)),
DynamicName::Dynamic(expr) => FolioName::Dynamic(own_expr(expr)),
}
}
pub(super) fn own_attribute(attribute: &Attribute<'_>) -> FolioAttribute {
FolioAttribute {
name: String::from(attribute.name),
value: attribute.value.map(String::from),
span: attribute.span,
}
}
fn count_op(op: &FolioOp) -> u64 {
ensure_sufficient_stack(|| count_op_guarded(op))
}
fn count_op_guarded(op: &FolioOp) -> u64 {
match op {
FolioOp::Element(element) => {
1 + element.bindings.len() as u64 + element.children.iter().map(count_op).sum::<u64>()
}
FolioOp::Component(component) => {
1 + component.bindings.len() as u64
+ component.children.iter().map(count_op).sum::<u64>()
}
FolioOp::Text(_) | FolioOp::Interpolation(_) | FolioOp::Comment(_) => 1,
FolioOp::If(if_op) => {
1 + if_op
.branches
.iter()
.flat_map(|branch| branch.ops.iter())
.map(count_op)
.sum::<u64>()
}
FolioOp::For(for_op) => 1 + for_op.ops.iter().map(count_op).sum::<u64>(),
FolioOp::Slot(slot) => {
1 + slot.bindings.len() as u64 + slot.fallback.iter().map(count_op).sum::<u64>()
}
}
}