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