use vize_l0::cstr;
use vize_l0::dump::Error as DumpError;
use crate::dump::owned::{
Attribute, Binding, Branch, Component, Element, For, If, Model, Op, OriginalFor, Slot,
};
use crate::dump::parse::Parser;
use crate::dump::parse::line::err;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum Phase {
Attrs,
Bindings,
Children,
}
#[derive(Debug)]
pub(super) enum Frame {
Element(Element, Phase),
Component(Component, Phase),
Model(Model),
If(If),
Branch(Branch),
For(For),
OriginalFor(OriginalFor),
Slot(Slot, Phase),
}
impl Parser {
pub(super) fn push_attr(
&mut self,
attribute: Attribute,
line_no: usize,
) -> Result<(), DumpError> {
match self.stack.last_mut() {
Some(Frame::Element(element, phase)) => match phase {
Phase::Attrs => {
element.attributes.push(attribute);
Ok(())
}
Phase::Bindings => Err(err(line_no, cstr!("attribute after a binding"))),
Phase::Children => Err(err(line_no, cstr!("attribute after a child"))),
},
Some(Frame::Component(component, phase)) => match phase {
Phase::Attrs => {
component.attributes.push(attribute);
Ok(())
}
Phase::Bindings => Err(err(line_no, cstr!("attribute after a binding"))),
Phase::Children => Err(err(line_no, cstr!("attribute after a child"))),
},
Some(Frame::Slot(slot, phase)) => match phase {
Phase::Attrs => {
slot.attributes.push(attribute);
Ok(())
}
Phase::Bindings => Err(err(line_no, cstr!("attribute after a binding"))),
Phase::Children => Err(err(line_no, cstr!("attribute after a child"))),
},
Some(Frame::Model(model)) => {
model.attributes.push(attribute);
Ok(())
}
Some(Frame::If(_)) => Err(err(line_no, cstr!("expected `branch` under `ui.if`"))),
None | Some(Frame::Branch(_) | Frame::For(_) | Frame::OriginalFor(_)) => {
Err(err(line_no, cstr!("`attr` outside an element")))
}
}
}
pub(super) fn push_leaf_binding(
&mut self,
binding: Binding,
line_no: usize,
) -> Result<(), DumpError> {
match self.stack.last_mut() {
Some(Frame::Element(element, _)) => element.bindings.push(binding),
Some(Frame::Component(component, _)) => component.bindings.push(binding),
Some(Frame::Slot(slot, _)) => slot.bindings.push(binding),
_ => return Err(err(line_no, cstr!("binding without an element-like owner"))),
}
Ok(())
}
pub(super) fn guard_binding(&mut self, line_no: usize) -> Result<(), DumpError> {
match self.stack.last_mut() {
Some(Frame::Element(_, phase) | Frame::Component(_, phase) | Frame::Slot(_, phase)) => {
if *phase == Phase::Children {
return Err(err(line_no, cstr!("binding after a child")));
}
*phase = Phase::Bindings;
Ok(())
}
Some(Frame::Model(_)) => Err(err(line_no, cstr!("expected `attr` under `ui.model`"))),
Some(Frame::If(_)) => Err(err(line_no, cstr!("expected `branch` under `ui.if`"))),
None | Some(Frame::Branch(_) | Frame::For(_) | Frame::OriginalFor(_)) => {
Err(err(line_no, cstr!("binding outside an element")))
}
}
}
pub(super) fn guard_child(&self, line_no: usize) -> Result<(), DumpError> {
match self.stack.last() {
Some(Frame::If(_)) => Err(err(line_no, cstr!("expected `branch` under `ui.if`"))),
Some(Frame::Model(_)) => Err(err(line_no, cstr!("expected `attr` under `ui.model`"))),
None
| Some(
Frame::Element(..)
| Frame::Component(..)
| Frame::Branch(_)
| Frame::For(_)
| Frame::OriginalFor(_)
| Frame::Slot(..),
) => Ok(()),
}
}
pub(super) fn attach_op(&mut self, op: Op, line_no: usize) -> Result<(), DumpError> {
match self.stack.last_mut() {
None => self.root.push(op),
Some(Frame::Element(element, phase)) => {
*phase = Phase::Children;
element.children.push(op);
}
Some(Frame::Component(component, phase)) => {
*phase = Phase::Children;
component.children.push(op);
}
Some(Frame::Branch(branch)) => branch.ops.push(op),
Some(Frame::For(for_op)) => for_op.ops.push(op),
Some(Frame::OriginalFor(for_op)) => for_op.ops.push(op),
Some(Frame::Slot(slot, phase)) => {
*phase = Phase::Children;
slot.fallback.push(op);
}
Some(Frame::If(_) | Frame::Model(_)) => {
return Err(err(line_no, cstr!("child under a `ui.if` or model frame")));
}
}
Ok(())
}
pub(super) fn close_top(&mut self, line_no: usize) -> Result<(), DumpError> {
let Some(frame) = self.stack.pop() else {
return Ok(());
};
match frame {
Frame::Element(element, _) => self.attach_op(Op::Element(element), line_no)?,
Frame::Component(component, _) => {
self.attach_op(Op::Component(component), line_no)?;
}
Frame::If(if_op) => self.attach_op(Op::If(if_op), line_no)?,
Frame::For(for_op) => self.attach_op(Op::For(for_op), line_no)?,
Frame::OriginalFor(for_op) => self.attach_op(Op::OriginalFor(for_op), line_no)?,
Frame::Slot(slot, _) => self.attach_op(Op::Slot(slot), line_no)?,
Frame::Branch(branch) => match self.stack.last_mut() {
Some(Frame::If(if_op)) => if_op.branches.push(branch),
_ => return Err(err(line_no, cstr!("`branch` outside `ui.if`"))),
},
Frame::Model(model) => match self.stack.last_mut() {
Some(Frame::Element(element, _)) => {
element.bindings.push(Binding::Model(model));
}
Some(Frame::Component(component, _)) => {
component.bindings.push(Binding::Model(model));
}
Some(Frame::Slot(slot, _)) => {
slot.bindings.push(Binding::Model(model));
}
_ => return Err(err(line_no, cstr!("model outside an element"))),
},
}
Ok(())
}
}