#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::{ActionEffect, ActionType, ElementTarget, Target};
use hyperchad_transformer_models::Visibility;
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Statement {
Expression(Expression),
Let { name: String, value: Expression },
If {
condition: Expression,
then_block: Block,
else_block: Option<Block>,
},
Match {
expr: Expression,
arms: Vec<MatchArm>,
},
For {
pattern: String,
iter: Expression,
body: Block,
},
While { condition: Expression, body: Block },
Block(Block),
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Block {
pub statements: Vec<Statement>,
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct MatchArm {
pub pattern: Pattern,
pub body: Expression,
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Pattern {
Literal(Literal),
Variable(String),
Wildcard,
Variant {
enum_name: String,
variant: String,
fields: Vec<Pattern>,
},
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ElementVariable {
pub name: String,
}
impl ElementVariable {
#[must_use]
pub fn show(self) -> ActionType {
ActionType::show_str_id(Target::reference(self.name))
}
#[must_use]
pub fn hide(self) -> ActionType {
ActionType::hide_str_id(Target::reference(self.name))
}
#[must_use]
pub fn focus(self) -> ActionType {
ActionType::focus_str_id(Target::reference(self.name))
}
#[must_use]
pub fn select(self) -> ActionType {
ActionType::select_str_id(Target::reference(self.name))
}
#[cfg(feature = "logic")]
#[must_use]
pub fn toggle_visibility(self) -> ActionType {
ActionType::toggle_visibility_str_id(Target::reference(self.name))
}
#[cfg(feature = "logic")]
#[must_use]
pub fn visibility(self) -> crate::logic::CalcValue {
crate::logic::get_visibility_str_id(Target::reference(self.name))
}
#[cfg(feature = "logic")]
#[must_use]
pub fn get_width_px(self) -> crate::logic::CalcValue {
crate::logic::get_width_px_str_id(Target::reference(self.name))
}
#[cfg(feature = "logic")]
#[must_use]
pub fn get_height_px(self) -> crate::logic::CalcValue {
crate::logic::get_height_px_str_id(Target::reference(self.name))
}
#[cfg(feature = "logic")]
#[must_use]
pub fn get_mouse_x(self) -> crate::logic::CalcValue {
crate::logic::get_mouse_x_str_id(Target::reference(self.name))
}
#[cfg(feature = "logic")]
#[must_use]
pub fn get_mouse_y(self) -> crate::logic::CalcValue {
crate::logic::get_mouse_y_str_id(Target::reference(self.name))
}
#[must_use]
pub fn set_visibility(self, visibility: Visibility) -> ActionType {
ActionType::set_visibility_str_id(visibility, Target::reference(self.name))
}
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Expression {
Literal(Literal),
Variable(String),
ElementRef(Box<Expression>),
Call {
function: String,
args: Vec<Expression>,
},
MethodCall {
receiver: Box<Expression>,
method: String,
args: Vec<Expression>,
},
Field {
object: Box<Expression>,
field: String,
},
Binary {
left: Box<Expression>,
op: BinaryOp,
right: Box<Expression>,
},
Unary { op: UnaryOp, expr: Box<Expression> },
If {
condition: Box<Expression>,
then_branch: Box<Expression>,
else_branch: Option<Box<Expression>>,
},
Match {
expr: Box<Expression>,
arms: Vec<MatchArm>,
},
Block(Block),
Array(Vec<Expression>),
Tuple(Vec<Expression>),
Range {
start: Option<Box<Expression>>,
end: Option<Box<Expression>>,
inclusive: bool,
},
Closure {
params: Vec<String>,
body: Box<Expression>,
},
Grouping(Box<Expression>),
RawRust(String),
}
impl std::fmt::Display for Expression {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Literal(literal) => std::fmt::Display::fmt(literal, f),
Self::Variable(variable) => std::fmt::Display::fmt(variable, f),
Self::ElementRef(..) => unimplemented!("element_ref"),
Self::Call { .. } => unimplemented!("call"),
Self::MethodCall { .. } => unimplemented!("method_call"),
Self::Field { .. } => unimplemented!("field"),
Self::Binary { .. } => unimplemented!("binary"),
Self::Unary { .. } => unimplemented!("unary"),
Self::If { .. } => unimplemented!("if"),
Self::Match { .. } => unimplemented!("match"),
Self::Block(..) => unimplemented!("block"),
Self::Array(..) => unimplemented!("array"),
Self::Tuple(..) => unimplemented!("tuple"),
Self::Range { .. } => unimplemented!("range"),
Self::Closure { .. } => unimplemented!("closure"),
Self::RawRust(_) => unimplemented!("raw_rust"),
Self::Grouping(_) => unimplemented!("grouping"),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum BinaryOp {
Add,
Subtract,
Multiply,
Divide,
Modulo,
Equal,
NotEqual,
Less,
LessEqual,
Greater,
GreaterEqual,
And,
Or,
BitAnd,
BitOr,
BitXor,
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum UnaryOp {
Not,
Minus,
Plus,
Ref,
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Literal {
String(String),
Integer(i64),
Float(f64),
Bool(bool),
Unit,
}
impl Literal {
#[must_use]
pub fn string(x: impl Into<String>) -> Self {
Self::String(x.into())
}
#[must_use]
pub fn integer(x: impl Into<i64>) -> Self {
Self::Integer(x.into())
}
#[must_use]
pub fn float(x: impl Into<f64>) -> Self {
Self::Float(x.into())
}
#[must_use]
pub fn bool(x: impl Into<bool>) -> Self {
Self::Bool(x.into())
}
}
impl std::fmt::Display for Literal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::String(x) => f.write_str(x),
Self::Integer(x) => write!(f, "{x}"),
Self::Float(x) => write!(f, "{x}"),
Self::Bool(x) => write!(f, "{x}"),
Self::Unit => write!(f, ""),
}
}
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Dsl {
pub statements: Vec<Statement>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ElementReference {
pub selector: String,
}
impl ElementReference {
#[must_use]
pub fn parse_selector(&self) -> ParsedSelector {
if self.selector.starts_with('#') {
ParsedSelector::Id(self.selector[1..].to_string())
} else if self.selector.starts_with('.') {
ParsedSelector::Class(self.selector[1..].to_string())
} else if self.selector.is_empty() {
ParsedSelector::Invalid
} else {
ParsedSelector::Id(self.selector.clone())
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum ParsedSelector {
Id(String),
Class(String),
Complex(String),
Invalid,
}
impl Dsl {
#[must_use]
pub const fn new(statements: Vec<Statement>) -> Self {
Self { statements }
}
#[must_use]
pub const fn evaluate(&self) -> Vec<ActionEffect> {
Vec::new()
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum BuiltinFunction {
Hide,
Show,
Toggle,
SetVisibility,
SetDisplay,
SetBackground,
Element,
GetVisibility,
GetDisplay,
GetWidth,
GetHeight,
GetPositionX,
GetPositionY,
GetMouseX,
GetMouseY,
GetDataAttr,
GetEventValue,
NoOp,
Log,
Navigate,
Custom,
If,
Match,
Add,
Subtract,
Multiply,
Divide,
Min,
Max,
Clamp,
Eq,
And,
Or,
Not,
}
#[derive(Clone, Debug, Default)]
pub struct EvalContext {
pub variables: std::collections::HashMap<String, DslValue>,
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum DslValue {
String(String),
Number(f64),
Bool(bool),
Visibility(Visibility),
Target(ElementTarget),
ElementRef(ElementReference),
Action(ActionEffect),
List(Vec<DslValue>),
Unit,
}
impl From<Literal> for DslValue {
fn from(lit: Literal) -> Self {
#[allow(clippy::cast_precision_loss)]
match lit {
Literal::String(s) => Self::String(s),
Literal::Integer(i) => Self::Number(i as f64),
Literal::Float(f) => Self::Number(f),
Literal::Bool(b) => Self::Bool(b),
Literal::Unit => Self::Unit,
}
}
}
impl From<DslValue> for ActionEffect {
fn from(value: DslValue) -> Self {
match value {
DslValue::Action(action) => action,
DslValue::ElementRef(element_ref) => {
Self {
action: ActionType::Custom {
action: format!("element_ref:{}", element_ref.selector),
},
delay_off: None,
throttle: None,
unique: None,
}
}
_ => Self {
action: ActionType::NoOp,
delay_off: None,
throttle: None,
unique: None,
},
}
}
}