Fix deserialize partial values with serde (#366)
* Enable serde to deserialize partial style values
* Implement style helpers for Line<T>
* Cargo fmt
52ae96646aca7b811a12109ca699fb14bf5b50b4 · by Nico Burns · (3 years ago) · parent 08e47a4ae · browse the tree at this commit
Semantic delta · 18
src/style/mod.rs
- signature changed struct Style pub
diff --git a/Cargo.toml b/Cargo.toml
index 492dff138..d87be4af0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -39,6 +39,7 @@ rand = "0.8.5"
rand_chacha = "0.3.1"
yoga = "0.4.0"
ordered-float = "3.4.0"
+serde_json = "1.0.93"
# Enable example and test-specific features
taffy = { path = ".", features = ["random"] }
diff --git a/src/style/mod.rs b/src/style/mod.rs
index b0cf1c238..ee4fa9d21 100644
--- a/src/style/mod.rs
+++ b/src/style/mod.rs
@@ -20,6 +20,8 @@ use crate::geometry::{Rect, Size};
#[cfg(feature = "grid")]
use crate::geometry::Line;
+#[cfg(feature = "serde")]
+use crate::style_helpers;
#[cfg(feature = "grid")]
use crate::sys::GridTrackVec;
@@ -98,14 +100,18 @@ pub struct Style {
/// What should the `position` value of this struct use as a base offset?
pub position: Position,
/// How should the position of this element be tweaked relative to the layout defined?
+ #[cfg_attr(feature = "serde", serde(default = "style_helpers::auto"))]
pub inset: Rect<LengthPercentageAuto>,
// Size properies
/// Sets the initial size of the item
+ #[cfg_attr(feature = "serde", serde(default = "style_helpers::auto"))]
pub size: Size<Dimension>,
/// Controls the minimum size of the item
+ #[cfg_attr(feature = "serde", serde(default = "style_helpers::auto"))]
pub min_size: Size<Dimension>,
/// Controls the maximum size of the item
+ #[cfg_attr(feature = "serde", serde(default = "style_helpers::auto"))]
pub max_size: Size<Dimension>,
/// Sets the preferred aspect ratio for the item
///
@@ -114,10 +120,13 @@ pub struct Style {
// Spacing Properties
/// How large should the margin be on each side?
+ #[cfg_attr(feature = "serde", serde(default = "style_helpers::zero"))]
pub margin: Rect<LengthPercentageAuto>,
/// How large should the padding be on each side?
+ #[cfg_attr(feature = "serde", serde(default = "style_helpers::zero"))]
pub padding: Rect<LengthPercentage>,
/// How large should the border be on each side?
+ #[cfg_attr(feature = "serde", serde(default = "style_helpers::zero"))]
pub border: Rect<LengthPercentage>,
// Alignment properties
@@ -137,6 +146,7 @@ pub struct Style {
/// How should contained within this item be aligned in the main/inline axis
pub justify_content: Option<JustifyContent>,
/// How large should the gaps between items in a grid or flex container be?
+ #[cfg_attr(feature = "serde", serde(default = "style_helpers::zero"))]
pub gap: Size<LengthPercentage>,
// Flexbox properies
diff --git a/src/style_helpers.rs b/src/style_helpers.rs
index 94f262b9f..7e1f55082 100644
--- a/src/style_helpers.rs
+++ b/src/style_helpers.rs
@@ -1,6 +1,6 @@
//! Helper functions which it make it easier to create instances of types in the `style` and `geometry` modules.
use crate::{
- geometry::{MinMax, Point, Rect, Size},
+ geometry::{Line, MinMax, Point, Rect, Size},
style::LengthPercentage,
};
use core::fmt::Debug;
@@ -119,6 +119,16 @@ impl<T: TaffyZero> Point<T> {
zero::<Self>()
}
}
+impl<T: TaffyZero> TaffyZero for Line<T> {
+ const ZERO: Line<T> = Line { start: T::ZERO, end: T::ZERO };
+}
+impl<T: TaffyZero> Line<T> {
+ /// Returns a Line where both the start and end values are the zero value of the contained type
+ /// (e.g. 0.0, Some(0.0), or Dimension::Points(0.0))
+ pub const fn zero() -> Self {
+ zero::<Self>()
+ }
+}
impl<T: TaffyZero> TaffyZero for Size<T> {
const ZERO: Size<T> = Size { width: T::ZERO, height: T::ZERO };
}
@@ -163,6 +173,16 @@ impl<T: TaffyAuto> Point<T> {
auto::<Self>()
}
}
+impl<T: TaffyAuto> TaffyAuto for Line<T> {
+ const AUTO: Line<T> = Line { start: T::AUTO, end: T::AUTO };
+}
+impl<T: TaffyAuto> Line<T> {
+ /// Returns a Line where both the start and end values are the auto value of the contained type
+ /// (e.g. Dimension::Auto or LengthPercentageAuto::Auto)
+ pub const fn auto() -> Self {
+ auto::<Self>()
+ }
+}
impl<T: TaffyAuto> TaffyAuto for Size<T> {
const AUTO: Size<T> = Size { width: T::AUTO, height: T::AUTO };
}
@@ -207,6 +227,16 @@ impl<T: TaffyMinContent> Point<T> {
min_content::<Self>()
}
}
+impl<T: TaffyMinContent> TaffyMinContent for Line<T> {
+ const MIN_CONTENT: Line<T> = Line { start: T::MIN_CONTENT, end: T::MIN_CONTENT };
+}
+impl<T: TaffyMinContent> Line<T> {
+ /// Returns a Line where both the start and end values are the min_content value of the contained type
+ /// (e.g. Dimension::Auto or LengthPercentageAuto::Auto)
+ pub const fn min_content() -> Self {
+ min_content::<Self>()
+ }
+}
impl<T: TaffyMinContent> TaffyMinContent for Size<T> {
const MIN_CONTENT: Size<T> = Size { width: T::MIN_CONTENT, height: T::MIN_CONTENT };
}
@@ -252,6 +282,16 @@ impl<T: TaffyMaxContent> Point<T> {
max_content::<Self>()
}
}
+impl<T: TaffyMaxContent> TaffyMaxContent for Line<T> {
+ const MAX_CONTENT: Line<T> = Line { start: T::MAX_CONTENT, end: T::MAX_CONTENT };
+}
+impl<T: TaffyMaxContent> Line<T> {
+ /// Returns a Line where both the start and end values are the max_content value of the contained type
+ /// (e.g. Dimension::Auto or LengthPercentageAuto::Auto)
+ pub const fn max_content() -> Self {
+ max_content::<Self>()
+ }
+}
impl<T: TaffyMaxContent> TaffyMaxContent for Size<T> {
const MAX_CONTENT: Size<T> = Size { width: T::MAX_CONTENT, height: T::MAX_CONTENT };
}
@@ -296,6 +336,18 @@ impl<T: TaffyFitContent> Point<T> {
fit_content(argument)
}
}
+impl<T: TaffyFitContent> TaffyFitContent for Line<T> {
+ fn fit_content(argument: LengthPercentage) -> Self {
+ Line { start: T::fit_content(argument), end: T::fit_content(argument) }
+ }
+}
+impl<T: TaffyFitContent> Line<T> {
+ /// Returns a Line where both the start and end values are the constant points value of the contained type
+ /// (e.g. 2.1, Some(2.1), or Dimension::Points(2.1))
+ pub fn fit_content(argument: LengthPercentage) -> Self {
+ fit_content(argument)
+ }
+}
impl<T: TaffyFitContent> TaffyFitContent for Size<T> {
fn fit_content(argument: LengthPercentage) -> Self {
Size { width: T::fit_content(argument), height: T::fit_content(argument) }
@@ -358,6 +410,18 @@ impl<T: FromPoints> Point<T> {
points::<Input, Self>(points_value)
}
}
+impl<T: FromPoints> FromPoints for Line<T> {
+ fn from_points<Input: Into<f32> + Copy>(points: Input) -> Self {
+ Line { start: T::from_points(points.into()), end: T::from_points(points.into()) }
+ }
+}
+impl<T: FromPoints> Line<T> {
+ /// Returns a Line where both the start and end values are the constant points value of the contained type
+ /// (e.g. 2.1, Some(2.1), or Dimension::Points(2.1))
+ pub fn points<Input: Into<f32> + Copy>(points_value: Input) -> Self {
+ points::<Input, Self>(points_value)
+ }
+}
impl<T: FromPoints> FromPoints for Size<T> {
fn from_points<Input: Into<f32> + Copy>(points: Input) -> Self {
Size { width: T::from_points(points.into()), height: T::from_points(points.into()) }
@@ -420,6 +484,18 @@ impl<T: FromPercent> Point<T> {
percent::<Input, Self>(percent_value)
}
}
+impl<T: FromPercent> FromPercent for Line<T> {
+ fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
+ Line { start: T::from_percent(percent.into()), end: T::from_percent(percent.into()) }
+ }
+}
+impl<T: FromPercent> Line<T> {
+ /// Returns a Line where both the start and end values are the constant percent value of the contained type
+ /// (e.g. 2.1, Some(2.1), or Dimension::Points(2.1))
+ pub fn percent<Input: Into<f32> + Copy>(percent_value: Input) -> Self {
+ percent::<Input, Self>(percent_value)
+ }
+}
impl<T: FromPercent> FromPercent for Size<T> {
fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
Size { width: T::from_percent(percent.into()), height: T::from_percent(percent.into()) }
diff --git a/tests/serde.rs b/tests/serde.rs
new file mode 100644
index 000000000..8d4d913af
--- /dev/null
+++ b/tests/serde.rs
@@ -0,0 +1,55 @@
+#[cfg(test)]
+#[cfg(feature = "serde")]
+mod serde {
+
+ use serde_json::{self, Value};
+ use taffy::style::Style;
+
+ #[test]
+ fn serde_can_serialize() {
+ let style = Style::DEFAULT;
+ let _ = serde_json::to_string(&style).unwrap();
+ }
+
+ #[test]
+ fn serde_can_deserialize_partial_values() {
+ use serde_json;
+ let json = r###"{
+ "inset": {
+ "left": { "Points": 22 },
+ "right": "Auto"
+ },
+ "size":
+ {
+ "width": { "Percent": 50 }
+ },
+ "min_size":
+ {
+ "height": { "Points": 10 }
+ },
+ "max_size":
+ {
+ "width": "Auto"
+ },
+ "margin":
+ {
+ "right": { "Points": 99.0 },
+ "bottom": { "Points": 99.0 }
+ },
+ "padding":
+ {
+ "left": { "Points": 99.0 }
+ },
+ "border":
+ {
+ "bottom": { "Points": 99.0 }
+ },
+ "gap": {
+ "width": { "Points": 99.0 }
+ },
+ "grid_row": { "start": "Auto" },
+ "grid_column": { "end": "Auto" }
+ }"###;
+ let _: Value = serde_json::from_str(&json).unwrap();
+ }
+}