1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3
4use super::video::AnimationEffect;
5
6#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
7#[serde(rename_all = "snake_case")]
8#[derive(Default)]
9pub enum CardDirection {
10 #[default]
11 Column,
12 Row,
13 ColumnReverse,
14 RowReverse,
15}
16
17#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
18#[serde(rename_all = "snake_case")]
19#[derive(Default)]
20pub enum CardAlign {
21 #[serde(alias = "flex-start", alias = "flex_start")]
22 #[default]
23 Start,
24 Center,
25 #[serde(alias = "flex-end", alias = "flex_end")]
26 End,
27 Stretch,
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
31#[serde(rename_all = "snake_case")]
32#[derive(Default)]
33pub enum CardJustify {
34 #[serde(alias = "flex-start", alias = "flex_start")]
35 #[default]
36 Start,
37 Center,
38 #[serde(alias = "flex-end", alias = "flex_end")]
39 End,
40 #[serde(alias = "space-between")]
41 SpaceBetween,
42 #[serde(alias = "space-around")]
43 SpaceAround,
44 #[serde(alias = "space-evenly")]
45 SpaceEvenly,
46}
47
48#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
51pub struct TimelineStep {
52 pub at: f64,
54 #[serde(default, deserialize_with = "deserialize_animation_effects")]
56 pub animation: Vec<AnimationEffect>,
57 #[serde(default)]
61 pub style: Option<Box<crate::css::CssStyle>>,
62}
63
64#[derive(Debug, Clone, JsonSchema, Default)]
66pub enum FontWeight {
67 #[default]
68 Normal,
69 Bold,
70 Weight(u16),
71}
72
73#[allow(dead_code)]
74impl FontWeight {
75 pub fn to_skia_weight(&self) -> i32 {
76 match self {
77 FontWeight::Normal => 400,
78 FontWeight::Bold => 700,
79 FontWeight::Weight(w) => *w as i32,
80 }
81 }
82}
83
84impl Serialize for FontWeight {
85 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
86 match self {
87 FontWeight::Normal => serializer.serialize_str("normal"),
88 FontWeight::Bold => serializer.serialize_str("bold"),
89 FontWeight::Weight(w) => serializer.serialize_u16(*w),
90 }
91 }
92}
93
94impl<'de> Deserialize<'de> for FontWeight {
95 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
96 struct FontWeightVisitor;
97 impl<'de> serde::de::Visitor<'de> for FontWeightVisitor {
98 type Value = FontWeight;
99 fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
100 write!(f, "\"normal\", \"bold\", or a number 100-900")
101 }
102 fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<FontWeight, E> {
103 match v {
104 "normal" => Ok(FontWeight::Normal),
105 "bold" => Ok(FontWeight::Bold),
106 _ => Err(E::custom(format!("unknown font weight: {}", v))),
107 }
108 }
109 fn visit_u64<E: serde::de::Error>(self, v: u64) -> Result<FontWeight, E> {
110 Ok(FontWeight::Weight(v as u16))
111 }
112 fn visit_i64<E: serde::de::Error>(self, v: i64) -> Result<FontWeight, E> {
113 Ok(FontWeight::Weight(v as u16))
114 }
115 fn visit_f64<E: serde::de::Error>(self, v: f64) -> Result<FontWeight, E> {
116 Ok(FontWeight::Weight(v as u16))
117 }
118 }
119 deserializer.deserialize_any(FontWeightVisitor)
120 }
121}
122
123#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
124#[serde(rename_all = "snake_case")]
125#[derive(Default)]
126pub enum FontStyleType {
127 #[default]
128 Normal,
129 Italic,
130 Oblique,
131}
132
133#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
134#[serde(rename_all = "snake_case")]
135#[derive(Default)]
136pub enum TextAlign {
137 #[default]
138 Left,
139 Center,
140 Right,
141}
142
143#[derive(Debug, Serialize, Deserialize, JsonSchema)]
144#[serde(rename_all = "snake_case")]
145#[derive(Default)]
146pub enum VerticalAlign {
147 Top,
148 #[default]
149 Middle,
150 Bottom,
151}
152
153#[derive(Debug, Clone, JsonSchema)]
155pub enum SizeDimension {
156 Fixed(f32),
157 Percent(f32),
158 Auto,
159}
160
161impl Serialize for SizeDimension {
162 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
163 match self {
164 SizeDimension::Fixed(v) => serializer.serialize_f32(*v),
165 SizeDimension::Percent(p) => serializer.serialize_str(&format!("{}%", p)),
166 SizeDimension::Auto => serializer.serialize_str("auto"),
167 }
168 }
169}
170
171impl<'de> Deserialize<'de> for SizeDimension {
172 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
173 struct SizeDimensionVisitor;
174 impl<'de> serde::de::Visitor<'de> for SizeDimensionVisitor {
175 type Value = SizeDimension;
176 fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
177 write!(f, "a number, \"auto\", or \"50%\"")
178 }
179 fn visit_f64<E: serde::de::Error>(self, v: f64) -> Result<SizeDimension, E> {
180 Ok(SizeDimension::Fixed(v as f32))
181 }
182 fn visit_i64<E: serde::de::Error>(self, v: i64) -> Result<SizeDimension, E> {
183 Ok(SizeDimension::Fixed(v as f32))
184 }
185 fn visit_u64<E: serde::de::Error>(self, v: u64) -> Result<SizeDimension, E> {
186 Ok(SizeDimension::Fixed(v as f32))
187 }
188 fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<SizeDimension, E> {
189 if v == "auto" {
190 Ok(SizeDimension::Auto)
191 } else if let Some(pct) = v.strip_suffix('%') {
192 pct.trim()
193 .parse::<f32>()
194 .map(SizeDimension::Percent)
195 .map_err(|_| E::custom(format!("invalid percentage: {}", v)))
196 } else {
197 Err(E::custom(format!(
198 "expected number, \"auto\", or \"N%\", got: {}",
199 v
200 )))
201 }
202 }
203 }
204 deserializer.deserialize_any(SizeDimensionVisitor)
205 }
206}
207
208pub fn deserialize_animation_effects<'de, D>(
209 deserializer: D,
210) -> Result<Vec<AnimationEffect>, D::Error>
211where
212 D: serde::Deserializer<'de>,
213{
214 use serde::de;
215
216 struct OneOrMany;
217
218 impl<'de> de::Visitor<'de> for OneOrMany {
219 type Value = Vec<AnimationEffect>;
220
221 fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
222 f.write_str("a single animation effect or an array of animation effects")
223 }
224
225 fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
226 where
227 A: de::SeqAccess<'de>,
228 {
229 Vec::deserialize(de::value::SeqAccessDeserializer::new(seq))
230 }
231
232 fn visit_map<M>(self, map: M) -> Result<Self::Value, M::Error>
233 where
234 M: de::MapAccess<'de>,
235 {
236 let effect = AnimationEffect::deserialize(de::value::MapAccessDeserializer::new(map))?;
237 Ok(vec![effect])
238 }
239 }
240
241 deserializer.deserialize_any(OneOrMany)
242}