1#[allow(unused_imports)]
9use alloc::collections::BTreeMap;
10
11#[allow(unused_imports)]
12use core::marker::PhantomData;
13use jacquard_common::CowStr;
14
15#[allow(unused_imports)]
16use jacquard_common::deps::codegen::unicode_segmentation::UnicodeSegmentation;
17use jacquard_common::types::blob::BlobRef;
18use jacquard_common::types::string::{Cid, Language, UriValue};
19use jacquard_derive::{IntoStatic, lexicon};
20use jacquard_lexicon::lexicon::LexiconDoc;
21use jacquard_lexicon::schema::LexiconSchema;
22
23#[allow(unused_imports)]
24use jacquard_lexicon::validation::{ConstraintError, ValidationPath};
25use serde::{Serialize, Deserialize};
26use crate::app_bsky::embed::AspectRatio;
27use crate::app_bsky::embed::video;
28
29#[lexicon]
30#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
31#[serde(rename_all = "camelCase")]
32pub struct Caption<'a> {
33 #[serde(borrow)]
34 pub file: BlobRef<'a>,
35 pub lang: Language,
36}
37
38
39#[lexicon]
40#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
41#[serde(rename_all = "camelCase")]
42pub struct Video<'a> {
43 #[serde(skip_serializing_if = "Option::is_none")]
45 #[serde(borrow)]
46 pub alt: Option<CowStr<'a>>,
47 #[serde(skip_serializing_if = "Option::is_none")]
48 #[serde(borrow)]
49 pub aspect_ratio: Option<AspectRatio<'a>>,
50 #[serde(skip_serializing_if = "Option::is_none")]
51 #[serde(borrow)]
52 pub captions: Option<Vec<video::Caption<'a>>>,
53 #[serde(skip_serializing_if = "Option::is_none")]
55 #[serde(borrow)]
56 pub presentation: Option<VideoPresentation<'a>>,
57 #[serde(borrow)]
59 pub video: BlobRef<'a>,
60}
61
62#[derive(Debug, Clone, PartialEq, Eq, Hash)]
65pub enum VideoPresentation<'a> {
66 Default,
67 Gif,
68 Other(CowStr<'a>),
69}
70
71impl<'a> VideoPresentation<'a> {
72 pub fn as_str(&self) -> &str {
73 match self {
74 Self::Default => "default",
75 Self::Gif => "gif",
76 Self::Other(s) => s.as_ref(),
77 }
78 }
79}
80
81impl<'a> From<&'a str> for VideoPresentation<'a> {
82 fn from(s: &'a str) -> Self {
83 match s {
84 "default" => Self::Default,
85 "gif" => Self::Gif,
86 _ => Self::Other(CowStr::from(s)),
87 }
88 }
89}
90
91impl<'a> From<String> for VideoPresentation<'a> {
92 fn from(s: String) -> Self {
93 match s.as_str() {
94 "default" => Self::Default,
95 "gif" => Self::Gif,
96 _ => Self::Other(CowStr::from(s)),
97 }
98 }
99}
100
101impl<'a> core::fmt::Display for VideoPresentation<'a> {
102 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
103 write!(f, "{}", self.as_str())
104 }
105}
106
107impl<'a> AsRef<str> for VideoPresentation<'a> {
108 fn as_ref(&self) -> &str {
109 self.as_str()
110 }
111}
112
113impl<'a> serde::Serialize for VideoPresentation<'a> {
114 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
115 where
116 S: serde::Serializer,
117 {
118 serializer.serialize_str(self.as_str())
119 }
120}
121
122impl<'de, 'a> serde::Deserialize<'de> for VideoPresentation<'a>
123where
124 'de: 'a,
125{
126 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
127 where
128 D: serde::Deserializer<'de>,
129 {
130 let s = <&'de str>::deserialize(deserializer)?;
131 Ok(Self::from(s))
132 }
133}
134
135impl<'a> Default for VideoPresentation<'a> {
136 fn default() -> Self {
137 Self::Other(Default::default())
138 }
139}
140
141impl jacquard_common::IntoStatic for VideoPresentation<'_> {
142 type Output = VideoPresentation<'static>;
143 fn into_static(self) -> Self::Output {
144 match self {
145 VideoPresentation::Default => VideoPresentation::Default,
146 VideoPresentation::Gif => VideoPresentation::Gif,
147 VideoPresentation::Other(v) => VideoPresentation::Other(v.into_static()),
148 }
149 }
150}
151
152
153#[lexicon]
154#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
155#[serde(rename_all = "camelCase")]
156pub struct View<'a> {
157 #[serde(skip_serializing_if = "Option::is_none")]
158 #[serde(borrow)]
159 pub alt: Option<CowStr<'a>>,
160 #[serde(skip_serializing_if = "Option::is_none")]
161 #[serde(borrow)]
162 pub aspect_ratio: Option<AspectRatio<'a>>,
163 #[serde(borrow)]
164 pub cid: Cid<'a>,
165 #[serde(borrow)]
166 pub playlist: UriValue<'a>,
167 #[serde(skip_serializing_if = "Option::is_none")]
169 #[serde(borrow)]
170 pub presentation: Option<ViewPresentation<'a>>,
171 #[serde(skip_serializing_if = "Option::is_none")]
172 #[serde(borrow)]
173 pub thumbnail: Option<UriValue<'a>>,
174}
175
176#[derive(Debug, Clone, PartialEq, Eq, Hash)]
179pub enum ViewPresentation<'a> {
180 Default,
181 Gif,
182 Other(CowStr<'a>),
183}
184
185impl<'a> ViewPresentation<'a> {
186 pub fn as_str(&self) -> &str {
187 match self {
188 Self::Default => "default",
189 Self::Gif => "gif",
190 Self::Other(s) => s.as_ref(),
191 }
192 }
193}
194
195impl<'a> From<&'a str> for ViewPresentation<'a> {
196 fn from(s: &'a str) -> Self {
197 match s {
198 "default" => Self::Default,
199 "gif" => Self::Gif,
200 _ => Self::Other(CowStr::from(s)),
201 }
202 }
203}
204
205impl<'a> From<String> for ViewPresentation<'a> {
206 fn from(s: String) -> Self {
207 match s.as_str() {
208 "default" => Self::Default,
209 "gif" => Self::Gif,
210 _ => Self::Other(CowStr::from(s)),
211 }
212 }
213}
214
215impl<'a> core::fmt::Display for ViewPresentation<'a> {
216 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
217 write!(f, "{}", self.as_str())
218 }
219}
220
221impl<'a> AsRef<str> for ViewPresentation<'a> {
222 fn as_ref(&self) -> &str {
223 self.as_str()
224 }
225}
226
227impl<'a> serde::Serialize for ViewPresentation<'a> {
228 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
229 where
230 S: serde::Serializer,
231 {
232 serializer.serialize_str(self.as_str())
233 }
234}
235
236impl<'de, 'a> serde::Deserialize<'de> for ViewPresentation<'a>
237where
238 'de: 'a,
239{
240 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
241 where
242 D: serde::Deserializer<'de>,
243 {
244 let s = <&'de str>::deserialize(deserializer)?;
245 Ok(Self::from(s))
246 }
247}
248
249impl<'a> Default for ViewPresentation<'a> {
250 fn default() -> Self {
251 Self::Other(Default::default())
252 }
253}
254
255impl jacquard_common::IntoStatic for ViewPresentation<'_> {
256 type Output = ViewPresentation<'static>;
257 fn into_static(self) -> Self::Output {
258 match self {
259 ViewPresentation::Default => ViewPresentation::Default,
260 ViewPresentation::Gif => ViewPresentation::Gif,
261 ViewPresentation::Other(v) => ViewPresentation::Other(v.into_static()),
262 }
263 }
264}
265
266impl<'a> LexiconSchema for Caption<'a> {
267 fn nsid() -> &'static str {
268 "app.bsky.embed.video"
269 }
270 fn def_name() -> &'static str {
271 "caption"
272 }
273 fn lexicon_doc() -> LexiconDoc<'static> {
274 lexicon_doc_app_bsky_embed_video()
275 }
276 fn validate(&self) -> Result<(), ConstraintError> {
277 {
278 let value = &self.file;
279 {
280 let size = value.blob().size;
281 if size > 20000usize {
282 return Err(ConstraintError::BlobTooLarge {
283 path: ValidationPath::from_field("file"),
284 max: 20000usize,
285 actual: size,
286 });
287 }
288 }
289 }
290 {
291 let value = &self.file;
292 {
293 let mime = value.blob().mime_type.as_str();
294 let accepted: &[&str] = &["text/vtt"];
295 let matched = accepted
296 .iter()
297 .any(|pattern| {
298 if *pattern == "*/*" {
299 true
300 } else if pattern.ends_with("/*") {
301 let prefix = &pattern[..pattern.len() - 2];
302 mime.starts_with(prefix)
303 && mime.as_bytes().get(prefix.len()) == Some(&b'/')
304 } else {
305 mime == *pattern
306 }
307 });
308 if !matched {
309 return Err(ConstraintError::BlobMimeTypeNotAccepted {
310 path: ValidationPath::from_field("file"),
311 accepted: vec!["text/vtt".to_string()],
312 actual: mime.to_string(),
313 });
314 }
315 }
316 }
317 Ok(())
318 }
319}
320
321impl<'a> LexiconSchema for Video<'a> {
322 fn nsid() -> &'static str {
323 "app.bsky.embed.video"
324 }
325 fn def_name() -> &'static str {
326 "main"
327 }
328 fn lexicon_doc() -> LexiconDoc<'static> {
329 lexicon_doc_app_bsky_embed_video()
330 }
331 fn validate(&self) -> Result<(), ConstraintError> {
332 if let Some(ref value) = self.alt {
333 #[allow(unused_comparisons)]
334 if <str>::len(value.as_ref()) > 10000usize {
335 return Err(ConstraintError::MaxLength {
336 path: ValidationPath::from_field("alt"),
337 max: 10000usize,
338 actual: <str>::len(value.as_ref()),
339 });
340 }
341 }
342 if let Some(ref value) = self.alt {
343 {
344 let count = UnicodeSegmentation::graphemes(value.as_ref(), true).count();
345 if count > 1000usize {
346 return Err(ConstraintError::MaxGraphemes {
347 path: ValidationPath::from_field("alt"),
348 max: 1000usize,
349 actual: count,
350 });
351 }
352 }
353 }
354 if let Some(ref value) = self.captions {
355 #[allow(unused_comparisons)]
356 if value.len() > 20usize {
357 return Err(ConstraintError::MaxLength {
358 path: ValidationPath::from_field("captions"),
359 max: 20usize,
360 actual: value.len(),
361 });
362 }
363 }
364 {
365 let value = &self.video;
366 {
367 let size = value.blob().size;
368 if size > 100000000usize {
369 return Err(ConstraintError::BlobTooLarge {
370 path: ValidationPath::from_field("video"),
371 max: 100000000usize,
372 actual: size,
373 });
374 }
375 }
376 }
377 {
378 let value = &self.video;
379 {
380 let mime = value.blob().mime_type.as_str();
381 let accepted: &[&str] = &["video/mp4"];
382 let matched = accepted
383 .iter()
384 .any(|pattern| {
385 if *pattern == "*/*" {
386 true
387 } else if pattern.ends_with("/*") {
388 let prefix = &pattern[..pattern.len() - 2];
389 mime.starts_with(prefix)
390 && mime.as_bytes().get(prefix.len()) == Some(&b'/')
391 } else {
392 mime == *pattern
393 }
394 });
395 if !matched {
396 return Err(ConstraintError::BlobMimeTypeNotAccepted {
397 path: ValidationPath::from_field("video"),
398 accepted: vec!["video/mp4".to_string()],
399 actual: mime.to_string(),
400 });
401 }
402 }
403 }
404 Ok(())
405 }
406}
407
408impl<'a> LexiconSchema for View<'a> {
409 fn nsid() -> &'static str {
410 "app.bsky.embed.video"
411 }
412 fn def_name() -> &'static str {
413 "view"
414 }
415 fn lexicon_doc() -> LexiconDoc<'static> {
416 lexicon_doc_app_bsky_embed_video()
417 }
418 fn validate(&self) -> Result<(), ConstraintError> {
419 if let Some(ref value) = self.alt {
420 #[allow(unused_comparisons)]
421 if <str>::len(value.as_ref()) > 10000usize {
422 return Err(ConstraintError::MaxLength {
423 path: ValidationPath::from_field("alt"),
424 max: 10000usize,
425 actual: <str>::len(value.as_ref()),
426 });
427 }
428 }
429 if let Some(ref value) = self.alt {
430 {
431 let count = UnicodeSegmentation::graphemes(value.as_ref(), true).count();
432 if count > 1000usize {
433 return Err(ConstraintError::MaxGraphemes {
434 path: ValidationPath::from_field("alt"),
435 max: 1000usize,
436 actual: count,
437 });
438 }
439 }
440 }
441 Ok(())
442 }
443}
444
445pub mod caption_state {
446
447 pub use crate::builder_types::{Set, Unset, IsSet, IsUnset};
448 #[allow(unused)]
449 use ::core::marker::PhantomData;
450 mod sealed {
451 pub trait Sealed {}
452 }
453 pub trait State: sealed::Sealed {
455 type Lang;
456 type File;
457 }
458 pub struct Empty(());
460 impl sealed::Sealed for Empty {}
461 impl State for Empty {
462 type Lang = Unset;
463 type File = Unset;
464 }
465 pub struct SetLang<S: State = Empty>(PhantomData<fn() -> S>);
467 impl<S: State> sealed::Sealed for SetLang<S> {}
468 impl<S: State> State for SetLang<S> {
469 type Lang = Set<members::lang>;
470 type File = S::File;
471 }
472 pub struct SetFile<S: State = Empty>(PhantomData<fn() -> S>);
474 impl<S: State> sealed::Sealed for SetFile<S> {}
475 impl<S: State> State for SetFile<S> {
476 type Lang = S::Lang;
477 type File = Set<members::file>;
478 }
479 #[allow(non_camel_case_types)]
481 pub mod members {
482 pub struct lang(());
484 pub struct file(());
486 }
487}
488
489pub struct CaptionBuilder<'a, S: caption_state::State> {
491 _state: PhantomData<fn() -> S>,
492 _fields: (Option<BlobRef<'a>>, Option<Language>),
493 _lifetime: PhantomData<&'a ()>,
494}
495
496impl<'a> Caption<'a> {
497 pub fn new() -> CaptionBuilder<'a, caption_state::Empty> {
499 CaptionBuilder::new()
500 }
501}
502
503impl<'a> CaptionBuilder<'a, caption_state::Empty> {
504 pub fn new() -> Self {
506 CaptionBuilder {
507 _state: PhantomData,
508 _fields: (None, None),
509 _lifetime: PhantomData,
510 }
511 }
512}
513
514impl<'a, S> CaptionBuilder<'a, S>
515where
516 S: caption_state::State,
517 S::File: caption_state::IsUnset,
518{
519 pub fn file(
521 mut self,
522 value: impl Into<BlobRef<'a>>,
523 ) -> CaptionBuilder<'a, caption_state::SetFile<S>> {
524 self._fields.0 = Option::Some(value.into());
525 CaptionBuilder {
526 _state: PhantomData,
527 _fields: self._fields,
528 _lifetime: PhantomData,
529 }
530 }
531}
532
533impl<'a, S> CaptionBuilder<'a, S>
534where
535 S: caption_state::State,
536 S::Lang: caption_state::IsUnset,
537{
538 pub fn lang(
540 mut self,
541 value: impl Into<Language>,
542 ) -> CaptionBuilder<'a, caption_state::SetLang<S>> {
543 self._fields.1 = Option::Some(value.into());
544 CaptionBuilder {
545 _state: PhantomData,
546 _fields: self._fields,
547 _lifetime: PhantomData,
548 }
549 }
550}
551
552impl<'a, S> CaptionBuilder<'a, S>
553where
554 S: caption_state::State,
555 S::Lang: caption_state::IsSet,
556 S::File: caption_state::IsSet,
557{
558 pub fn build(self) -> Caption<'a> {
560 Caption {
561 file: self._fields.0.unwrap(),
562 lang: self._fields.1.unwrap(),
563 extra_data: Default::default(),
564 }
565 }
566 pub fn build_with_data(
568 self,
569 extra_data: BTreeMap<
570 jacquard_common::deps::smol_str::SmolStr,
571 jacquard_common::types::value::Data<'a>,
572 >,
573 ) -> Caption<'a> {
574 Caption {
575 file: self._fields.0.unwrap(),
576 lang: self._fields.1.unwrap(),
577 extra_data: Some(extra_data),
578 }
579 }
580}
581
582fn lexicon_doc_app_bsky_embed_video() -> LexiconDoc<'static> {
583 #[allow(unused_imports)]
584 use jacquard_common::{CowStr, deps::smol_str::SmolStr, types::blob::MimeType};
585 use jacquard_lexicon::lexicon::*;
586 use alloc::collections::BTreeMap;
587 LexiconDoc {
588 lexicon: Lexicon::Lexicon1,
589 id: CowStr::new_static("app.bsky.embed.video"),
590 defs: {
591 let mut map = BTreeMap::new();
592 map.insert(
593 SmolStr::new_static("caption"),
594 LexUserType::Object(LexObject {
595 required: Some(
596 vec![SmolStr::new_static("lang"), SmolStr::new_static("file")],
597 ),
598 properties: {
599 #[allow(unused_mut)]
600 let mut map = BTreeMap::new();
601 map.insert(
602 SmolStr::new_static("file"),
603 LexObjectProperty::Blob(LexBlob { ..Default::default() }),
604 );
605 map.insert(
606 SmolStr::new_static("lang"),
607 LexObjectProperty::String(LexString {
608 format: Some(LexStringFormat::Language),
609 ..Default::default()
610 }),
611 );
612 map
613 },
614 ..Default::default()
615 }),
616 );
617 map.insert(
618 SmolStr::new_static("main"),
619 LexUserType::Object(LexObject {
620 required: Some(vec![SmolStr::new_static("video")]),
621 properties: {
622 #[allow(unused_mut)]
623 let mut map = BTreeMap::new();
624 map.insert(
625 SmolStr::new_static("alt"),
626 LexObjectProperty::String(LexString {
627 description: Some(
628 CowStr::new_static(
629 "Alt text description of the video, for accessibility.",
630 ),
631 ),
632 max_length: Some(10000usize),
633 max_graphemes: Some(1000usize),
634 ..Default::default()
635 }),
636 );
637 map.insert(
638 SmolStr::new_static("aspectRatio"),
639 LexObjectProperty::Ref(LexRef {
640 r#ref: CowStr::new_static(
641 "app.bsky.embed.defs#aspectRatio",
642 ),
643 ..Default::default()
644 }),
645 );
646 map.insert(
647 SmolStr::new_static("captions"),
648 LexObjectProperty::Array(LexArray {
649 items: LexArrayItem::Ref(LexRef {
650 r#ref: CowStr::new_static("#caption"),
651 ..Default::default()
652 }),
653 max_length: Some(20usize),
654 ..Default::default()
655 }),
656 );
657 map.insert(
658 SmolStr::new_static("presentation"),
659 LexObjectProperty::String(LexString {
660 description: Some(
661 CowStr::new_static(
662 "A hint to the client about how to present the video.",
663 ),
664 ),
665 ..Default::default()
666 }),
667 );
668 map.insert(
669 SmolStr::new_static("video"),
670 LexObjectProperty::Blob(LexBlob { ..Default::default() }),
671 );
672 map
673 },
674 ..Default::default()
675 }),
676 );
677 map.insert(
678 SmolStr::new_static("view"),
679 LexUserType::Object(LexObject {
680 required: Some(
681 vec![SmolStr::new_static("cid"), SmolStr::new_static("playlist")],
682 ),
683 properties: {
684 #[allow(unused_mut)]
685 let mut map = BTreeMap::new();
686 map.insert(
687 SmolStr::new_static("alt"),
688 LexObjectProperty::String(LexString {
689 max_length: Some(10000usize),
690 max_graphemes: Some(1000usize),
691 ..Default::default()
692 }),
693 );
694 map.insert(
695 SmolStr::new_static("aspectRatio"),
696 LexObjectProperty::Ref(LexRef {
697 r#ref: CowStr::new_static(
698 "app.bsky.embed.defs#aspectRatio",
699 ),
700 ..Default::default()
701 }),
702 );
703 map.insert(
704 SmolStr::new_static("cid"),
705 LexObjectProperty::String(LexString {
706 format: Some(LexStringFormat::Cid),
707 ..Default::default()
708 }),
709 );
710 map.insert(
711 SmolStr::new_static("playlist"),
712 LexObjectProperty::String(LexString {
713 format: Some(LexStringFormat::Uri),
714 ..Default::default()
715 }),
716 );
717 map.insert(
718 SmolStr::new_static("presentation"),
719 LexObjectProperty::String(LexString {
720 description: Some(
721 CowStr::new_static(
722 "A hint to the client about how to present the video.",
723 ),
724 ),
725 ..Default::default()
726 }),
727 );
728 map.insert(
729 SmolStr::new_static("thumbnail"),
730 LexObjectProperty::String(LexString {
731 format: Some(LexStringFormat::Uri),
732 ..Default::default()
733 }),
734 );
735 map
736 },
737 ..Default::default()
738 }),
739 );
740 map
741 },
742 ..Default::default()
743 }
744}
745
746pub mod video_state {
747
748 pub use crate::builder_types::{Set, Unset, IsSet, IsUnset};
749 #[allow(unused)]
750 use ::core::marker::PhantomData;
751 mod sealed {
752 pub trait Sealed {}
753 }
754 pub trait State: sealed::Sealed {
756 type Video;
757 }
758 pub struct Empty(());
760 impl sealed::Sealed for Empty {}
761 impl State for Empty {
762 type Video = Unset;
763 }
764 pub struct SetVideo<S: State = Empty>(PhantomData<fn() -> S>);
766 impl<S: State> sealed::Sealed for SetVideo<S> {}
767 impl<S: State> State for SetVideo<S> {
768 type Video = Set<members::video>;
769 }
770 #[allow(non_camel_case_types)]
772 pub mod members {
773 pub struct video(());
775 }
776}
777
778pub struct VideoBuilder<'a, S: video_state::State> {
780 _state: PhantomData<fn() -> S>,
781 _fields: (
782 Option<CowStr<'a>>,
783 Option<AspectRatio<'a>>,
784 Option<Vec<video::Caption<'a>>>,
785 Option<VideoPresentation<'a>>,
786 Option<BlobRef<'a>>,
787 ),
788 _lifetime: PhantomData<&'a ()>,
789}
790
791impl<'a> Video<'a> {
792 pub fn new() -> VideoBuilder<'a, video_state::Empty> {
794 VideoBuilder::new()
795 }
796}
797
798impl<'a> VideoBuilder<'a, video_state::Empty> {
799 pub fn new() -> Self {
801 VideoBuilder {
802 _state: PhantomData,
803 _fields: (None, None, None, None, None),
804 _lifetime: PhantomData,
805 }
806 }
807}
808
809impl<'a, S: video_state::State> VideoBuilder<'a, S> {
810 pub fn alt(mut self, value: impl Into<Option<CowStr<'a>>>) -> Self {
812 self._fields.0 = value.into();
813 self
814 }
815 pub fn maybe_alt(mut self, value: Option<CowStr<'a>>) -> Self {
817 self._fields.0 = value;
818 self
819 }
820}
821
822impl<'a, S: video_state::State> VideoBuilder<'a, S> {
823 pub fn aspect_ratio(mut self, value: impl Into<Option<AspectRatio<'a>>>) -> Self {
825 self._fields.1 = value.into();
826 self
827 }
828 pub fn maybe_aspect_ratio(mut self, value: Option<AspectRatio<'a>>) -> Self {
830 self._fields.1 = value;
831 self
832 }
833}
834
835impl<'a, S: video_state::State> VideoBuilder<'a, S> {
836 pub fn captions(
838 mut self,
839 value: impl Into<Option<Vec<video::Caption<'a>>>>,
840 ) -> Self {
841 self._fields.2 = value.into();
842 self
843 }
844 pub fn maybe_captions(mut self, value: Option<Vec<video::Caption<'a>>>) -> Self {
846 self._fields.2 = value;
847 self
848 }
849}
850
851impl<'a, S: video_state::State> VideoBuilder<'a, S> {
852 pub fn presentation(
854 mut self,
855 value: impl Into<Option<VideoPresentation<'a>>>,
856 ) -> Self {
857 self._fields.3 = value.into();
858 self
859 }
860 pub fn maybe_presentation(mut self, value: Option<VideoPresentation<'a>>) -> Self {
862 self._fields.3 = value;
863 self
864 }
865}
866
867impl<'a, S> VideoBuilder<'a, S>
868where
869 S: video_state::State,
870 S::Video: video_state::IsUnset,
871{
872 pub fn video(
874 mut self,
875 value: impl Into<BlobRef<'a>>,
876 ) -> VideoBuilder<'a, video_state::SetVideo<S>> {
877 self._fields.4 = Option::Some(value.into());
878 VideoBuilder {
879 _state: PhantomData,
880 _fields: self._fields,
881 _lifetime: PhantomData,
882 }
883 }
884}
885
886impl<'a, S> VideoBuilder<'a, S>
887where
888 S: video_state::State,
889 S::Video: video_state::IsSet,
890{
891 pub fn build(self) -> Video<'a> {
893 Video {
894 alt: self._fields.0,
895 aspect_ratio: self._fields.1,
896 captions: self._fields.2,
897 presentation: self._fields.3,
898 video: self._fields.4.unwrap(),
899 extra_data: Default::default(),
900 }
901 }
902 pub fn build_with_data(
904 self,
905 extra_data: BTreeMap<
906 jacquard_common::deps::smol_str::SmolStr,
907 jacquard_common::types::value::Data<'a>,
908 >,
909 ) -> Video<'a> {
910 Video {
911 alt: self._fields.0,
912 aspect_ratio: self._fields.1,
913 captions: self._fields.2,
914 presentation: self._fields.3,
915 video: self._fields.4.unwrap(),
916 extra_data: Some(extra_data),
917 }
918 }
919}
920
921pub mod view_state {
922
923 pub use crate::builder_types::{Set, Unset, IsSet, IsUnset};
924 #[allow(unused)]
925 use ::core::marker::PhantomData;
926 mod sealed {
927 pub trait Sealed {}
928 }
929 pub trait State: sealed::Sealed {
931 type Cid;
932 type Playlist;
933 }
934 pub struct Empty(());
936 impl sealed::Sealed for Empty {}
937 impl State for Empty {
938 type Cid = Unset;
939 type Playlist = Unset;
940 }
941 pub struct SetCid<S: State = Empty>(PhantomData<fn() -> S>);
943 impl<S: State> sealed::Sealed for SetCid<S> {}
944 impl<S: State> State for SetCid<S> {
945 type Cid = Set<members::cid>;
946 type Playlist = S::Playlist;
947 }
948 pub struct SetPlaylist<S: State = Empty>(PhantomData<fn() -> S>);
950 impl<S: State> sealed::Sealed for SetPlaylist<S> {}
951 impl<S: State> State for SetPlaylist<S> {
952 type Cid = S::Cid;
953 type Playlist = Set<members::playlist>;
954 }
955 #[allow(non_camel_case_types)]
957 pub mod members {
958 pub struct cid(());
960 pub struct playlist(());
962 }
963}
964
965pub struct ViewBuilder<'a, S: view_state::State> {
967 _state: PhantomData<fn() -> S>,
968 _fields: (
969 Option<CowStr<'a>>,
970 Option<AspectRatio<'a>>,
971 Option<Cid<'a>>,
972 Option<UriValue<'a>>,
973 Option<ViewPresentation<'a>>,
974 Option<UriValue<'a>>,
975 ),
976 _lifetime: PhantomData<&'a ()>,
977}
978
979impl<'a> View<'a> {
980 pub fn new() -> ViewBuilder<'a, view_state::Empty> {
982 ViewBuilder::new()
983 }
984}
985
986impl<'a> ViewBuilder<'a, view_state::Empty> {
987 pub fn new() -> Self {
989 ViewBuilder {
990 _state: PhantomData,
991 _fields: (None, None, None, None, None, None),
992 _lifetime: PhantomData,
993 }
994 }
995}
996
997impl<'a, S: view_state::State> ViewBuilder<'a, S> {
998 pub fn alt(mut self, value: impl Into<Option<CowStr<'a>>>) -> Self {
1000 self._fields.0 = value.into();
1001 self
1002 }
1003 pub fn maybe_alt(mut self, value: Option<CowStr<'a>>) -> Self {
1005 self._fields.0 = value;
1006 self
1007 }
1008}
1009
1010impl<'a, S: view_state::State> ViewBuilder<'a, S> {
1011 pub fn aspect_ratio(mut self, value: impl Into<Option<AspectRatio<'a>>>) -> Self {
1013 self._fields.1 = value.into();
1014 self
1015 }
1016 pub fn maybe_aspect_ratio(mut self, value: Option<AspectRatio<'a>>) -> Self {
1018 self._fields.1 = value;
1019 self
1020 }
1021}
1022
1023impl<'a, S> ViewBuilder<'a, S>
1024where
1025 S: view_state::State,
1026 S::Cid: view_state::IsUnset,
1027{
1028 pub fn cid(
1030 mut self,
1031 value: impl Into<Cid<'a>>,
1032 ) -> ViewBuilder<'a, view_state::SetCid<S>> {
1033 self._fields.2 = Option::Some(value.into());
1034 ViewBuilder {
1035 _state: PhantomData,
1036 _fields: self._fields,
1037 _lifetime: PhantomData,
1038 }
1039 }
1040}
1041
1042impl<'a, S> ViewBuilder<'a, S>
1043where
1044 S: view_state::State,
1045 S::Playlist: view_state::IsUnset,
1046{
1047 pub fn playlist(
1049 mut self,
1050 value: impl Into<UriValue<'a>>,
1051 ) -> ViewBuilder<'a, view_state::SetPlaylist<S>> {
1052 self._fields.3 = Option::Some(value.into());
1053 ViewBuilder {
1054 _state: PhantomData,
1055 _fields: self._fields,
1056 _lifetime: PhantomData,
1057 }
1058 }
1059}
1060
1061impl<'a, S: view_state::State> ViewBuilder<'a, S> {
1062 pub fn presentation(
1064 mut self,
1065 value: impl Into<Option<ViewPresentation<'a>>>,
1066 ) -> Self {
1067 self._fields.4 = value.into();
1068 self
1069 }
1070 pub fn maybe_presentation(mut self, value: Option<ViewPresentation<'a>>) -> Self {
1072 self._fields.4 = value;
1073 self
1074 }
1075}
1076
1077impl<'a, S: view_state::State> ViewBuilder<'a, S> {
1078 pub fn thumbnail(mut self, value: impl Into<Option<UriValue<'a>>>) -> Self {
1080 self._fields.5 = value.into();
1081 self
1082 }
1083 pub fn maybe_thumbnail(mut self, value: Option<UriValue<'a>>) -> Self {
1085 self._fields.5 = value;
1086 self
1087 }
1088}
1089
1090impl<'a, S> ViewBuilder<'a, S>
1091where
1092 S: view_state::State,
1093 S::Cid: view_state::IsSet,
1094 S::Playlist: view_state::IsSet,
1095{
1096 pub fn build(self) -> View<'a> {
1098 View {
1099 alt: self._fields.0,
1100 aspect_ratio: self._fields.1,
1101 cid: self._fields.2.unwrap(),
1102 playlist: self._fields.3.unwrap(),
1103 presentation: self._fields.4,
1104 thumbnail: self._fields.5,
1105 extra_data: Default::default(),
1106 }
1107 }
1108 pub fn build_with_data(
1110 self,
1111 extra_data: BTreeMap<
1112 jacquard_common::deps::smol_str::SmolStr,
1113 jacquard_common::types::value::Data<'a>,
1114 >,
1115 ) -> View<'a> {
1116 View {
1117 alt: self._fields.0,
1118 aspect_ratio: self._fields.1,
1119 cid: self._fields.2.unwrap(),
1120 playlist: self._fields.3.unwrap(),
1121 presentation: self._fields.4,
1122 thumbnail: self._fields.5,
1123 extra_data: Some(extra_data),
1124 }
1125 }
1126}