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