use alloc::boxed::Box;
use oxc_parser::AdmittedExpression;
use vize_l0::{SourceBlock, Span};
use super::{NativeAttribute, NativeTemplateComponent, NativeTemplateGrammar};
pub use crate::dialect::vue3::operand::NativeConditionKind;
use crate::dialect::vue3::operand::conditional_head;
use crate::embed::syntax::{NativeSyntax, RetainedExpression, parse_once};
use crate::embed::{Embed, Grammar, Lang, Shape, SourceError, prepare_attribute_value};
use crate::markup::DirectiveNameError;
use crate::markup::{Attribute, Element};
mod event;
pub use event::{
NativeAttributeHandler, NativeAttributeHandlerFailure, NativeAttributeHandlerView,
};
mod for_head;
pub use for_head::{
NativeAttributeForHead, NativeAttributeForHeadFailure, NativeAttributeForHeadView,
};
mod head;
pub use head::NativeAttributeHead;
mod binding;
pub use binding::{
NativeAttributeBindingExpression, NativeAttributeBindingExpressionView, NativeStaticBindingHead,
};
mod origin;
use origin::Origin;
mod value;
pub use value::{NativeAttributeValue, NativeAttributeValueFailure, NativeAttributeValueView};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NativeAttributeOperandError {
ForeignComponent,
RecoveredComponent,
Verbatim,
UnsupportedDirective,
Directive(DirectiveNameError),
IncompleteValue,
Source(SourceError),
UnexpectedShape,
}
#[derive(Debug)]
pub struct NativeAttributeExpressionFailure<'a> {
kind: NativeAttributeOperandError,
syntax: Option<Box<NativeSyntax<'a>>>,
}
impl NativeAttributeExpressionFailure<'_> {
#[must_use]
pub const fn kind(&self) -> NativeAttributeOperandError {
self.kind
}
#[must_use]
pub fn syntax(&self) -> Option<&NativeSyntax<'_>> {
self.syntax.as_deref()
}
}
impl<'a> From<NativeAttributeOperandError> for NativeAttributeExpressionFailure<'a> {
fn from(kind: NativeAttributeOperandError) -> Self {
Self { kind, syntax: None }
}
}
pub struct NativeAttributeExpression<'a> {
origin: Origin<'a>,
kind: NativeConditionKind,
syntax: RetainedExpression<'a>,
}
impl<'a> NativeTemplateComponent<'a> {
pub fn observe_attribute_expression(
&self,
attribute: NativeAttribute<'_, 'a>,
) -> Result<NativeAttributeExpression<'a>, NativeAttributeExpressionFailure<'a>> {
Origin::check_original_header(self, &attribute)?;
let block = self.component().block();
let name_offset = block.offset_of(attribute.surface().name.text).ok_or(
NativeAttributeOperandError::Source(SourceError::InvalidAuthoredSpan),
)?;
let kind = conditional_head(
attribute.surface().name.text,
name_offset,
block.root_source(),
)
.map_err(NativeAttributeOperandError::Directive)?
.ok_or(NativeAttributeOperandError::UnsupportedDirective)?;
observe_conditional(self, attribute, kind)
}
}
fn observe_conditional<'a>(
selected: &NativeTemplateComponent<'a>,
attribute: NativeAttribute<'_, 'a>,
kind: NativeConditionKind,
) -> Result<NativeAttributeExpression<'a>, NativeAttributeExpressionFailure<'a>> {
let (origin, syntax) = observe_expression_value(selected, attribute)?;
Ok(NativeAttributeExpression {
origin,
kind,
syntax,
})
}
fn observe_expression_value<'a>(
selected: &NativeTemplateComponent<'a>,
attribute: NativeAttribute<'_, 'a>,
) -> Result<(Origin<'a>, RetainedExpression<'a>), NativeAttributeExpressionFailure<'a>> {
let origin = Origin::from_attribute(selected, &attribute)?;
let source = prepare_attribute_value(
selected.component().allocator(),
origin.block.root_source(),
origin.value_span,
)
.map_err(NativeAttributeOperandError::Source)?;
let lang = match selected.grammar() {
NativeTemplateGrammar::JavaScriptModule => Lang::Js,
NativeTemplateGrammar::TypeScriptModule => Lang::Ts,
};
let syntax = parse_once(
selected.component().allocator(),
Embed {
grammar: Grammar {
shape: Shape::Expr,
lang,
},
source,
},
)
.into_expression()
.map_err(|syntax| NativeAttributeExpressionFailure {
kind: NativeAttributeOperandError::UnexpectedShape,
syntax: Some(syntax),
})?;
Ok((origin, syntax))
}
impl<'a> NativeAttributeExpression<'a> {
#[must_use]
pub const fn kind(&self) -> NativeConditionKind {
self.kind
}
#[must_use]
pub fn syntax(&self) -> &RetainedExpression<'a> {
&self.syntax
}
#[must_use]
pub const fn raw_value(&self) -> &'a str {
self.origin.raw_value
}
#[must_use]
pub const fn value_span(&self) -> Span {
self.origin.value_span
}
#[must_use]
pub const fn name_span(&self) -> Span {
self.origin.name_span
}
pub fn admitted_for<'s>(
&'s self,
selected: &'s NativeTemplateComponent<'a>,
attribute: NativeAttribute<'s, 'a>,
) -> Option<NativeAttributeExpressionView<'s, 'a>> {
if !self.origin.matches(selected, &attribute) {
return None;
}
self.syntax.admitted_expression()?;
Some(NativeAttributeExpressionView {
owner: self,
selected,
attribute,
})
}
#[must_use]
pub fn into_syntax(self) -> RetainedExpression<'a> {
self.syntax
}
}
pub struct NativeAttributeExpressionView<'s, 'a> {
owner: &'s NativeAttributeExpression<'a>,
selected: &'s NativeTemplateComponent<'a>,
attribute: NativeAttribute<'s, 'a>,
}
impl<'s, 'a> NativeAttributeExpressionView<'s, 'a> {
#[must_use]
pub fn selected(&self) -> &'s NativeTemplateComponent<'a> {
self.selected
}
#[must_use]
pub fn attribute(&self) -> &NativeAttribute<'s, 'a> {
&self.attribute
}
#[must_use]
pub fn operand(&self) -> &'s NativeAttributeExpression<'a> {
self.owner
}
#[must_use]
pub fn expression(&self) -> Option<AdmittedExpression<'s, 'a>> {
self.owner.syntax.admitted_expression()
}
}
#[cfg(test)]
mod tests;