1use chrono::{DateTime, NaiveDate, TimeZone};
2use serde::Serialize;
3use ts_rs::TS;
4
5use crate::{
6 types::{Markdown, MultiLine, Slug, Toggle},
7 EditorType,
8};
9
10#[derive(Serialize, TS)]
11#[ts(export)]
12pub struct EditorField {
13 pub name: &'static str,
14 pub title: &'static str,
15 pub placeholder: Option<&'static str>,
16 pub validator: Option<&'static str>,
17 pub required: bool,
18 pub field_type: EditorType,
19}
20
21pub trait ToEditorField {
23 fn to_editor_field(
24 default: Option<impl Into<Self>>,
25 name: &'static str,
26 title: &'static str,
27 placeholder: Option<&'static str>,
28 validator: Option<&'static str>,
29 component_key: Option<&'static str>,
30 ) -> EditorField
31 where
32 Self: std::marker::Sized;
33}
34
35impl ToEditorField for () {
36 fn to_editor_field(
37 _default: Option<impl Into<Self>>,
38 name: &'static str,
39 title: &'static str,
40 placeholder: Option<&'static str>,
41 validator: Option<&'static str>,
42 component_key: Option<&'static str>,
43 ) -> EditorField
44 where
45 Self: std::marker::Sized,
46 {
47 EditorField {
48 name,
49 title,
50 placeholder,
51 required: true,
52 validator,
53 field_type: crate::EditorType::Null {
54 component_key: component_key.map(Into::into),
55 },
56 }
57 }
58}
59
60impl ToEditorField for bool {
61 fn to_editor_field(
62 default: Option<impl Into<bool>>,
63 name: &'static str,
64 title: &'static str,
65 placeholder: Option<&'static str>,
66 validator: Option<&'static str>,
67 component_key: Option<&'static str>,
68 ) -> EditorField
69 where
70 Self: std::marker::Sized,
71 {
72 EditorField {
73 name,
74 title,
75 placeholder,
76 required: true,
77 validator,
78 field_type: crate::EditorType::Bool {
79 default: default.map(Into::into),
80 component_key: component_key.map(Into::into),
81 },
82 }
83 }
84}
85
86impl ToEditorField for i32 {
87 fn to_editor_field(
88 default: Option<impl Into<Self>>,
89 name: &'static str,
90 title: &'static str,
91 placeholder: Option<&'static str>,
92 validator: Option<&'static str>,
93 component_key: Option<&'static str>,
94 ) -> EditorField
95 where
96 Self: std::marker::Sized,
97 {
98 EditorField {
99 name,
100 title,
101 placeholder,
102 required: true,
103 validator,
104 field_type: crate::EditorType::Integer {
105 default: default.map(Into::into),
106 component_key: component_key.map(Into::into),
107 },
108 }
109 }
110}
111
112impl ToEditorField for f32 {
113 fn to_editor_field(
114 default: Option<impl Into<Self>>,
115 name: &'static str,
116 title: &'static str,
117 placeholder: Option<&'static str>,
118 validator: Option<&'static str>,
119 component_key: Option<&'static str>,
120 ) -> EditorField
121 where
122 Self: std::marker::Sized,
123 {
124 EditorField {
125 name,
126 title,
127 placeholder,
128 required: true,
129 validator,
130 field_type: crate::EditorType::Float {
131 default: default.map(Into::into),
132 component_key: component_key.map(Into::into),
133 },
134 }
135 }
136}
137
138impl ToEditorField for String {
139 fn to_editor_field(
140 default: Option<impl Into<Self>>,
141 name: &'static str,
142 title: &'static str,
143 placeholder: Option<&'static str>,
144 validator: Option<&'static str>,
145 component_key: Option<&'static str>,
146 ) -> EditorField
147 where
148 Self: std::marker::Sized,
149 {
150 EditorField {
151 name,
152 title,
153 placeholder,
154 required: true,
155 validator,
156 field_type: crate::EditorType::SingleLine {
157 default: default.map(Into::into),
158 component_key: component_key.map(Into::into),
159 },
160 }
161 }
162}
163
164impl ToEditorField for Slug {
165 fn to_editor_field(
166 default: Option<impl Into<Self>>,
167 name: &'static str,
168 title: &'static str,
169 placeholder: Option<&'static str>,
170 validator: Option<&'static str>,
171 component_key: Option<&'static str>,
172 ) -> EditorField
173 where
174 Self: std::marker::Sized,
175 {
176 EditorField {
177 name,
178 title,
179 placeholder,
180 required: true,
181 validator,
182 field_type: crate::EditorType::SingleLine {
183 default: default.map(Into::into).map(|v| v.0),
184 component_key: component_key.map(Into::into),
185 },
186 }
187 }
188}
189
190impl ToEditorField for MultiLine {
191 fn to_editor_field(
192 default: Option<impl Into<Self>>,
193 name: &'static str,
194 title: &'static str,
195 placeholder: Option<&'static str>,
196 validator: Option<&'static str>,
197 component_key: Option<&'static str>,
198 ) -> EditorField
199 where
200 Self: std::marker::Sized,
201 {
202 EditorField {
203 name,
204 title,
205 placeholder,
206 required: true,
207 validator,
208 field_type: crate::EditorType::MultiLine {
209 default: default.map(Into::into).map(|v| v.0),
210 component_key: component_key.map(Into::into),
211 },
212 }
213 }
214}
215
216impl ToEditorField for Markdown {
217 fn to_editor_field(
218 default: Option<impl Into<Self>>,
219 name: &'static str,
220 title: &'static str,
221 placeholder: Option<&'static str>,
222 validator: Option<&'static str>,
223 component_key: Option<&'static str>,
224 ) -> EditorField
225 where
226 Self: std::marker::Sized,
227 {
228 EditorField {
229 name,
230 title,
231 placeholder,
232 required: true,
233 validator,
234 field_type: crate::EditorType::Markdown {
235 default: default.map(Into::into).map(|v| v.0),
236 component_key: component_key.map(Into::into),
237 },
238 }
239 }
240}
241
242impl<Z: TimeZone> ToEditorField for DateTime<Z> {
243 fn to_editor_field(
244 default: Option<impl Into<DateTime<Z>>>,
245 name: &'static str,
246 title: &'static str,
247 placeholder: Option<&'static str>,
248 validator: Option<&'static str>,
249 component_key: Option<&'static str>,
250 ) -> EditorField
251 where
252 Self: std::marker::Sized,
253 {
254 EditorField {
255 name,
256 title,
257 placeholder,
258 validator,
259 required: true,
260 field_type: EditorType::DateTime {
261 default: default.map(Into::into).as_ref().map(DateTime::to_utc),
262 component_key: component_key.map(Into::into),
263 },
264 }
265 }
266}
267
268impl ToEditorField for NaiveDate {
269 fn to_editor_field(
270 default: Option<impl Into<NaiveDate>>,
271 name: &'static str,
272 title: &'static str,
273 placeholder: Option<&'static str>,
274 validator: Option<&'static str>,
275 component_key: Option<&'static str>,
276 ) -> EditorField
277 where
278 Self: std::marker::Sized,
279 {
280 EditorField {
281 name,
282 title,
283 placeholder,
284 validator,
285 required: true,
286 field_type: EditorType::Date {
287 default: default.map(Into::into).as_ref().map(|d| {
288 d.and_hms_opt(0, 0, 0)
289 .expect("should always be valid")
290 .and_utc()
291 }),
292 component_key: component_key.map(Into::into),
293 },
294 }
295 }
296}
297
298impl<T: ToEditorField + Serialize> ToEditorField for Toggle<T> {
299 fn to_editor_field(
300 default: Option<impl Into<Self>>,
301 name: &'static str,
302 title: &'static str,
303 placeholder: Option<&'static str>,
304 validator: Option<&'static str>,
305 component_key: Option<&'static str>,
306 ) -> EditorField
307 where
308 Self: std::marker::Sized,
309 {
310 let dummy_field = T::to_editor_field(
311 None::<T>,
312 name,
313 title,
314 placeholder,
315 validator,
316 component_key,
317 );
318 let field_type = dummy_field.field_type;
319
320 EditorField {
321 name,
322 title,
323 placeholder,
324 validator,
325 required: true,
326 field_type: EditorType::Toggle {
327 component_key: component_key.map(Into::into),
328 default: default
329 .map(|v| serde_json::to_value(v.into()).expect("this should never fail")),
330 value: Box::new(field_type),
331 },
332 }
333 }
334}
335
336impl<T> ToEditorField for Option<T>
337where
338 T: ToEditorField + Serialize,
339{
340 fn to_editor_field(
341 default: Option<impl Into<Option<T>>>,
342 name: &'static str,
343 title: &'static str,
344 placeholder: Option<&'static str>,
345 validator: Option<&'static str>,
346 component_key: Option<&'static str>,
347 ) -> EditorField
348 where
349 Self: std::marker::Sized,
350 {
351 let test = default.and_then(Into::into);
352
353 let mut field =
354 T::to_editor_field(test, name, title, placeholder, validator, component_key);
355 field.required = false;
356
357 field
358 }
359}
360
361impl<T> ToEditorField for Vec<T>
362where
363 T: ToEditorField + Serialize,
364{
365 fn to_editor_field(
366 default: Option<impl Into<Vec<T>>>,
367 name: &'static str,
368 title: &'static str,
369 placeholder: Option<&'static str>,
370 validator: Option<&'static str>,
371 component_key: Option<&'static str>,
372 ) -> EditorField
373 where
374 Self: std::marker::Sized,
375 {
376 let dummy_field = T::to_editor_field(
377 None::<T>,
378 name,
379 title,
380 placeholder,
381 validator,
382 component_key,
383 );
384 let field_type = dummy_field.field_type;
385
386 EditorField {
387 name,
388 title,
389 placeholder,
390 required: true,
391 validator,
392 field_type: EditorType::Array {
393 default: default
394 .map(|v| serde_json::to_value(v.into()).expect("this should never fail")),
395 component_key: component_key.map(Into::into),
396 of: Box::new(field_type),
397 },
398 }
399 }
400}
401
402#[cfg(feature = "url")]
403impl ToEditorField for url::Url {
404 fn to_editor_field(
405 default: Option<impl Into<Self>>,
406 name: &'static str,
407 title: &'static str,
408 placeholder: Option<&'static str>,
409 validator: Option<&'static str>,
410 component_key: Option<&'static str>,
411 ) -> EditorField
412 where
413 Self: std::marker::Sized,
414 {
415 EditorField {
416 name,
417 title,
418 placeholder,
419 required: true,
420 validator,
421 field_type: crate::EditorType::SingleLine {
422 default: default.map(Into::into).map(Into::into),
423 component_key: component_key.map(Into::into).or_else(|| Some("url".into())),
424 },
425 }
426 }
427}
428
429#[cfg(feature = "rgb")]
430impl ToEditorField for rgb::RGB8 {
431 fn to_editor_field(
432 default: Option<impl Into<Self>>,
433 name: &'static str,
434 title: &'static str,
435 placeholder: Option<&'static str>,
436 validator: Option<&'static str>,
437 component_key: Option<&'static str>,
438 ) -> EditorField
439 where
440 Self: std::marker::Sized,
441 {
442 let default = default.map(Into::into);
443
444 super::EditorField {
445 name,
446 title,
447 placeholder,
448 validator,
449 required: true,
450 field_type: crate::EditorType::Struct {
451 component_key: component_key.map(Into::into).or(Some("color-input".into())),
452 default: default
453 .map(serde_json::to_value)
454 .map(|v| v.expect("rgb values should always serialize correctly")),
455 fields: vec![
456 i32::to_editor_field(
457 default.map(|c| i32::from(c.r)),
458 "r",
459 "",
460 None,
461 None,
462 None,
463 ),
464 i32::to_editor_field(
465 default.map(|c| i32::from(c.g)),
466 "g",
467 "",
468 None,
469 None,
470 None,
471 ),
472 i32::to_editor_field(
473 default.map(|c| i32::from(c.b)),
474 "b",
475 "",
476 None,
477 None,
478 None,
479 ),
480 ],
481 },
482 }
483 }
484}
485
486#[cfg(feature = "rgb")]
487impl ToEditorField for rgb::RGBA8 {
488 fn to_editor_field(
489 default: Option<impl Into<Self>>,
490 name: &'static str,
491 title: &'static str,
492 placeholder: Option<&'static str>,
493 validator: Option<&'static str>,
494 component_key: Option<&'static str>,
495 ) -> EditorField
496 where
497 Self: std::marker::Sized,
498 {
499 let default = default.map(Into::into);
500
501 super::EditorField {
502 name,
503 title,
504 placeholder,
505 validator,
506 required: true,
507 field_type: crate::EditorType::Struct {
508 component_key: component_key.map(Into::into).or(Some("color-input".into())),
509 default: default
510 .map(serde_json::to_value)
511 .map(|v| v.expect("rgb values should always serialize correctly")),
512 fields: vec![
513 i32::to_editor_field(
514 default.map(|c| i32::from(c.r)),
515 "r",
516 "",
517 None,
518 None,
519 None,
520 ),
521 i32::to_editor_field(
522 default.map(|c| i32::from(c.g)),
523 "g",
524 "",
525 None,
526 None,
527 None,
528 ),
529 i32::to_editor_field(
530 default.map(|c| i32::from(c.b)),
531 "b",
532 "",
533 None,
534 None,
535 None,
536 ),
537 i32::to_editor_field(
538 default.map(|c| i32::from(c.a)),
539 "a",
540 "",
541 None,
542 None,
543 None,
544 ),
545 ],
546 },
547 }
548 }
549}