#[cfg(feature = "serde")]
use convert_case::{Case, Casing};
#[cfg(feature = "serde")]
use lazy_static::lazy_static;
#[cfg(feature = "serde")]
use regex::Regex;
#[cfg(feature = "serde")]
use serde::{
de::{value::StringDeserializer, DeserializeOwned, IntoDeserializer},
Deserialize, Deserializer,
};
#[cfg(feature = "serde")]
use std::str::FromStr;
use taffy::node::Node as TaffyNode;
use taffy::prelude::*;
#[derive(Default, Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(default))]
#[cfg_attr(all(feature = "serde"), serde(rename_all = "kebab-case"))]
pub struct FlexNode {
#[cfg_attr(feature = "serde", serde(flatten))]
pub element: Option<FlexElement>,
#[cfg_attr(feature = "serde", serde(flatten))]
pub layout: FlexLayout,
#[cfg_attr(feature = "serde", serde(flatten))]
pub root: Option<FlexRootData>,
#[cfg_attr(
feature = "serde",
serde(skip_serializing_if = "Vec::is_empty", default)
)]
pub nodes: Vec<FlexNode>,
}
#[derive(Default, Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(default))]
#[cfg_attr(all(feature = "serde"), serde(rename_all = "kebab-case"))]
pub struct FlexRootData {
pub stylesheet: Option<String>,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
#[cfg_attr(feature = "serde", serde(default))]
#[cfg_attr(all(feature = "serde"), serde(rename_all = "kebab-case"))]
pub struct FlexGeomNode {
pub width: f32,
pub height: f32,
pub position: FlexPoint,
pub element: Option<FlexElement>,
pub root: Option<FlexRootData>,
pub nodes: Vec<FlexGeomNode>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(untagged))]
pub enum FlexElement {
Text(FlexText),
Rect(FlexRect),
}
#[derive(Default, Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(default))]
pub struct FlexGlobalAttribs {
pub id: String,
pub class: String,
pub style: String,
}
#[derive(Default, Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct FlexText {
#[cfg_attr(feature = "serde", serde(flatten))]
pub global: FlexGlobalAttribs,
#[cfg_attr(feature = "serde", serde(flatten))]
pub attribs: FlexTextAttribs,
pub text: String,
}
#[derive(Default, Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(all(feature = "serde"), serde(rename_all = "kebab-case"))]
#[cfg_attr(feature = "serde", serde(default))]
pub struct FlexTextAttribs {}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(all(feature = "serde"), serde(rename_all = "kebab-case"))]
pub enum FlexSvgTextAnchor {
Start,
Middle,
End,
}
impl Default for FlexSvgTextAnchor {
fn default() -> Self {
Self::Middle
}
}
#[derive(Default, Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct FlexRect {
#[cfg_attr(feature = "serde", serde(flatten))]
pub global: FlexGlobalAttribs,
#[cfg_attr(feature = "serde", serde(flatten))]
pub attribs: FlexRectAttribs,
pub rect: bool,
}
#[derive(Default, Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(default))]
pub struct FlexRectAttribs {
pub rx: FlexDimension,
pub ry: FlexDimension,
}
#[derive(Debug, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(default))]
#[cfg_attr(all(feature = "serde"), serde(rename_all = "kebab-case"))]
pub struct FlexLayout {
pub display: Display,
#[cfg_attr(
feature = "serde",
serde(deserialize_with = "deserialize_simple_enum_as_pascal")
)]
pub position_type: PositionType,
#[cfg_attr(
feature = "serde",
serde(deserialize_with = "deserialize_simple_enum_as_pascal")
)]
pub flex_direction: FlexDirection,
#[cfg_attr(
feature = "serde",
serde(deserialize_with = "deserialize_simple_enum_as_pascal")
)]
pub flex_wrap: FlexWrap,
#[cfg_attr(
feature = "serde",
serde(deserialize_with = "deserialize_simple_enum_as_pascal")
)]
pub align_items: AlignItems,
#[cfg_attr(
feature = "serde",
serde(deserialize_with = "deserialize_simple_enum_as_pascal")
)]
pub align_self: AlignSelf,
#[cfg_attr(
feature = "serde",
serde(deserialize_with = "deserialize_simple_enum_as_pascal")
)]
pub align_content: AlignContent,
#[cfg_attr(
feature = "serde",
serde(deserialize_with = "deserialize_simple_enum_as_pascal")
)]
pub justify_content: JustifyContent,
pub position: Rect<Dimension>,
pub margin: Rect<Dimension>,
pub padding: Rect<Dimension>,
pub border: Rect<Dimension>,
pub flex_grow: f32,
pub flex_shrink: f32,
pub flex_basis: Dimension,
#[cfg_attr(feature = "serde", serde(flatten))]
pub size: FlexSize<FlexDimension>,
pub min_size: Size<Dimension>,
pub max_size: Size<Dimension>,
pub aspect_ratio: Number,
}
impl Default for FlexLayout {
fn default() -> Self {
Self {
display: Default::default(),
position_type: Default::default(),
flex_direction: Default::default(),
flex_wrap: Default::default(),
align_items: Default::default(),
align_self: Default::default(),
align_content: Default::default(),
justify_content: Default::default(),
position: Default::default(),
margin: Default::default(),
padding: Default::default(),
border: Default::default(),
flex_grow: 0.0,
flex_shrink: 1.0,
flex_basis: Dimension::Auto,
size: Default::default(),
min_size: Default::default(),
max_size: Default::default(),
aspect_ratio: Default::default(),
}
}
}
impl From<FlexLayout> for Style {
fn from(s: FlexLayout) -> Style {
Style {
display: s.display,
position_type: s.position_type,
flex_direction: s.flex_direction,
flex_wrap: s.flex_wrap,
align_items: s.align_items,
align_self: s.align_self,
align_content: s.align_content,
justify_content: s.justify_content,
position: s.position,
margin: s.margin,
padding: s.padding,
border: s.border,
flex_grow: s.flex_grow,
flex_shrink: s.flex_shrink,
flex_basis: s.flex_basis,
size: s.size.into(),
min_size: s.min_size,
max_size: s.max_size,
aspect_ratio: s.aspect_ratio,
}
}
}
#[cfg(feature = "serde")]
pub fn deserialize_simple_enum_as_pascal<'de, T, D>(
deserializer: D,
) -> Result<T, D::Error>
where
T: DeserializeOwned,
D: Deserializer<'de>,
{
let val = String::deserialize(deserializer)?;
let pascal = val.to_case(Case::Pascal);
let deserializer: StringDeserializer<D::Error> = pascal.into_deserializer();
T::deserialize(deserializer).map_err(serde::de::Error::custom)
}
#[derive(Debug, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(default))]
pub struct FlexSize<T> {
pub width: T,
pub height: T,
}
impl Default for FlexSize<f32> {
fn default() -> Self {
FlexSize { width: 0.0, height: 0.0 }
}
}
impl Default for FlexSize<FlexDimension> {
fn default() -> Self {
FlexSize {
width: FlexDimension::default(),
height: FlexDimension::default(),
}
}
}
impl<T, U: std::convert::From<T>> From<FlexSize<T>> for Size<U> {
fn from(s: FlexSize<T>) -> Size<U> {
Size { width: s.width.into(), height: s.height.into() }
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
#[cfg_attr(feature = "serde", serde(untagged))]
pub enum FlexDimension {
Auto,
#[cfg_attr(
feature = "serde",
serde(deserialize_with = "deserialize_percent")
)]
Percent(f32),
#[cfg_attr(
feature = "serde",
serde(deserialize_with = "deserialize_point")
)]
Points(f32),
}
impl Default for FlexDimension {
fn default() -> Self {
FlexDimension::Percent(1.0)
}
}
impl From<FlexDimension> for Dimension {
fn from(s: FlexDimension) -> Dimension {
match s {
FlexDimension::Auto => Dimension::Auto,
FlexDimension::Percent(value) => Dimension::Percent(value),
FlexDimension::Points(value) => Dimension::Points(value),
}
}
}
#[cfg(feature = "serde")]
lazy_static! {
static ref RE_F32: Regex = Regex::new(r"(\d[-+]?[0-9]*\.?[0-9]+)").unwrap();
}
#[cfg(feature = "serde")]
fn deserialize_percent<'de, D>(deserializer: D) -> Result<f32, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
if !s.ends_with('%') {
return Err(serde::de::Error::custom("expecting %"));
}
let cap = RE_F32
.captures(&s)
.and_then(|c| c.get(1))
.ok_or_else(|| serde::de::Error::custom("invalid percent"))?;
f32::from_str(cap.as_str())
.map(|value| (value / 100.0))
.map_err(serde::de::Error::custom)
}
#[cfg(feature = "serde")]
fn deserialize_point<'de, D>(deserializer: D) -> Result<f32, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
if !s.ends_with("pt") {
return Err(serde::de::Error::custom("expecting pt"));
}
let cap = RE_F32
.captures(&s)
.and_then(|c| c.get(1))
.ok_or_else(|| serde::de::Error::custom("invalid point"))?;
f32::from_str(cap.as_str()).map_err(serde::de::Error::custom)
}
#[derive(Default, Debug, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(default))]
pub struct FlexPoint {
pub x: f32,
pub y: f32,
}
#[derive(Debug, Clone)]
pub(crate) struct FlexTaffyNode {
pub taffy_node: TaffyNode,
pub element: Option<FlexElement>,
pub root: Option<FlexRootData>,
pub nodes: Vec<FlexTaffyNode>,
}