#[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<Self>,
},
}
#[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_by_id(Target::reference(self.name))
}
#[must_use]
pub fn hide(self) -> ActionType {
ActionType::hide_by_id(Target::reference(self.name))
}
#[must_use]
pub fn focus(self) -> ActionType {
ActionType::focus_by_id(Target::reference(self.name))
}
#[must_use]
pub fn select(self) -> ActionType {
ActionType::select_by_id(Target::reference(self.name))
}
#[cfg(feature = "logic")]
#[must_use]
pub fn toggle_visibility(self) -> ActionType {
ActionType::toggle_visibility_by_id(Target::reference(self.name))
}
#[cfg(feature = "logic")]
#[must_use]
pub fn visibility(self) -> crate::logic::CalcValue {
crate::logic::get_visibility_by_id(Target::reference(self.name))
}
#[must_use]
pub fn display(self) -> ActionType {
ActionType::display_by_id(Target::reference(self.name))
}
#[must_use]
pub fn no_display(self) -> ActionType {
ActionType::no_display_by_id(Target::reference(self.name))
}
#[must_use]
pub fn set_display(self, display: bool) -> ActionType {
ActionType::set_display_by_id(display, Target::reference(self.name))
}
#[cfg(feature = "logic")]
#[must_use]
pub fn toggle_display(self) -> ActionType {
ActionType::toggle_display_by_id(Target::reference(self.name))
}
#[cfg(feature = "logic")]
#[must_use]
pub fn get_display(self) -> crate::logic::CalcValue {
crate::logic::get_display_by_id(Target::reference(self.name))
}
#[cfg(feature = "logic")]
#[must_use]
pub fn get_width_px(self) -> crate::logic::CalcValue {
crate::logic::get_width_px_by_id(Target::reference(self.name))
}
#[cfg(feature = "logic")]
#[must_use]
pub fn get_height_px(self) -> crate::logic::CalcValue {
crate::logic::get_height_px_by_id(Target::reference(self.name))
}
#[cfg(feature = "logic")]
#[must_use]
pub fn get_mouse_x(self) -> crate::logic::CalcValue {
crate::logic::get_mouse_x_by_id(Target::reference(self.name))
}
#[cfg(feature = "logic")]
#[must_use]
pub fn get_mouse_y(self) -> crate::logic::CalcValue {
crate::logic::get_mouse_y_by_id(Target::reference(self.name))
}
#[must_use]
pub fn set_visibility(self, visibility: Visibility) -> ActionType {
ActionType::set_visibility_by_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<Self>),
ElementByIdRef(Box<Self>),
Call {
function: String,
args: Vec<Self>,
},
MethodCall {
receiver: Box<Self>,
method: String,
args: Vec<Self>,
},
Field {
object: Box<Self>,
field: String,
},
Binary {
left: Box<Self>,
op: BinaryOp,
right: Box<Self>,
},
Unary {
op: UnaryOp,
expr: Box<Self>,
},
If {
condition: Box<Self>,
then_branch: Box<Self>,
else_branch: Option<Box<Self>>,
},
Match {
expr: Box<Self>,
arms: Vec<MatchArm>,
},
Block(Block),
Array(Vec<Self>),
Tuple(Vec<Self>),
Range {
start: Option<Box<Self>>,
end: Option<Box<Self>>,
inclusive: bool,
},
Closure {
params: Vec<String>,
body: Box<Self>,
},
Grouping(Box<Self>),
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::ElementByIdRef(..) => unimplemented!("element_by_id_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::BTreeMap<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<Self>),
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,
},
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test_log::test]
fn test_parse_selector_id_with_hash() {
let element_ref = ElementReference {
selector: "#my-element".to_string(),
};
let parsed = element_ref.parse_selector();
assert_eq!(parsed, ParsedSelector::Id("my-element".to_string()));
}
#[test_log::test]
fn test_parse_selector_class_with_dot() {
let element_ref = ElementReference {
selector: ".my-class".to_string(),
};
let parsed = element_ref.parse_selector();
assert_eq!(parsed, ParsedSelector::Class("my-class".to_string()));
}
#[test_log::test]
fn test_parse_selector_bare_string_as_id() {
let element_ref = ElementReference {
selector: "my-element-id".to_string(),
};
let parsed = element_ref.parse_selector();
assert_eq!(parsed, ParsedSelector::Id("my-element-id".to_string()));
}
#[test_log::test]
fn test_parse_selector_empty_string() {
let element_ref = ElementReference {
selector: String::new(),
};
let parsed = element_ref.parse_selector();
assert_eq!(parsed, ParsedSelector::Invalid);
}
#[test_log::test]
fn test_parse_selector_hash_only() {
let element_ref = ElementReference {
selector: "#".to_string(),
};
let parsed = element_ref.parse_selector();
assert_eq!(parsed, ParsedSelector::Id(String::new()));
}
#[test_log::test]
fn test_parse_selector_dot_only() {
let element_ref = ElementReference {
selector: ".".to_string(),
};
let parsed = element_ref.parse_selector();
assert_eq!(parsed, ParsedSelector::Class(String::new()));
}
#[test_log::test]
fn test_parse_selector_special_characters() {
let element_ref = ElementReference {
selector: "#element-with-dash_and_underscore".to_string(),
};
let parsed = element_ref.parse_selector();
assert_eq!(
parsed,
ParsedSelector::Id("element-with-dash_and_underscore".to_string())
);
}
#[test_log::test]
fn test_action_effect_from_dsl_value_action() {
let action_effect = ActionEffect {
action: ActionType::NoOp,
delay_off: Some(100),
throttle: Some(200),
unique: Some(true),
};
let dsl_value = DslValue::Action(action_effect);
let result: ActionEffect = dsl_value.into();
assert_eq!(result.action, ActionType::NoOp);
assert_eq!(result.delay_off, Some(100));
assert_eq!(result.throttle, Some(200));
assert_eq!(result.unique, Some(true));
}
#[test_log::test]
fn test_action_effect_from_dsl_value_element_ref() {
let element_ref = ElementReference {
selector: "#my-element".to_string(),
};
let dsl_value = DslValue::ElementRef(element_ref);
let result: ActionEffect = dsl_value.into();
match result.action {
ActionType::Custom { action } => {
assert_eq!(action, "element_ref:#my-element");
}
_ => panic!("Expected Custom action type"),
}
assert_eq!(result.delay_off, None);
assert_eq!(result.throttle, None);
assert_eq!(result.unique, None);
}
#[test_log::test]
fn test_action_effect_from_dsl_value_non_action() {
let dsl_value = DslValue::String("test".to_string());
let result: ActionEffect = dsl_value.into();
assert_eq!(result.action, ActionType::NoOp);
assert_eq!(result.delay_off, None);
assert_eq!(result.throttle, None);
assert_eq!(result.unique, None);
}
#[test_log::test]
fn test_element_variable_creates_target_ref_not_literal() {
let element_var = ElementVariable {
name: "my-element".to_string(),
};
let style_action = element_var.clone().show();
match style_action {
ActionType::Style { target, .. } => {
match target {
ElementTarget::ById(target_ref) => {
assert!(
matches!(&target_ref, Target::Ref(name) if name == "my-element"),
"Expected Target::Ref with variable name, got {target_ref:?}",
);
}
_ => panic!("Expected ElementTarget::ById"),
}
}
_ => panic!("Expected Style action"),
}
let input_action = element_var.select();
match input_action {
ActionType::Input(crate::InputActionType::Select { target }) => match target {
ElementTarget::ById(target_ref) => {
assert!(
matches!(&target_ref, Target::Ref(name) if name == "my-element"),
"Expected Target::Ref with variable name, got {target_ref:?}",
);
}
_ => panic!("Expected ElementTarget::ById"),
},
_ => panic!("Expected Input Select action"),
}
}
#[test_log::test]
fn test_element_variable_hide_uses_target_ref() {
let element_var = ElementVariable {
name: "target-element".to_string(),
};
let action = element_var.hide();
match action {
ActionType::Style { target, action } => {
match target {
ElementTarget::ById(target_ref) => {
assert!(
matches!(&target_ref, Target::Ref(name) if name == "target-element"),
"Expected Target::Ref, got {target_ref:?}",
);
}
_ => panic!("Expected ElementTarget::ById"),
}
assert_eq!(
action,
crate::StyleAction::SetVisibility(Visibility::Hidden)
);
}
_ => panic!("Expected Style action"),
}
}
#[test_log::test]
fn test_element_variable_focus_uses_target_ref() {
let element_var = ElementVariable {
name: "focus-target".to_string(),
};
let action = element_var.focus();
match action {
ActionType::Style { target, action } => {
match target {
ElementTarget::ById(target_ref) => {
assert!(
matches!(&target_ref, Target::Ref(name) if name == "focus-target"),
"Expected Target::Ref, got {target_ref:?}",
);
}
_ => panic!("Expected ElementTarget::ById"),
}
assert_eq!(action, crate::StyleAction::SetFocus(true));
}
_ => panic!("Expected Style action"),
}
}
#[test_log::test]
fn test_element_variable_display_methods() {
let element_var = ElementVariable {
name: "display-element".to_string(),
};
let action = element_var.clone().display();
match action {
ActionType::Style { target, action } => {
match target {
ElementTarget::ById(target_ref) => {
assert!(
matches!(&target_ref, Target::Ref(name) if name == "display-element")
);
}
_ => panic!("Expected ElementTarget::ById"),
}
assert_eq!(action, crate::StyleAction::SetDisplay(true));
}
_ => panic!("Expected Style action"),
}
let action = element_var.clone().no_display();
match action {
ActionType::Style { action, .. } => {
assert_eq!(action, crate::StyleAction::SetDisplay(false));
}
_ => panic!("Expected Style action"),
}
let action = element_var.set_display(false);
match action {
ActionType::Style { action, .. } => {
assert_eq!(action, crate::StyleAction::SetDisplay(false));
}
_ => panic!("Expected Style action"),
}
}
#[test_log::test]
fn test_element_variable_set_visibility() {
let element_var = ElementVariable {
name: "visibility-element".to_string(),
};
let action = element_var.set_visibility(Visibility::Hidden);
match action {
ActionType::Style { target, action } => {
match target {
ElementTarget::ById(target_ref) => {
assert!(
matches!(&target_ref, Target::Ref(name) if name == "visibility-element")
);
}
_ => panic!("Expected ElementTarget::ById"),
}
assert_eq!(
action,
crate::StyleAction::SetVisibility(Visibility::Hidden)
);
}
_ => panic!("Expected Style action"),
}
}
#[test_log::test]
fn test_action_effect_from_dsl_value_number() {
let dsl_value = DslValue::Number(42.5);
let result: ActionEffect = dsl_value.into();
assert_eq!(result.action, ActionType::NoOp);
}
#[test_log::test]
fn test_action_effect_from_dsl_value_bool() {
let dsl_value = DslValue::Bool(true);
let result: ActionEffect = dsl_value.into();
assert_eq!(result.action, ActionType::NoOp);
}
#[test_log::test]
fn test_action_effect_from_dsl_value_visibility() {
let dsl_value = DslValue::Visibility(Visibility::Hidden);
let result: ActionEffect = dsl_value.into();
assert_eq!(result.action, ActionType::NoOp);
}
#[test_log::test]
fn test_action_effect_from_dsl_value_unit() {
let dsl_value = DslValue::Unit;
let result: ActionEffect = dsl_value.into();
assert_eq!(result.action, ActionType::NoOp);
}
#[test_log::test]
fn test_action_effect_from_dsl_value_list() {
let dsl_value = DslValue::List(vec![DslValue::Number(1.0), DslValue::Number(2.0)]);
let result: ActionEffect = dsl_value.into();
assert_eq!(result.action, ActionType::NoOp);
}
#[test_log::test]
fn test_dsl_value_from_literal_string() {
let literal = Literal::String("test".to_string());
let value: DslValue = literal.into();
assert_eq!(value, DslValue::String("test".to_string()));
}
#[test_log::test]
fn test_dsl_value_from_literal_integer() {
let literal = Literal::Integer(42);
let value: DslValue = literal.into();
assert_eq!(value, DslValue::Number(42.0));
}
#[test_log::test]
fn test_dsl_value_from_literal_float() {
let literal = Literal::Float(4.567);
let value: DslValue = literal.into();
assert_eq!(value, DslValue::Number(4.567));
}
#[test_log::test]
fn test_dsl_value_from_literal_bool() {
let literal = Literal::Bool(true);
let value: DslValue = literal.into();
assert_eq!(value, DslValue::Bool(true));
}
#[test_log::test]
fn test_dsl_value_from_literal_unit() {
let literal = Literal::Unit;
let value: DslValue = literal.into();
assert_eq!(value, DslValue::Unit);
}
#[test_log::test]
fn test_literal_display_string() {
let literal = Literal::String("hello".to_string());
assert_eq!(format!("{literal}"), "hello");
}
#[test_log::test]
fn test_literal_display_integer() {
let literal = Literal::Integer(42);
assert_eq!(format!("{literal}"), "42");
}
#[test_log::test]
fn test_literal_display_float() {
let literal = Literal::Float(4.567);
assert_eq!(format!("{literal}"), "4.567");
}
#[test_log::test]
fn test_literal_display_bool() {
let literal = Literal::Bool(true);
assert_eq!(format!("{literal}"), "true");
let literal_false = Literal::Bool(false);
assert_eq!(format!("{literal_false}"), "false");
}
#[test_log::test]
fn test_literal_display_unit() {
let literal = Literal::Unit;
assert_eq!(format!("{literal}"), "");
}
#[test_log::test]
fn test_literal_constructors() {
let string_lit = Literal::string("test");
assert_eq!(string_lit, Literal::String("test".to_string()));
let int_lit = Literal::integer(42i32);
assert_eq!(int_lit, Literal::Integer(42));
let float_lit = Literal::float(4.567f32);
match float_lit {
Literal::Float(f) => assert!((f - 4.567).abs() < 0.001),
_ => panic!("Expected Float literal"),
}
let bool_lit = Literal::bool(true);
assert_eq!(bool_lit, Literal::Bool(true));
}
#[cfg(feature = "logic")]
#[test_log::test]
fn test_element_variable_toggle_visibility() {
let element_var = ElementVariable {
name: "toggle-element".to_string(),
};
let action = element_var.toggle_visibility();
match action {
ActionType::Logic(if_action) => {
match &if_action.condition {
crate::logic::Condition::Eq(left, _) => match left {
crate::logic::Value::Calc(crate::logic::CalcValue::Visibility {
target,
}) => match target {
ElementTarget::ById(target_ref) => {
assert!(
matches!(target_ref, Target::Ref(name) if name == "toggle-element"),
"Expected Target::Ref for variable, got {target_ref:?}",
);
}
_ => panic!("Expected ElementTarget::ById"),
},
_ => panic!("Expected CalcValue::Visibility"),
},
crate::logic::Condition::Bool(_) => panic!("Expected Eq condition"),
}
}
_ => panic!("Expected Logic action type"),
}
}
#[cfg(feature = "logic")]
#[test_log::test]
fn test_element_variable_visibility_returns_calc_value() {
let element_var = ElementVariable {
name: "visibility-var".to_string(),
};
let calc_value = element_var.visibility();
match calc_value {
crate::logic::CalcValue::Visibility { target } => match target {
ElementTarget::ById(target_ref) => {
assert!(
matches!(&target_ref, Target::Ref(name) if name == "visibility-var"),
"Expected Target::Ref for variable, got {target_ref:?}",
);
}
_ => panic!("Expected ElementTarget::ById"),
},
_ => panic!("Expected CalcValue::Visibility"),
}
}
#[cfg(feature = "logic")]
#[test_log::test]
fn test_element_variable_toggle_display() {
let element_var = ElementVariable {
name: "display-toggle".to_string(),
};
let action = element_var.toggle_display();
match action {
ActionType::Logic(if_action) => match &if_action.condition {
crate::logic::Condition::Eq(left, _) => match left {
crate::logic::Value::Calc(crate::logic::CalcValue::Display { target }) => {
match target {
ElementTarget::ById(target_ref) => {
assert!(
matches!(target_ref, Target::Ref(name) if name == "display-toggle"),
"Expected Target::Ref, got {target_ref:?}",
);
}
_ => panic!("Expected ElementTarget::ById"),
}
}
_ => panic!("Expected CalcValue::Display"),
},
crate::logic::Condition::Bool(_) => panic!("Expected Eq condition"),
},
_ => panic!("Expected Logic action type"),
}
}
#[cfg(feature = "logic")]
#[test_log::test]
fn test_element_variable_get_display_returns_calc_value() {
let element_var = ElementVariable {
name: "get-display-var".to_string(),
};
let calc_value = element_var.get_display();
match calc_value {
crate::logic::CalcValue::Display { target } => match target {
ElementTarget::ById(target_ref) => {
assert!(
matches!(&target_ref, Target::Ref(name) if name == "get-display-var"),
"Expected Target::Ref, got {target_ref:?}",
);
}
_ => panic!("Expected ElementTarget::ById"),
},
_ => panic!("Expected CalcValue::Display"),
}
}
#[cfg(feature = "logic")]
#[test_log::test]
fn test_element_variable_get_width_px_returns_calc_value() {
let element_var = ElementVariable {
name: "width-element".to_string(),
};
let calc_value = element_var.get_width_px();
match calc_value {
crate::logic::CalcValue::WidthPx { target } => match target {
ElementTarget::ById(target_ref) => {
assert!(
matches!(&target_ref, Target::Ref(name) if name == "width-element"),
"Expected Target::Ref, got {target_ref:?}",
);
}
_ => panic!("Expected ElementTarget::ById"),
},
_ => panic!("Expected CalcValue::WidthPx"),
}
}
#[cfg(feature = "logic")]
#[test_log::test]
fn test_element_variable_get_height_px_returns_calc_value() {
let element_var = ElementVariable {
name: "height-element".to_string(),
};
let calc_value = element_var.get_height_px();
match calc_value {
crate::logic::CalcValue::HeightPx { target } => match target {
ElementTarget::ById(target_ref) => {
assert!(
matches!(&target_ref, Target::Ref(name) if name == "height-element"),
"Expected Target::Ref, got {target_ref:?}",
);
}
_ => panic!("Expected ElementTarget::ById"),
},
_ => panic!("Expected CalcValue::HeightPx"),
}
}
#[cfg(feature = "logic")]
#[test_log::test]
fn test_element_variable_get_mouse_x_returns_calc_value() {
let element_var = ElementVariable {
name: "mouse-x-element".to_string(),
};
let calc_value = element_var.get_mouse_x();
match calc_value {
crate::logic::CalcValue::MouseX { target } => match target {
Some(ElementTarget::ById(target_ref)) => {
assert!(
matches!(&target_ref, Target::Ref(name) if name == "mouse-x-element"),
"Expected Target::Ref, got {target_ref:?}",
);
}
_ => panic!("Expected Some(ElementTarget::ById)"),
},
_ => panic!("Expected CalcValue::MouseX"),
}
}
#[cfg(feature = "logic")]
#[test_log::test]
fn test_element_variable_get_mouse_y_returns_calc_value() {
let element_var = ElementVariable {
name: "mouse-y-element".to_string(),
};
let calc_value = element_var.get_mouse_y();
match calc_value {
crate::logic::CalcValue::MouseY { target } => match target {
Some(ElementTarget::ById(target_ref)) => {
assert!(
matches!(&target_ref, Target::Ref(name) if name == "mouse-y-element"),
"Expected Target::Ref, got {target_ref:?}",
);
}
_ => panic!("Expected Some(ElementTarget::ById)"),
},
_ => panic!("Expected CalcValue::MouseY"),
}
}
}