#![no_std]
#![cfg_attr(feature = "fail-on-warnings", deny(warnings))]
#![warn(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)]
#![allow(clippy::multiple_crate_versions)]
extern crate alloc;
use alloc::string::ToString;
use alloc::{borrow::Cow, boxed::Box, string::String, sync::Arc, vec::Vec};
use core::fmt::{Arguments, Write};
pub use hyperchad_template_macros::container;
pub use hyperchad_transformer::Container;
pub use hyperchad_actions as actions;
pub use hyperchad_color as color;
pub use hyperchad_template_actions_dsl as template_actions_dsl;
pub use hyperchad_transformer as transformer;
pub use hyperchad_transformer_models as transformer_models;
#[allow(clippy::mixed_attributes_style)]
pub mod prelude {
pub use crate::{
self as hyperchad_template, ContainerVecExt, ContainerVecMethods, IntoActionEffect,
IntoBorder, ToBool, actions as hyperchad_actions, calc, color as hyperchad_color, fx,
template_actions_dsl as hyperchad_template_actions_dsl,
transformer as hyperchad_transformer, transformer_models as hyperchad_transformer_models,
};
}
pub type Containers = Vec<Container>;
pub trait ContainerVecMethods {
fn display_to_string(
&self,
with_debug_attrs: bool,
wrap_raw_in_element: bool,
) -> Result<String, Box<dyn core::error::Error>>;
fn display_to_string_pretty(
&self,
with_debug_attrs: bool,
wrap_raw_in_element: bool,
) -> Result<String, Box<dyn core::error::Error>>;
#[must_use]
fn to_string(&self) -> String;
#[must_use]
fn into_string(self) -> String;
}
impl ContainerVecMethods for Vec<Container> {
fn display_to_string(
&self,
with_debug_attrs: bool,
wrap_raw_in_element: bool,
) -> Result<String, Box<dyn core::error::Error>> {
self.iter()
.map(|c| c.display_to_string_default(with_debug_attrs, wrap_raw_in_element))
.collect::<Result<String, _>>()
}
fn display_to_string_pretty(
&self,
with_debug_attrs: bool,
wrap_raw_in_element: bool,
) -> Result<String, Box<dyn core::error::Error>> {
self.iter()
.map(|c| c.display_to_string_default_pretty(with_debug_attrs, wrap_raw_in_element))
.collect::<Result<String, _>>()
}
fn to_string(&self) -> String {
self.iter().map(ToString::to_string).collect::<String>()
}
fn into_string(self) -> String {
self.iter().map(ToString::to_string).collect::<String>()
}
}
#[must_use]
pub fn to_html(containers: &[Container]) -> String {
containers
.iter()
.map(ToString::to_string)
.collect::<String>()
}
#[must_use]
pub fn into_html(containers: &[Container]) -> String {
containers
.iter()
.map(ToString::to_string)
.collect::<String>()
}
pub trait ContainerVecExt {
fn display_to_string(
&self,
with_debug_attrs: bool,
wrap_raw_in_element: bool,
) -> Result<String, Box<dyn core::error::Error>>;
fn display_to_string_pretty(
&self,
with_debug_attrs: bool,
wrap_raw_in_element: bool,
) -> Result<String, Box<dyn core::error::Error>>;
#[must_use]
fn into_string(self) -> String;
#[must_use]
fn to_string(&self) -> String;
}
impl ContainerVecExt for Vec<Container> {
fn display_to_string(
&self,
with_debug_attrs: bool,
wrap_raw_in_element: bool,
) -> Result<String, Box<dyn core::error::Error>> {
self.iter()
.map(|c| c.display_to_string_default(with_debug_attrs, wrap_raw_in_element))
.collect::<Result<String, _>>()
}
fn display_to_string_pretty(
&self,
with_debug_attrs: bool,
wrap_raw_in_element: bool,
) -> Result<String, Box<dyn core::error::Error>> {
self.iter()
.map(|c| c.display_to_string_default_pretty(with_debug_attrs, wrap_raw_in_element))
.collect::<Result<String, _>>()
}
fn into_string(self) -> String {
self.iter().map(ToString::to_string).collect::<String>()
}
fn to_string(&self) -> String {
self.iter().map(ToString::to_string).collect::<String>()
}
}
pub trait RenderContainer {
type Error;
fn render_to(&self, containers: &mut Vec<Container>) -> Result<(), Self::Error>;
fn render_to_string(&self) -> Result<String, Self::Error> {
let mut containers = Vec::new();
self.render_to(&mut containers)?;
Ok(containers
.iter()
.map(ToString::to_string)
.collect::<String>())
}
fn render(&self) -> Result<Vec<Container>, Self::Error> {
let mut containers = Vec::new();
self.render_to(&mut containers)?;
Ok(containers)
}
}
impl RenderContainer for Container {
type Error = core::fmt::Error;
fn render_to(&self, containers: &mut Vec<Container>) -> Result<(), Self::Error> {
containers.push(self.clone());
Ok(())
}
}
impl RenderContainer for &str {
type Error = core::fmt::Error;
fn render_to(&self, containers: &mut Vec<Container>) -> Result<(), Self::Error> {
if self.is_empty() {
return Ok(());
}
containers.push(Container {
element: hyperchad_transformer::Element::Text {
value: (*self).to_string(),
},
..Default::default()
});
Ok(())
}
}
impl RenderContainer for String {
type Error = core::fmt::Error;
fn render_to(&self, containers: &mut Vec<Container>) -> Result<(), Self::Error> {
if self.is_empty() {
return Ok(());
}
containers.push(Container {
element: hyperchad_transformer::Element::Text {
value: self.clone(),
},
..Default::default()
});
Ok(())
}
}
impl RenderContainer for Cow<'_, str> {
type Error = core::fmt::Error;
fn render_to(&self, containers: &mut Vec<Container>) -> Result<(), Self::Error> {
if self.is_empty() {
return Ok(());
}
<&str as RenderContainer>::render_to(&self.as_ref(), containers)
}
}
impl RenderContainer for Arguments<'_> {
type Error = core::fmt::Error;
fn render_to(&self, containers: &mut Vec<Container>) -> Result<(), Self::Error> {
let mut s = String::new();
s.write_fmt(*self)?;
containers.push(Container {
element: hyperchad_transformer::Element::Text { value: s },
..Default::default()
});
Ok(())
}
}
impl<T: RenderContainer + ?Sized> RenderContainer for &T {
type Error = T::Error;
fn render_to(&self, containers: &mut Vec<Container>) -> Result<(), Self::Error> {
T::render_to(self, containers)
}
}
impl<T: RenderContainer + ?Sized> RenderContainer for &mut T {
type Error = T::Error;
fn render_to(&self, containers: &mut Vec<Container>) -> Result<(), Self::Error> {
T::render_to(self, containers)
}
}
impl<T: RenderContainer + ?Sized> RenderContainer for Box<T> {
type Error = T::Error;
fn render_to(&self, containers: &mut Vec<Container>) -> Result<(), Self::Error> {
T::render_to(self, containers)
}
}
impl<T: RenderContainer + ?Sized> RenderContainer for Arc<T> {
type Error = T::Error;
fn render_to(&self, containers: &mut Vec<Container>) -> Result<(), Self::Error> {
T::render_to(self, containers)
}
}
macro_rules! impl_render_container_with_itoa {
($($ty:ty)*) => {
$(
impl RenderContainer for $ty {
type Error = core::fmt::Error;
fn render_to(
&self,
containers: &mut Vec<Container>,
) -> Result<(), Self::Error> {
let mut buffer = itoa::Buffer::new();
let s = buffer.format(*self);
<&str as RenderContainer>::render_to(&s, containers)
}
}
)*
};
}
impl_render_container_with_itoa! {
i8 i16 i32 i64 i128 isize
u8 u16 u32 u64 u128 usize
}
impl RenderContainer for f32 {
type Error = core::fmt::Error;
fn render_to(&self, containers: &mut Vec<Container>) -> Result<(), Self::Error> {
let mut buffer = ryu::Buffer::new();
let s = buffer.format(*self);
<&str as RenderContainer>::render_to(&s, containers)
}
}
impl RenderContainer for f64 {
type Error = core::fmt::Error;
fn render_to(&self, containers: &mut Vec<Container>) -> Result<(), Self::Error> {
let mut buffer = ryu::Buffer::new();
let s = buffer.format(*self);
<&str as RenderContainer>::render_to(&s, containers)
}
}
impl RenderContainer for bool {
type Error = core::fmt::Error;
fn render_to(&self, containers: &mut Vec<Container>) -> Result<(), Self::Error> {
let s = if *self { "true" } else { "false" };
<&str as RenderContainer>::render_to(&s, containers)
}
}
impl RenderContainer for char {
type Error = core::fmt::Error;
fn render_to(&self, containers: &mut Vec<Container>) -> Result<(), Self::Error> {
let mut buffer = [0; 4];
let s = self.encode_utf8(&mut buffer);
let s: &str = s;
<&str as RenderContainer>::render_to(&s, containers)
}
}
impl<T: RenderContainer> RenderContainer for Option<T> {
type Error = T::Error;
fn render_to(&self, containers: &mut Vec<Container>) -> Result<(), Self::Error> {
self.as_ref()
.map_or_else(|| Ok(()), |value| value.render_to(containers))
}
}
impl RenderContainer for Vec<Container> {
type Error = core::fmt::Error;
fn render_to(&self, containers: &mut Vec<Container>) -> Result<(), Self::Error> {
containers.extend_from_slice(self);
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct ContainerList(pub Vec<Container>);
impl core::fmt::Display for ContainerList {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.0
.iter()
.try_for_each(|c| f.write_fmt(format_args!("{c}")))
}
}
impl ContainerList {
#[must_use]
pub const fn new(containers: Vec<Container>) -> Self {
Self(containers)
}
#[must_use]
pub fn into_string(self) -> String {
self.0.iter().map(ToString::to_string).collect::<String>()
}
pub fn iter(&self) -> core::slice::Iter<'_, Container> {
self.0.iter()
}
#[must_use]
pub fn into_inner(self) -> Vec<Container> {
self.0
}
#[must_use]
pub const fn as_inner(&self) -> &Vec<Container> {
&self.0
}
}
impl<'a> IntoIterator for &'a ContainerList {
type Item = &'a Container;
type IntoIter = core::slice::Iter<'a, Container>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
impl IntoIterator for ContainerList {
type Item = Container;
type IntoIter = alloc::vec::IntoIter<Container>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl From<Vec<Container>> for ContainerList {
fn from(containers: Vec<Container>) -> Self {
Self(containers)
}
}
impl From<ContainerList> for Vec<Container> {
fn from(list: ContainerList) -> Self {
list.0
}
}
impl core::ops::Deref for ContainerList {
type Target = Vec<Container>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl core::ops::DerefMut for ContainerList {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
pub use hyperchad_transformer_models::*;
#[cfg(feature = "logic")]
pub use hyperchad_actions::logic::{IfExpression, Responsive, if_responsive};
pub trait ToBool {
#[must_use]
fn to_bool(self) -> bool;
}
impl ToBool for bool {
fn to_bool(self) -> bool {
self
}
}
#[cfg(feature = "logic")]
impl ToBool for IfExpression<bool, Responsive> {
fn to_bool(self) -> bool {
self.default
.unwrap_or_else(|| self.value.unwrap_or_default())
}
}
pub trait IntoActionEffect {
#[must_use]
fn into_action_effect(self) -> actions::ActionEffect;
}
impl IntoActionEffect for actions::Action {
fn into_action_effect(self) -> actions::ActionEffect {
self.effect
}
}
impl IntoActionEffect for actions::ActionEffect {
fn into_action_effect(self) -> actions::ActionEffect {
self
}
}
impl IntoActionEffect for Vec<actions::ActionEffect> {
fn into_action_effect(self) -> actions::ActionEffect {
actions::ActionType::MultiEffect(self).into()
}
}
impl IntoActionEffect for actions::ActionType {
fn into_action_effect(self) -> actions::ActionEffect {
self.into()
}
}
impl IntoActionEffect for Vec<actions::ActionType> {
fn into_action_effect(self) -> actions::ActionEffect {
self.into_iter()
.map(IntoActionEffect::into_action_effect)
.collect::<Vec<_>>()
.into()
}
}
#[cfg(feature = "logic")]
impl IntoActionEffect for actions::logic::If {
fn into_action_effect(self) -> actions::ActionEffect {
actions::ActionType::Logic(self).into()
}
}
pub trait IntoBorder {
#[must_use]
fn into_border(self) -> (hyperchad_color::Color, hyperchad_transformer::Number);
}
impl IntoBorder for (hyperchad_color::Color, hyperchad_transformer::Number) {
fn into_border(self) -> (hyperchad_color::Color, hyperchad_transformer::Number) {
self
}
}
impl IntoBorder for (hyperchad_color::Color, i32) {
fn into_border(self) -> (hyperchad_color::Color, hyperchad_transformer::Number) {
(
self.0,
hyperchad_transformer::Number::Integer(i64::from(self.1)),
)
}
}
impl IntoBorder for (hyperchad_color::Color, u16) {
fn into_border(self) -> (hyperchad_color::Color, hyperchad_transformer::Number) {
(
self.0,
hyperchad_transformer::Number::Integer(i64::from(self.1)),
)
}
}
impl IntoBorder for (hyperchad_color::Color, f32) {
fn into_border(self) -> (hyperchad_color::Color, hyperchad_transformer::Number) {
(self.0, hyperchad_transformer::Number::Real(self.1))
}
}
impl IntoBorder for (hyperchad_color::Color, f64) {
#[allow(clippy::cast_possible_truncation)]
fn into_border(self) -> (hyperchad_color::Color, hyperchad_transformer::Number) {
(self.0, hyperchad_transformer::Number::Real(self.1 as f32))
}
}
impl IntoBorder for (i32, hyperchad_color::Color) {
fn into_border(self) -> (hyperchad_color::Color, hyperchad_transformer::Number) {
(
self.1,
hyperchad_transformer::Number::Integer(i64::from(self.0)),
)
}
}
impl IntoBorder for (u16, hyperchad_color::Color) {
fn into_border(self) -> (hyperchad_color::Color, hyperchad_transformer::Number) {
(
self.1,
hyperchad_transformer::Number::Integer(i64::from(self.0)),
)
}
}
impl IntoBorder for (f32, hyperchad_color::Color) {
fn into_border(self) -> (hyperchad_color::Color, hyperchad_transformer::Number) {
(self.1, hyperchad_transformer::Number::Real(self.0))
}
}
impl IntoBorder for (f64, hyperchad_color::Color) {
#[allow(clippy::cast_possible_truncation)]
fn into_border(self) -> (hyperchad_color::Color, hyperchad_transformer::Number) {
(self.1, hyperchad_transformer::Number::Real(self.0 as f32))
}
}
impl IntoBorder for (i32, &str) {
fn into_border(self) -> (hyperchad_color::Color, hyperchad_transformer::Number) {
(
hyperchad_color::Color::from_hex(self.1),
hyperchad_transformer::Number::Integer(i64::from(self.0)),
)
}
}
impl IntoBorder for (u16, &str) {
fn into_border(self) -> (hyperchad_color::Color, hyperchad_transformer::Number) {
(
hyperchad_color::Color::from_hex(self.1),
hyperchad_transformer::Number::Integer(i64::from(self.0)),
)
}
}
impl IntoBorder for (f32, &str) {
fn into_border(self) -> (hyperchad_color::Color, hyperchad_transformer::Number) {
(
hyperchad_color::Color::from_hex(self.1),
hyperchad_transformer::Number::Real(self.0),
)
}
}
impl IntoBorder for (f64, &str) {
#[allow(clippy::cast_possible_truncation)]
fn into_border(self) -> (hyperchad_color::Color, hyperchad_transformer::Number) {
(
hyperchad_color::Color::from_hex(self.1),
hyperchad_transformer::Number::Real(self.0 as f32),
)
}
}
impl IntoBorder for (&str, i32) {
fn into_border(self) -> (hyperchad_color::Color, hyperchad_transformer::Number) {
(
hyperchad_color::Color::from_hex(self.0),
hyperchad_transformer::Number::Integer(i64::from(self.1)),
)
}
}
impl IntoBorder for (&str, u16) {
fn into_border(self) -> (hyperchad_color::Color, hyperchad_transformer::Number) {
(
hyperchad_color::Color::from_hex(self.0),
hyperchad_transformer::Number::Integer(i64::from(self.1)),
)
}
}
impl IntoBorder for (&str, f32) {
fn into_border(self) -> (hyperchad_color::Color, hyperchad_transformer::Number) {
(
hyperchad_color::Color::from_hex(self.0),
hyperchad_transformer::Number::Real(self.1),
)
}
}
impl IntoBorder for (&str, f64) {
#[allow(clippy::cast_possible_truncation)]
fn into_border(self) -> (hyperchad_color::Color, hyperchad_transformer::Number) {
(
hyperchad_color::Color::from_hex(self.0),
hyperchad_transformer::Number::Real(self.1 as f32),
)
}
}
impl IntoBorder for (i32, String) {
fn into_border(self) -> (hyperchad_color::Color, hyperchad_transformer::Number) {
(
hyperchad_color::Color::from_hex(&self.1),
hyperchad_transformer::Number::Integer(i64::from(self.0)),
)
}
}
impl IntoBorder for (u16, String) {
fn into_border(self) -> (hyperchad_color::Color, hyperchad_transformer::Number) {
(
hyperchad_color::Color::from_hex(&self.1),
hyperchad_transformer::Number::Integer(i64::from(self.0)),
)
}
}
impl IntoBorder for (f32, String) {
fn into_border(self) -> (hyperchad_color::Color, hyperchad_transformer::Number) {
(
hyperchad_color::Color::from_hex(&self.1),
hyperchad_transformer::Number::Real(self.0),
)
}
}
impl IntoBorder for (f64, String) {
#[allow(clippy::cast_possible_truncation)]
fn into_border(self) -> (hyperchad_color::Color, hyperchad_transformer::Number) {
(
hyperchad_color::Color::from_hex(&self.1),
hyperchad_transformer::Number::Real(self.0 as f32),
)
}
}
impl IntoBorder for (String, i32) {
fn into_border(self) -> (hyperchad_color::Color, hyperchad_transformer::Number) {
(
hyperchad_color::Color::from_hex(&self.0),
hyperchad_transformer::Number::Integer(i64::from(self.1)),
)
}
}
impl IntoBorder for (String, u16) {
fn into_border(self) -> (hyperchad_color::Color, hyperchad_transformer::Number) {
(
hyperchad_color::Color::from_hex(&self.0),
hyperchad_transformer::Number::Integer(i64::from(self.1)),
)
}
}
impl IntoBorder for (String, f32) {
fn into_border(self) -> (hyperchad_color::Color, hyperchad_transformer::Number) {
(
hyperchad_color::Color::from_hex(&self.0),
hyperchad_transformer::Number::Real(self.1),
)
}
}
impl IntoBorder for (String, f64) {
#[allow(clippy::cast_possible_truncation)]
fn into_border(self) -> (hyperchad_color::Color, hyperchad_transformer::Number) {
(
hyperchad_color::Color::from_hex(&self.0),
hyperchad_transformer::Number::Real(self.1 as f32),
)
}
}
#[allow(clippy::mixed_attributes_style)]
pub mod calc {
use hyperchad_transformer::Number;
#[must_use]
pub fn to_number<T: Into<Number>>(value: T) -> Number {
value.into()
}
#[must_use]
pub fn to_percent_number<T>(value: T) -> Number
where
T: Into<Number>,
{
let num = value.into();
match num {
Number::Integer(n) => Number::IntegerPercent(n),
Number::Real(n) => Number::RealPercent(n),
Number::IntegerPercent(_) | Number::RealPercent(_) => num,
_ => {
let raw_value = num.calc(0.0, 100.0, 100.0);
Number::RealPercent(raw_value)
}
}
}
#[must_use]
pub fn to_vw_number<T>(value: T) -> Number
where
T: Into<Number>,
{
let num = value.into();
match num {
Number::Integer(n) => Number::IntegerVw(n),
Number::Real(n) => Number::RealVw(n),
Number::IntegerVw(_) | Number::RealVw(_) => num,
_ => {
let raw_value = num.calc(0.0, 100.0, 100.0);
Number::RealVw(raw_value)
}
}
}
#[must_use]
pub fn to_vh_number<T>(value: T) -> Number
where
T: Into<Number>,
{
let num = value.into();
match num {
Number::Integer(n) => Number::IntegerVh(n),
Number::Real(n) => Number::RealVh(n),
Number::IntegerVh(_) | Number::RealVh(_) => num,
_ => {
let raw_value = num.calc(0.0, 100.0, 100.0);
Number::RealVh(raw_value)
}
}
}
#[must_use]
pub fn to_dvw_number<T>(value: T) -> Number
where
T: Into<Number>,
{
let num = value.into();
match num {
Number::Integer(n) => Number::IntegerDvw(n),
Number::Real(n) => Number::RealDvw(n),
Number::IntegerDvw(_) | Number::RealDvw(_) => num,
_ => {
let raw_value = num.calc(0.0, 100.0, 100.0);
Number::RealDvw(raw_value)
}
}
}
#[must_use]
pub fn to_dvh_number<T>(value: T) -> Number
where
T: Into<Number>,
{
let num = value.into();
match num {
Number::Integer(n) => Number::IntegerDvh(n),
Number::Real(n) => Number::RealDvh(n),
Number::IntegerDvh(_) | Number::RealDvh(_) => num,
_ => {
let raw_value = num.calc(0.0, 100.0, 100.0);
Number::RealDvh(raw_value)
}
}
}
#[must_use]
pub fn add_numbers(left: &Number, right: &Number) -> Number {
#[allow(clippy::cast_precision_loss)]
match (left, right) {
(Number::Integer(a), Number::Integer(b)) => Number::Integer(a + b),
(Number::Real(a), Number::Real(b)) => Number::Real(a + b),
(Number::IntegerPercent(a), Number::IntegerPercent(b)) => Number::IntegerPercent(a + b),
(Number::RealPercent(a), Number::RealPercent(b)) => Number::RealPercent(a + b),
(Number::IntegerVw(a), Number::IntegerVw(b)) => Number::IntegerVw(a + b),
(Number::RealVw(a), Number::RealVw(b)) => Number::RealVw(a + b),
(Number::IntegerVh(a), Number::IntegerVh(b)) => Number::IntegerVh(a + b),
(Number::RealVh(a), Number::RealVh(b)) => Number::RealVh(a + b),
(Number::IntegerDvw(a), Number::IntegerDvw(b)) => Number::IntegerDvw(a + b),
(Number::RealDvw(a), Number::RealDvw(b)) => Number::RealDvw(a + b),
(Number::IntegerDvh(a), Number::IntegerDvh(b)) => Number::IntegerDvh(a + b),
(Number::RealDvh(a), Number::RealDvh(b)) => Number::RealDvh(a + b),
(Number::Integer(a), Number::Real(b)) => Number::Real(*a as f32 + b),
(Number::Real(a), Number::Integer(b)) => Number::Real(a + *b as f32),
(Number::IntegerPercent(a), Number::RealPercent(b)) => {
Number::RealPercent(*a as f32 + b)
}
(Number::RealPercent(a), Number::IntegerPercent(b)) => {
Number::RealPercent(a + *b as f32)
}
(Number::IntegerVw(a), Number::RealVw(b)) => Number::RealVw(*a as f32 + b),
(Number::RealVw(a), Number::IntegerVw(b)) => Number::RealVw(a + *b as f32),
(Number::IntegerVh(a), Number::RealVh(b)) => Number::RealVh(*a as f32 + b),
(Number::RealVh(a), Number::IntegerVh(b)) => Number::RealVh(a + *b as f32),
(Number::IntegerDvw(a), Number::RealDvw(b)) => Number::RealDvw(*a as f32 + b),
(Number::RealDvw(a), Number::IntegerDvw(b)) => Number::RealDvw(a + *b as f32),
(Number::IntegerDvh(a), Number::RealDvh(b)) => Number::RealDvh(*a as f32 + b),
(Number::RealDvh(a), Number::IntegerDvh(b)) => Number::RealDvh(a + *b as f32),
_ => {
let left_pixels = left.calc(100.0, 1920.0, 1080.0);
let right_pixels = right.calc(100.0, 1920.0, 1080.0);
Number::Real(left_pixels + right_pixels)
}
}
}
#[must_use]
pub fn subtract_numbers(left: &Number, right: &Number) -> Number {
#[allow(clippy::cast_precision_loss)]
match (left, right) {
(Number::Integer(a), Number::Integer(b)) => Number::Integer(a - b),
(Number::Real(a), Number::Real(b)) => Number::Real(a - b),
(Number::IntegerPercent(a), Number::IntegerPercent(b)) => Number::IntegerPercent(a - b),
(Number::RealPercent(a), Number::RealPercent(b)) => Number::RealPercent(a - b),
(Number::IntegerVw(a), Number::IntegerVw(b)) => Number::IntegerVw(a - b),
(Number::RealVw(a), Number::RealVw(b)) => Number::RealVw(a - b),
(Number::IntegerVh(a), Number::IntegerVh(b)) => Number::IntegerVh(a - b),
(Number::RealVh(a), Number::RealVh(b)) => Number::RealVh(a - b),
(Number::IntegerDvw(a), Number::IntegerDvw(b)) => Number::IntegerDvw(a - b),
(Number::RealDvw(a), Number::RealDvw(b)) => Number::RealDvw(a - b),
(Number::IntegerDvh(a), Number::IntegerDvh(b)) => Number::IntegerDvh(a - b),
(Number::RealDvh(a), Number::RealDvh(b)) => Number::RealDvh(a - b),
(Number::Integer(a), Number::Real(b)) => Number::Real(*a as f32 - b),
(Number::Real(a), Number::Integer(b)) => Number::Real(a - *b as f32),
(Number::IntegerPercent(a), Number::RealPercent(b)) => {
Number::RealPercent(*a as f32 - b)
}
(Number::RealPercent(a), Number::IntegerPercent(b)) => {
Number::RealPercent(a - *b as f32)
}
(Number::IntegerVw(a), Number::RealVw(b)) => Number::RealVw(*a as f32 - b),
(Number::RealVw(a), Number::IntegerVw(b)) => Number::RealVw(a - *b as f32),
(Number::IntegerVh(a), Number::RealVh(b)) => Number::RealVh(*a as f32 - b),
(Number::RealVh(a), Number::IntegerVh(b)) => Number::RealVh(a - *b as f32),
(Number::IntegerDvw(a), Number::RealDvw(b)) => Number::RealDvw(*a as f32 - b),
(Number::RealDvw(a), Number::IntegerDvw(b)) => Number::RealDvw(a - *b as f32),
(Number::IntegerDvh(a), Number::RealDvh(b)) => Number::RealDvh(*a as f32 - b),
(Number::RealDvh(a), Number::IntegerDvh(b)) => Number::RealDvh(a - *b as f32),
_ => {
let left_pixels = left.calc(100.0, 1920.0, 1080.0);
let right_pixels = right.calc(100.0, 1920.0, 1080.0);
Number::Real(left_pixels - right_pixels)
}
}
}
#[must_use]
pub fn multiply_numbers(left: &Number, right: &Number) -> Number {
#[allow(clippy::cast_precision_loss)]
match (left, right) {
(Number::Integer(a), Number::Integer(b)) => Number::Integer(a * b),
(Number::Real(a), Number::Real(b)) => Number::Real(a * b),
(Number::Integer(a), Number::Real(b)) => Number::Real(*a as f32 * b),
(Number::Real(a), Number::Integer(b)) => Number::Real(a * *b as f32),
(Number::IntegerPercent(a), Number::Integer(b))
| (Number::Integer(a), Number::IntegerPercent(b)) => Number::IntegerPercent(a * b),
(Number::RealPercent(a), Number::Real(b))
| (Number::Real(a), Number::RealPercent(b)) => Number::RealPercent(a * b),
_ => {
let left_pixels = left.calc(100.0, 1920.0, 1080.0);
let right_pixels = right.calc(100.0, 1920.0, 1080.0);
Number::Real(left_pixels * right_pixels)
}
}
}
#[must_use]
pub fn divide_numbers(left: &Number, right: &Number) -> Number {
#[allow(clippy::cast_precision_loss)]
match (left, right) {
(Number::Integer(a), Number::Integer(b)) => {
if *b != 0 {
Number::Real(*a as f32 / *b as f32)
} else {
Number::Real(0.0) }
}
(Number::Real(a), Number::Real(b)) => {
if *b == 0.0 {
Number::Real(0.0)
} else {
Number::Real(a / b)
}
}
(Number::Integer(a), Number::Real(b)) => {
if *b == 0.0 {
Number::Real(0.0)
} else {
Number::Real(*a as f32 / b)
}
}
(Number::Real(a), Number::Integer(b)) => {
if *b != 0 {
Number::Real(a / *b as f32)
} else {
Number::Real(0.0)
}
}
(Number::IntegerPercent(a), Number::Integer(b)) => {
if *b != 0 {
Number::RealPercent(*a as f32 / *b as f32)
} else {
Number::RealPercent(0.0)
}
}
(Number::RealPercent(a), Number::Real(b)) => {
if *b == 0.0 {
Number::RealPercent(0.0)
} else {
Number::RealPercent(a / b)
}
}
_ => {
let left_pixels = left.calc(100.0, 1920.0, 1080.0);
let right_pixels = right.calc(100.0, 1920.0, 1080.0);
if right_pixels == 0.0 {
Number::Real(0.0)
} else {
Number::Real(left_pixels / right_pixels)
}
}
}
}
}
#[allow(clippy::mixed_attributes_style)]
pub mod unit_functions {
use hyperchad_transformer::Number;
fn number_to_f32(num: &Number) -> f32 {
num.calc(0.0, 100.0, 100.0)
}
#[must_use]
pub fn vw<T: Into<Number>>(value: T) -> Number {
let num = value.into();
match num {
Number::Integer(n) => Number::IntegerVw(n),
Number::Real(n) => Number::RealVw(n),
_ => Number::RealVw(number_to_f32(&num)),
}
}
#[must_use]
pub fn vh<T: Into<Number>>(value: T) -> Number {
let num = value.into();
match num {
Number::Integer(n) => Number::IntegerVh(n),
Number::Real(n) => Number::RealVh(n),
_ => Number::RealVh(number_to_f32(&num)),
}
}
#[must_use]
pub fn dvw<T: Into<Number>>(value: T) -> Number {
let num = value.into();
match num {
Number::Integer(n) => Number::IntegerDvw(n),
Number::Real(n) => Number::RealDvw(n),
_ => Number::RealDvw(number_to_f32(&num)),
}
}
#[must_use]
pub fn dvh<T: Into<Number>>(value: T) -> Number {
let num = value.into();
match num {
Number::Integer(n) => Number::IntegerDvh(n),
Number::Real(n) => Number::RealDvh(n),
_ => Number::RealDvh(number_to_f32(&num)),
}
}
}
#[must_use]
pub fn fx<T>(content: T) -> actions::ActionEffect
where
T: IntoActionEffect,
{
content.into_action_effect()
}
#[allow(clippy::mixed_attributes_style)]
pub mod color_functions {
use alloc::string::String;
use hyperchad_color::Color;
#[must_use]
pub fn rgb<R, G, B>(red: R, green: G, blue: B) -> Color
where
R: ToRgbValue,
G: ToRgbValue,
B: ToRgbValue,
{
Color {
r: red.to_rgb_value(),
g: green.to_rgb_value(),
b: blue.to_rgb_value(),
a: None,
}
}
#[must_use]
pub fn rgb_alpha<R, G, B, A>(red: R, green: G, blue: B, alpha: A) -> Color
where
R: ToRgbValue,
G: ToRgbValue,
B: ToRgbValue,
A: Into<AlphaValue>,
{
let alpha_val = alpha.into().to_u8();
Color {
r: red.to_rgb_value(),
g: green.to_rgb_value(),
b: blue.to_rgb_value(),
a: Some(alpha_val),
}
}
#[must_use]
pub fn rgba<R, G, B, A>(red: R, green: G, blue: B, alpha: A) -> Color
where
R: ToRgbValue,
G: ToRgbValue,
B: ToRgbValue,
A: Into<AlphaValue>,
{
rgb_alpha(red, green, blue, alpha)
}
pub enum AlphaValue {
Float(f32),
Integer(u8),
Percentage(f32),
}
impl AlphaValue {
#[must_use]
pub fn to_u8(self) -> u8 {
match self {
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
Self::Float(f) => {
let clamped = f.clamp(0.0, 1.0);
(clamped * 255.0).round() as u8
}
Self::Integer(i) => i,
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
Self::Percentage(p) => {
let clamped = p.clamp(0.0, 100.0);
(clamped / 100.0 * 255.0).round() as u8
}
}
}
}
impl From<f32> for AlphaValue {
fn from(f: f32) -> Self {
Self::Float(f)
}
}
impl From<f64> for AlphaValue {
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
fn from(f: f64) -> Self {
Self::Float(f as f32)
}
}
impl From<u8> for AlphaValue {
fn from(i: u8) -> Self {
Self::Integer(i)
}
}
impl From<i32> for AlphaValue {
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
fn from(i: i32) -> Self {
let clamped = i.clamp(0, 255);
Self::Integer(clamped as u8)
}
}
impl From<i64> for AlphaValue {
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
fn from(i: i64) -> Self {
let clamped = i.clamp(0, 255);
Self::Integer(clamped as u8)
}
}
impl From<u16> for AlphaValue {
fn from(i: u16) -> Self {
let clamped = i.min(255);
Self::Integer(clamped as u8)
}
}
impl From<u32> for AlphaValue {
fn from(i: u32) -> Self {
let clamped = i.min(255);
Self::Integer(clamped as u8)
}
}
impl From<u64> for AlphaValue {
fn from(i: u64) -> Self {
let clamped = i.min(255);
Self::Integer(clamped as u8)
}
}
impl From<&str> for AlphaValue {
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
fn from(s: &str) -> Self {
let trimmed = s.trim();
trimmed.strip_suffix('%').map_or_else(
|| {
trimmed.parse::<f32>().map_or(Self::Integer(0), |f| {
if f <= 1.0 {
Self::Float(f)
} else {
Self::Integer(f.clamp(0.0, 255.0) as u8)
}
})
},
|percent_str| {
percent_str
.parse::<f32>()
.map_or(Self::Percentage(0.0), Self::Percentage)
},
)
}
}
impl From<String> for AlphaValue {
fn from(s: String) -> Self {
Self::from(s.as_str())
}
}
pub trait ToRgbValue {
#[must_use]
fn to_rgb_value(self) -> u8;
}
impl ToRgbValue for u8 {
fn to_rgb_value(self) -> u8 {
self
}
}
impl ToRgbValue for i32 {
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
fn to_rgb_value(self) -> u8 {
self.clamp(0, 255) as u8
}
}
impl ToRgbValue for i64 {
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
fn to_rgb_value(self) -> u8 {
self.clamp(0, 255) as u8
}
}
impl ToRgbValue for u16 {
fn to_rgb_value(self) -> u8 {
self.min(255) as u8
}
}
impl ToRgbValue for u32 {
fn to_rgb_value(self) -> u8 {
self.min(255) as u8
}
}
impl ToRgbValue for u64 {
fn to_rgb_value(self) -> u8 {
self.min(255) as u8
}
}
impl ToRgbValue for f32 {
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
fn to_rgb_value(self) -> u8 {
self.clamp(0.0, 255.0).round() as u8
}
}
impl ToRgbValue for f64 {
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
fn to_rgb_value(self) -> u8 {
self.clamp(0.0, 255.0).round() as u8
}
}
}