1use super::*;
2use indexmap::IndexMap;
3use paste::paste;
4use std::{borrow::Cow, fmt};
5
6#[derive(Default, PartialEq, Debug, Clone)]
31pub struct Style {
32 values: IndexMap<Cow<'static, str>, String>,
33}
34
35impl fmt::Display for Style {
36 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
37 self.values
38 .iter()
39 .map(|(k, v)| format!("{}: {};", k, v))
40 .collect::<String>()
41 .fmt(f)
42 }
43}
44
45macro_rules! setter_functions {
46 ( @more_fns $prop_ty:ident and ) => {
47 paste! {
48 pub fn [<and_ $prop_ty:snake>](mut self, val: impl FnOnce($prop_ty) -> $prop_ty) -> Self
50 where
51 $prop_ty: Default + StyleUpdater,
52 {
53 self = val($prop_ty::default()).update_style(self);
54 self
55 }
56
57 }
58 };
59 ( $( $prop_ty:ident $( +$ext:ident )? $(,)? )+ ) => {
60 $(
61 paste! {
62 pub fn [<$prop_ty:snake>](mut self, val: impl Into<$prop_ty>) -> Self
64 where
65 $prop_ty: StyleUpdater,
66 {
67 self = val.into().update_style(self);
68 self
69 }
70
71 pub fn [<try_ $prop_ty:snake>](self, val: Option<impl Into<$prop_ty>>) -> Self {
73 if let Some(val) = val {
74 self.[<$prop_ty:snake>](val)
75 } else {
76 self
77 }
78 }
79 }
80 $( setter_functions!(@more_fns $prop_ty $ext); )?
81 )+
82 }
83}
84
85impl Style {
86 pub fn new() -> Self {
87 Self::default()
88 }
89
90 pub fn to_css(&self) -> Option<String> {
92 self.values
93 .iter()
94 .fold(Option::None, |mut css, (key, value)| {
95 *css.get_or_insert(String::default()) += &format!("{}: {};", key, value);
96 css
97 })
98 }
99
100 pub fn insert(mut self, key: impl Into<Cow<'static, str>>, value: impl ToString) -> Self {
102 self.values.insert(key.into(), value.to_string());
103 self
104 }
105
106 pub fn try_insert(
108 self,
109 key: impl Into<Cow<'static, str>>,
110 value: Option<impl ToString>,
111 ) -> Self {
112 if let Some(val) = value {
113 self.insert(key, val)
114 } else {
115 self
116 }
117 }
118
119 pub fn merge(mut self, other: impl StyleUpdater) -> Self {
121 self = other.update_style(self);
122 self
123 }
124
125 pub fn try_merge(self, other: Option<impl StyleUpdater>) -> Self {
127 if let Some(other) = other {
128 self.merge(other)
129 } else {
130 self
131 }
132 }
133
134 setter_functions! {
135 Opacity,
136 Gap,
137 AlignContent,
138 AlignItems,
139 JustifyContent,
140 JustifySelf,
141 AlignSelf,
142 FlexWrap,
143 FlexBasis,
144 FlexDirection,
145 FlexOrder,
146 FlexGrow,
147 FlexShrink,
148 Display,
149 Visibility,
150 Cursor,
151 Background +and,
152 Border +and,
153 Margin +and,
154 Padding +and,
155 Size +and,
156 Transition +and,
157 BoxShadow +and,
158 Position +and,
159 Text +and,
160 Font +and,
161 }
162}
163
164pub trait StyleUpdater {
165 fn update_style(self, style: Style) -> Style;
166}
167
168impl StyleUpdater for Style {
169 fn update_style(self, mut style: Style) -> Style {
170 style.values.extend(self.values);
171 style
172 }
173}
174
175pub fn style() -> Style {
176 Style::default()
177}