typed_open_graph/
lib.rs

1#![deny(unsafe_code)]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3
4use humantime::format_rfc3339;
5use std::fmt::Display;
6use std::time::SystemTime;
7
8const PREFIX: &str = "og: https://ogp.me/ns#";
9
10#[allow(dead_code)]
11#[derive(Debug)]
12enum OpenGraphDataTypes {
13    Boolean(bool),
14    String(String),
15    Float(f64),
16    Integer(isize),
17    Url(url::Url),
18}
19
20impl Display for OpenGraphDataTypes {
21    fn fmt(&self, f1: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22        let str = match self {
23            Self::Boolean(b) => {
24                if *b {
25                    "true".to_string()
26                } else {
27                    "false".to_string()
28                }
29            }
30            Self::String(s) => s.to_string(),
31            Self::Float(f) => f.to_string(),
32            Self::Integer(i) => i.to_string(),
33            Self::Url(url) => url.to_string(),
34        };
35        write!(f1, "{}", str)
36    }
37}
38
39/// The determiner of the object being tagged.
40pub enum Determiner {
41    A,
42    An,
43    The,
44    Blank,
45    Auto,
46}
47
48impl Display for Determiner {
49    fn fmt(&self, f1: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50        let str = match self {
51            Self::A => "a",
52            Self::An => "an",
53            Self::The => "the",
54            Self::Blank => "",
55            Self::Auto => "auto",
56        };
57        write!(f1, "{}", str)
58    }
59}
60
61/// The status of the payment.
62pub enum PaymentStatus {
63    Pending,
64    Paid,
65    Failed,
66    Expired,
67}
68
69impl Display for PaymentStatus {
70    fn fmt(&self, f1: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
71        let str = match self {
72            Self::Pending => "PENDING",
73            Self::Paid => "PAID",
74            Self::Failed => "FAILED",
75            Self::Expired => "EXPIRED",
76        };
77        write!(f1, "{}", str)
78    }
79}
80
81/// Profile Gender
82pub enum Gender {
83    Male,
84    Female,
85}
86
87impl Display for Gender {
88    fn fmt(&self, f1: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89        let str = match self {
90            Self::Male => "male",
91            Self::Female => "female",
92        };
93        write!(f1, "{}", str)
94    }
95}
96
97pub trait OpenGraphTypeMarker: Send + Sync + Sized {
98    const TYPE: &'static str;
99    const EXTRA_PREFIX: &'static str;
100
101    fn builder(
102        title: impl Into<&'static str>,
103        image: url::Url,
104        url: url::Url,
105    ) -> OpenGraphBuilder<Self> {
106        OpenGraphBuilder::new(title, image, url)
107    }
108}
109
110#[derive(Debug)]
111pub struct MusicSong;
112
113impl OpenGraphTypeMarker for MusicSong {
114    const TYPE: &'static str = "music.song";
115    const EXTRA_PREFIX: &'static str = "music: https://ogp.me/ns/music#";
116}
117
118#[derive(Debug)]
119pub struct MusicAlbum;
120
121impl OpenGraphTypeMarker for MusicAlbum {
122    const TYPE: &'static str = "music.album";
123    const EXTRA_PREFIX: &'static str = "music: https://ogp.me/ns/music#";
124}
125
126#[derive(Debug)]
127pub struct MusicPlaylist;
128
129impl OpenGraphTypeMarker for MusicPlaylist {
130    const TYPE: &'static str = "music.playlist";
131    const EXTRA_PREFIX: &'static str = "music: https://ogp.me/ns/music#";
132}
133
134#[derive(Debug)]
135pub struct MusicRadioStation;
136
137impl OpenGraphTypeMarker for MusicRadioStation {
138    const TYPE: &'static str = "music.radio_station";
139    const EXTRA_PREFIX: &'static str = "music: https://ogp.me/ns/music#";
140}
141
142#[derive(Debug)]
143pub struct VideoMovie;
144
145impl OpenGraphTypeMarker for VideoMovie {
146    const TYPE: &'static str = "video.movie";
147    const EXTRA_PREFIX: &'static str = "video: https://ogp.me/ns/video#";
148}
149
150#[derive(Debug)]
151pub struct VideoEpisode;
152
153impl OpenGraphTypeMarker for VideoEpisode {
154    const TYPE: &'static str = "video.episode";
155    const EXTRA_PREFIX: &'static str = "video: https://ogp.me/ns/video#";
156}
157
158#[derive(Debug)]
159pub struct VideoTvShow;
160
161impl OpenGraphTypeMarker for VideoTvShow {
162    const TYPE: &'static str = "video.tv_show";
163    const EXTRA_PREFIX: &'static str = "video: https://ogp.me/ns/video#";
164}
165
166#[derive(Debug)]
167pub struct VideoOther;
168
169impl OpenGraphTypeMarker for VideoOther {
170    const TYPE: &'static str = "video.other";
171    const EXTRA_PREFIX: &'static str = "video: https://ogp.me/ns/video#";
172}
173
174#[derive(Debug)]
175pub struct Article;
176
177impl OpenGraphTypeMarker for Article {
178    const TYPE: &'static str = "article";
179    const EXTRA_PREFIX: &'static str = "article: https://ogp.me/ns/article#";
180}
181
182#[derive(Debug)]
183pub struct Book;
184
185impl OpenGraphTypeMarker for Book {
186    const TYPE: &'static str = "book";
187    const EXTRA_PREFIX: &'static str = "book: https://ogp.me/ns/book#";
188}
189
190#[derive(Debug)]
191pub struct PaymentLink;
192
193impl OpenGraphTypeMarker for PaymentLink {
194    const TYPE: &'static str = "payment.link";
195    const EXTRA_PREFIX: &'static str = "payment: https://ogp.me/ns/payment#";
196}
197
198#[derive(Debug)]
199pub struct Profile;
200
201impl OpenGraphTypeMarker for Profile {
202    const TYPE: &'static str = "profile";
203    const EXTRA_PREFIX: &'static str = "profile: https://ogp.me/ns/profile#";
204}
205
206#[derive(Debug)]
207pub struct Website;
208
209impl OpenGraphTypeMarker for Website {
210    const TYPE: &'static str = "website";
211    const EXTRA_PREFIX: &'static str = "website: https://ogp.me/ns/website#";
212}
213
214#[derive(Debug)]
215pub struct OpenGraphBuilder<M: OpenGraphTypeMarker> {
216    list: Vec<(&'static str, OpenGraphDataTypes)>,
217    marker: std::marker::PhantomData<M>,
218}
219
220impl<M: OpenGraphTypeMarker> OpenGraphBuilder<M> {
221    pub fn new(title: impl Into<&'static str>, image: url::Url, url: url::Url) -> Self {
222        Self {
223            list: vec![
224                (
225                    "og:title",
226                    OpenGraphDataTypes::String(title.into().to_string()),
227                ),
228                ("og:image", OpenGraphDataTypes::Url(image)),
229                ("og:url", OpenGraphDataTypes::Url(url)),
230            ],
231            marker: std::marker::PhantomData,
232        }
233    }
234
235    pub fn image_secure_url(mut self, image_secure_url: url::Url) -> Self {
236        self.list.push((
237            "og:image:secure_url",
238            OpenGraphDataTypes::Url(image_secure_url),
239        ));
240        self
241    }
242
243    pub fn image_type(mut self, image_type: impl Into<&'static str>) -> Self {
244        self.list.push((
245            "og:image:type",
246            OpenGraphDataTypes::String(image_type.into().to_string()),
247        ));
248        self
249    }
250
251    pub fn image_width(mut self, image_width: isize) -> Self {
252        self.list
253            .push(("og:image:width", OpenGraphDataTypes::Integer(image_width)));
254        self
255    }
256
257    pub fn image_height(mut self, image_height: isize) -> Self {
258        self.list
259            .push(("og:image:height", OpenGraphDataTypes::Integer(image_height)));
260        self
261    }
262
263    pub fn image_alt(mut self, image_alt: impl Into<&'static str>) -> Self {
264        self.list.push((
265            "og:image:alt",
266            OpenGraphDataTypes::String(image_alt.into().to_string()),
267        ));
268        self
269    }
270
271    pub fn audio(mut self, audio: url::Url) -> Self {
272        self.list.push(("og:audio", OpenGraphDataTypes::Url(audio)));
273        self
274    }
275
276    pub fn audio_secure_url(mut self, audio_secure_url: url::Url) -> Self {
277        self.list.push((
278            "og:audio:secure_url",
279            OpenGraphDataTypes::Url(audio_secure_url),
280        ));
281        self
282    }
283
284    pub fn audio_type(mut self, audio_type: impl Into<&'static str>) -> Self {
285        self.list.push((
286            "og:audio:type",
287            OpenGraphDataTypes::String(audio_type.into().to_string()),
288        ));
289        self
290    }
291
292    pub fn description(mut self, description: impl Into<&'static str>) -> Self {
293        self.list.push((
294            "og:description",
295            OpenGraphDataTypes::String(description.into().to_string()),
296        ));
297        self
298    }
299
300    pub fn determiner(mut self, determiner: Determiner) -> Self {
301        self.list.push((
302            "og:determiner",
303            OpenGraphDataTypes::String(determiner.to_string()),
304        ));
305        self
306    }
307
308    pub fn locale(mut self, locale: impl Into<&'static str>) -> Self {
309        self.list.push((
310            "og:locale",
311            OpenGraphDataTypes::String(locale.into().to_string()),
312        ));
313        self
314    }
315
316    pub fn locale_alternate(mut self, locales: Vec<impl Into<&'static str>>) -> Self {
317        for locale in locales {
318            self.list.push((
319                "og:locale:alternate",
320                OpenGraphDataTypes::String(locale.into().to_string()),
321            ));
322        }
323        self
324    }
325
326    pub fn site_name(mut self, site_name: impl Into<&'static str>) -> Self {
327        self.list.push((
328            "og:site_name",
329            OpenGraphDataTypes::String(site_name.into().to_string()),
330        ));
331        self
332    }
333
334    pub fn video(mut self, video: url::Url) -> Self {
335        self.list.push(("og:video", OpenGraphDataTypes::Url(video)));
336        self
337    }
338
339    pub fn video_secure_url(mut self, video_secure_url: url::Url) -> Self {
340        self.list.push((
341            "og:video:secure_url",
342            OpenGraphDataTypes::Url(video_secure_url),
343        ));
344        self
345    }
346
347    pub fn video_type(mut self, video_type: impl Into<&'static str>) -> Self {
348        self.list.push((
349            "og:video:type",
350            OpenGraphDataTypes::String(video_type.into().to_string()),
351        ));
352        self
353    }
354
355    pub fn video_width(mut self, video_width: isize) -> Self {
356        self.list
357            .push(("og:video:width", OpenGraphDataTypes::Integer(video_width)));
358        self
359    }
360
361    pub fn video_height(mut self, video_height: isize) -> Self {
362        self.list
363            .push(("og:video:height", OpenGraphDataTypes::Integer(video_height)));
364        self
365    }
366
367    pub fn video_alt(mut self, video_tag: impl Into<&'static str>) -> Self {
368        self.list.push((
369            "og:video:alt",
370            OpenGraphDataTypes::String(video_tag.into().to_string()),
371        ));
372        self
373    }
374
375    pub fn custom(mut self, key: impl Into<&'static str>, value: impl Into<&'static str>) -> Self {
376        self.list.push((
377            key.into(),
378            OpenGraphDataTypes::String(value.into().to_string()),
379        ));
380        self
381    }
382
383    pub fn build(self) -> OpenGraph {
384        OpenGraph {
385            prefix: format!("{} {}", PREFIX, M::EXTRA_PREFIX),
386            og_type: M::TYPE.to_string(),
387            list: self
388                .list
389                .iter()
390                .map(|i| (i.0.to_string(), i.1.to_string()))
391                .collect(),
392        }
393    }
394}
395
396impl<M: OpenGraphTypeMarker> Into<OpenGraph> for OpenGraphBuilder<M> {
397    fn into(self) -> OpenGraph {
398        self.build()
399    }
400}
401
402impl OpenGraphBuilder<MusicSong> {
403    pub fn music_duration(mut self, music_duration: isize) -> Self {
404        if music_duration >= 1 {
405            self.list.push((
406                "music:duration",
407                OpenGraphDataTypes::Integer(music_duration),
408            ));
409        }
410        self
411    }
412
413    pub fn music_album(mut self, music_album: url::Url) -> Self {
414        self.list
415            .push(("music:album", OpenGraphDataTypes::Url(music_album)));
416        self
417    }
418
419    pub fn music_album_disc(mut self, music_album_disc: isize) -> Self {
420        if music_album_disc >= 1 {
421            self.list.push((
422                "music:album:disc",
423                OpenGraphDataTypes::Integer(music_album_disc),
424            ));
425        }
426        self
427    }
428
429    pub fn music_album_track(mut self, music_album_track: isize) -> Self {
430        if music_album_track >= 1 {
431            self.list.push((
432                "music:album:track",
433                OpenGraphDataTypes::Integer(music_album_track),
434            ));
435        }
436        self
437    }
438
439    pub fn music_musician(mut self, music_musician: url::Url) -> Self {
440        self.list
441            .push(("music:musician", OpenGraphDataTypes::Url(music_musician)));
442        self
443    }
444}
445
446impl OpenGraphBuilder<MusicAlbum> {
447    pub fn music_song(mut self, music_song: url::Url) -> Self {
448        self.list
449            .push(("music:song", OpenGraphDataTypes::Url(music_song)));
450        self
451    }
452
453    pub fn music_song_disc(mut self, music_song_disc: isize) -> Self {
454        if music_song_disc >= 1 {
455            self.list.push((
456                "music:song:disc",
457                OpenGraphDataTypes::Integer(music_song_disc),
458            ));
459        }
460        self
461    }
462
463    pub fn music_song_track(mut self, music_song_track: isize) -> Self {
464        if music_song_track >= 1 {
465            self.list.push((
466                "music:song:track",
467                OpenGraphDataTypes::Integer(music_song_track),
468            ));
469        }
470        self
471    }
472
473    pub fn music_musician(mut self, music_musician: url::Url) -> Self {
474        self.list
475            .push(("music:musician", OpenGraphDataTypes::Url(music_musician)));
476        self
477    }
478
479    pub fn music_release_date(mut self, music_release_date: SystemTime) -> Self {
480        self.list.push((
481            "music:release_date",
482            OpenGraphDataTypes::String(format_rfc3339(music_release_date).to_string()),
483        ));
484        self
485    }
486
487    #[cfg(feature = "chrono")]
488    pub fn music_release_date_chrono(
489        mut self,
490        music_release_date: impl Into<chrono::DateTime<chrono::Utc>>,
491    ) -> Self {
492        self.list.push((
493            "music:release_date",
494            OpenGraphDataTypes::String(music_release_date.into().to_rfc3339()),
495        ));
496        self
497    }
498}
499
500impl OpenGraphBuilder<MusicPlaylist> {
501    pub fn music_song(mut self, music_song: url::Url) -> Self {
502        self.list
503            .push(("music:song", OpenGraphDataTypes::Url(music_song)));
504        self
505    }
506
507    pub fn music_song_disc(mut self, music_song_disc: isize) -> Self {
508        if music_song_disc >= 1 {
509            self.list.push((
510                "music:song:disc",
511                OpenGraphDataTypes::Integer(music_song_disc),
512            ));
513        }
514        self
515    }
516
517    pub fn music_song_track(mut self, music_song_track: isize) -> Self {
518        if music_song_track >= 1 {
519            self.list.push((
520                "music:song:track",
521                OpenGraphDataTypes::Integer(music_song_track),
522            ));
523        }
524        self
525    }
526
527    pub fn music_creator(mut self, music_creator: url::Url) -> Self {
528        self.list
529            .push(("music:creator", OpenGraphDataTypes::Url(music_creator)));
530        self
531    }
532}
533
534impl OpenGraphBuilder<MusicRadioStation> {
535    pub fn music_creator(mut self, music_creator: url::Url) -> Self {
536        self.list
537            .push(("music:creator", OpenGraphDataTypes::Url(music_creator)));
538        self
539    }
540}
541
542impl OpenGraphBuilder<VideoMovie> {
543    pub fn video_actor(mut self, video_actor: url::Url) -> Self {
544        self.list
545            .push(("video:actor", OpenGraphDataTypes::Url(video_actor)));
546        self
547    }
548
549    pub fn video_actor_role(mut self, video_actor_role: impl Into<&'static str>) -> Self {
550        self.list.push((
551            "video:actor:role",
552            OpenGraphDataTypes::String(video_actor_role.into().to_string()),
553        ));
554        self
555    }
556
557    pub fn video_director(mut self, video_director: url::Url) -> Self {
558        self.list
559            .push(("video:director", OpenGraphDataTypes::Url(video_director)));
560        self
561    }
562
563    pub fn video_writer(mut self, video_writer: url::Url) -> Self {
564        self.list
565            .push(("video:writer", OpenGraphDataTypes::Url(video_writer)));
566        self
567    }
568
569    pub fn video_duration(mut self, video_duration: isize) -> Self {
570        if video_duration >= 1 {
571            self.list.push((
572                "video:duration",
573                OpenGraphDataTypes::Integer(video_duration),
574            ));
575        }
576        self
577    }
578
579    pub fn video_release_date(mut self, video_release_date: SystemTime) -> Self {
580        self.list.push((
581            "video:release_date",
582            OpenGraphDataTypes::String(format_rfc3339(video_release_date).to_string()),
583        ));
584        self
585    }
586
587    #[cfg(feature = "chrono")]
588    pub fn video_release_date_chrono(
589        mut self,
590        video_release_date: impl Into<chrono::DateTime<chrono::Utc>>,
591    ) -> Self {
592        self.list.push((
593            "video:release_date",
594            OpenGraphDataTypes::String(video_release_date.into().to_rfc3339()),
595        ));
596        self
597    }
598
599    pub fn video_tag(mut self, video_tag: impl Into<&'static str>) -> Self {
600        self.list.push((
601            "video:tag",
602            OpenGraphDataTypes::String(video_tag.into().to_string()),
603        ));
604        self
605    }
606}
607
608impl OpenGraphBuilder<VideoEpisode> {
609    pub fn video_actor(mut self, video_actor: url::Url) -> Self {
610        self.list
611            .push(("video:actor", OpenGraphDataTypes::Url(video_actor)));
612        self
613    }
614
615    pub fn video_actor_role(mut self, video_actor_role: impl Into<&'static str>) -> Self {
616        self.list.push((
617            "video:actor:role",
618            OpenGraphDataTypes::String(video_actor_role.into().to_string()),
619        ));
620        self
621    }
622
623    pub fn video_director(mut self, video_director: url::Url) -> Self {
624        self.list
625            .push(("video:director", OpenGraphDataTypes::Url(video_director)));
626        self
627    }
628
629    pub fn video_writer(mut self, video_writer: url::Url) -> Self {
630        self.list
631            .push(("video:writer", OpenGraphDataTypes::Url(video_writer)));
632        self
633    }
634
635    pub fn video_duration(mut self, video_duration: isize) -> Self {
636        if video_duration >= 1 {
637            self.list.push((
638                "video:duration",
639                OpenGraphDataTypes::Integer(video_duration),
640            ));
641        }
642        self
643    }
644
645    pub fn video_release_date(mut self, video_release_date: SystemTime) -> Self {
646        self.list.push((
647            "video:release_date",
648            OpenGraphDataTypes::String(format_rfc3339(video_release_date).to_string()),
649        ));
650        self
651    }
652
653    #[cfg(feature = "chrono")]
654    pub fn video_release_date_chrono(
655        mut self,
656        video_release_date: impl Into<chrono::DateTime<chrono::Utc>>,
657    ) -> Self {
658        self.list.push((
659            "video:release_date",
660            OpenGraphDataTypes::String(video_release_date.into().to_rfc3339()),
661        ));
662        self
663    }
664
665    pub fn video_tag(mut self, video_tag: impl Into<&'static str>) -> Self {
666        self.list.push((
667            "video:tag",
668            OpenGraphDataTypes::String(video_tag.into().to_string()),
669        ));
670        self
671    }
672
673    pub fn video_series(mut self, video_series: url::Url) -> Self {
674        self.list
675            .push(("video:series", OpenGraphDataTypes::Url(video_series)));
676        self
677    }
678}
679
680impl OpenGraphBuilder<VideoTvShow> {
681    pub fn video_actor(mut self, video_actor: url::Url) -> Self {
682        self.list
683            .push(("video:actor", OpenGraphDataTypes::Url(video_actor)));
684        self
685    }
686
687    pub fn video_actor_role(mut self, video_actor_role: impl Into<&'static str>) -> Self {
688        self.list.push((
689            "video:actor:role",
690            OpenGraphDataTypes::String(video_actor_role.into().to_string()),
691        ));
692        self
693    }
694
695    pub fn video_director(mut self, video_director: url::Url) -> Self {
696        self.list
697            .push(("video:director", OpenGraphDataTypes::Url(video_director)));
698        self
699    }
700
701    pub fn video_writer(mut self, video_writer: url::Url) -> Self {
702        self.list
703            .push(("video:writer", OpenGraphDataTypes::Url(video_writer)));
704        self
705    }
706
707    pub fn video_duration(mut self, video_duration: isize) -> Self {
708        if video_duration >= 1 {
709            self.list.push((
710                "video:duration",
711                OpenGraphDataTypes::Integer(video_duration),
712            ));
713        }
714        self
715    }
716
717    #[cfg(feature = "chrono")]
718    pub fn video_release_date_chrono(
719        mut self,
720        video_release_date: impl Into<chrono::DateTime<chrono::Utc>>,
721    ) -> Self {
722        self.list.push((
723            "video:release_date",
724            OpenGraphDataTypes::String(video_release_date.into().to_rfc3339()),
725        ));
726        self
727    }
728
729    pub fn video_tag(mut self, video_tag: impl Into<&'static str>) -> Self {
730        self.list.push((
731            "video:tag",
732            OpenGraphDataTypes::String(video_tag.into().to_string()),
733        ));
734        self
735    }
736}
737
738impl OpenGraphBuilder<VideoOther> {
739    pub fn video_actor(mut self, video_actor: url::Url) -> Self {
740        self.list
741            .push(("video:actor", OpenGraphDataTypes::Url(video_actor)));
742        self
743    }
744
745    pub fn video_actor_role(mut self, video_actor_role: impl Into<&'static str>) -> Self {
746        self.list.push((
747            "video:actor:role",
748            OpenGraphDataTypes::String(video_actor_role.into().to_string()),
749        ));
750        self
751    }
752
753    pub fn video_director(mut self, video_director: url::Url) -> Self {
754        self.list
755            .push(("video:director", OpenGraphDataTypes::Url(video_director)));
756        self
757    }
758
759    pub fn video_writer(mut self, video_writer: url::Url) -> Self {
760        self.list
761            .push(("video:writer", OpenGraphDataTypes::Url(video_writer)));
762        self
763    }
764
765    pub fn video_duration(mut self, video_duration: isize) -> Self {
766        if video_duration >= 1 {
767            self.list.push((
768                "video:duration",
769                OpenGraphDataTypes::Integer(video_duration),
770            ));
771        }
772        self
773    }
774
775    pub fn video_release_date(mut self, video_release_date: SystemTime) -> Self {
776        self.list.push((
777            "video:release_date",
778            OpenGraphDataTypes::String(format_rfc3339(video_release_date).to_string()),
779        ));
780        self
781    }
782
783    #[cfg(feature = "chrono")]
784    pub fn video_release_date_chrono(
785        mut self,
786        video_release_date: impl Into<chrono::DateTime<chrono::Utc>>,
787    ) -> Self {
788        self.list.push((
789            "video:release_date",
790            OpenGraphDataTypes::String(video_release_date.into().to_rfc3339()),
791        ));
792        self
793    }
794
795    pub fn video_tag(mut self, video_tag: impl Into<&'static str>) -> Self {
796        self.list.push((
797            "video:tag",
798            OpenGraphDataTypes::String(video_tag.into().to_string()),
799        ));
800        self
801    }
802}
803
804impl OpenGraphBuilder<Article> {
805    pub fn article_published_time(mut self, article_published_time: SystemTime) -> Self {
806        self.list.push((
807            "article:published_time",
808            OpenGraphDataTypes::String(format_rfc3339(article_published_time).to_string()),
809        ));
810        self
811    }
812
813    #[cfg(feature = "chrono")]
814    pub fn article_published_time_chrono(
815        mut self,
816        article_published_time: impl Into<chrono::DateTime<chrono::Utc>>,
817    ) -> Self {
818        self.list.push((
819            "article:published_time",
820            OpenGraphDataTypes::String(article_published_time.into().to_rfc3339()),
821        ));
822        self
823    }
824
825    pub fn article_modified_time(mut self, article_modified_time: SystemTime) -> Self {
826        self.list.push((
827            "article:modified_time",
828            OpenGraphDataTypes::String(format_rfc3339(article_modified_time).to_string()),
829        ));
830        self
831    }
832
833    #[cfg(feature = "chrono")]
834    pub fn article_modified_time_chrono(
835        mut self,
836        article_modified_time: impl Into<chrono::DateTime<chrono::Utc>>,
837    ) -> Self {
838        self.list.push((
839            "article:modified_time",
840            OpenGraphDataTypes::String(article_modified_time.into().to_rfc3339()),
841        ));
842        self
843    }
844
845    pub fn article_expiration_time(mut self, article_expiration_time: SystemTime) -> Self {
846        self.list.push((
847            "article:expiration_time",
848            OpenGraphDataTypes::String(format_rfc3339(article_expiration_time).to_string()),
849        ));
850        self
851    }
852
853    #[cfg(feature = "chrono")]
854    pub fn article_expiration_time_chrono(
855        mut self,
856        article_expiration_time: impl Into<chrono::DateTime<chrono::Utc>>,
857    ) -> Self {
858        self.list.push((
859            "article:expiration_time",
860            OpenGraphDataTypes::String(article_expiration_time.into().to_rfc3339()),
861        ));
862        self
863    }
864
865    pub fn article_author(mut self, article_author: url::Url) -> Self {
866        self.list
867            .push(("article:author", OpenGraphDataTypes::Url(article_author)));
868        self
869    }
870
871    pub fn article_section(mut self, article_section: impl Into<&'static str>) -> Self {
872        self.list.push((
873            "article:section",
874            OpenGraphDataTypes::String(article_section.into().to_string()),
875        ));
876        self
877    }
878
879    pub fn article_tag(mut self, article_tag: impl Into<&'static str>) -> Self {
880        self.list.push((
881            "article:tag",
882            OpenGraphDataTypes::String(article_tag.into().to_string()),
883        ));
884        self
885    }
886}
887
888impl OpenGraphBuilder<Book> {
889    pub fn book_author(mut self, book_author: url::Url) -> Self {
890        self.list
891            .push(("book:author", OpenGraphDataTypes::Url(book_author)));
892        self
893    }
894
895    pub fn book_isbn(mut self, book_isbn: impl Into<&'static str>) -> Self {
896        self.list.push((
897            "book:isbn",
898            OpenGraphDataTypes::String(book_isbn.into().to_string()),
899        ));
900        self
901    }
902
903    pub fn book_release_date(mut self, book_release_date: SystemTime) -> Self {
904        self.list.push((
905            "book:release_date",
906            OpenGraphDataTypes::String(format_rfc3339(book_release_date).to_string()),
907        ));
908        self
909    }
910
911    #[cfg(feature = "chrono")]
912    pub fn book_release_data_chrono(
913        mut self,
914        book_release_date: impl Into<chrono::DateTime<chrono::Utc>>,
915    ) -> Self {
916        self.list.push((
917            "book:release_date",
918            OpenGraphDataTypes::String(book_release_date.into().to_rfc3339()),
919        ));
920        self
921    }
922
923    pub fn book_tag(mut self, book_tag: impl Into<&'static str>) -> Self {
924        self.list.push((
925            "book:tag",
926            OpenGraphDataTypes::String(book_tag.into().to_string()),
927        ));
928        self
929    }
930}
931
932impl OpenGraphBuilder<PaymentLink> {
933    pub fn payment_description(mut self, payment_description: impl Into<&'static str>) -> Self {
934        self.list.push((
935            "payment:description",
936            OpenGraphDataTypes::String(payment_description.into().to_string()),
937        ));
938        self
939    }
940
941    pub fn payment_currency(mut self, payment_currency: impl Into<&'static str>) -> Self {
942        self.list.push((
943            "payment:currency",
944            OpenGraphDataTypes::String(payment_currency.into().to_string()),
945        ));
946        self
947    }
948
949    pub fn payment_amount(mut self, payment_amount: impl Into<f64>) -> Self {
950        self.list.push((
951            "payment:amount",
952            OpenGraphDataTypes::Float(payment_amount.into()),
953        ));
954        self
955    }
956
957    #[cfg(feature = "chrono")]
958    pub fn payment_expires_at_chrono(
959        mut self,
960        payment_expires_at: impl Into<chrono::DateTime<chrono::Utc>>,
961    ) -> Self {
962        self.list.push((
963            "payment:expires_at",
964            OpenGraphDataTypes::String(payment_expires_at.into().to_rfc3339()),
965        ));
966        self
967    }
968
969    pub fn payment_status(mut self, payment_status: PaymentStatus) -> Self {
970        self.list.push((
971            "payment:status",
972            OpenGraphDataTypes::String(payment_status.to_string()),
973        ));
974        self
975    }
976
977    pub fn payment_id(mut self, payment_id: impl Into<&'static str>) -> Self {
978        self.list.push((
979            "payment:id",
980            OpenGraphDataTypes::String(payment_id.into().to_string()),
981        ));
982        self
983    }
984
985    pub fn payment_success_url(mut self, payment_success_url: url::Url) -> Self {
986        self.list.push((
987            "payment:success_url",
988            OpenGraphDataTypes::Url(payment_success_url),
989        ));
990        self
991    }
992}
993
994impl OpenGraphBuilder<Profile> {
995    pub fn profile_first_name(mut self, profile_first_name: impl Into<&'static str>) -> Self {
996        self.list.push((
997            "profile:first_name",
998            OpenGraphDataTypes::String(profile_first_name.into().to_string()),
999        ));
1000        self
1001    }
1002
1003    pub fn profile_last_name(mut self, profile_last_name: impl Into<&'static str>) -> Self {
1004        self.list.push((
1005            "profile:last_name",
1006            OpenGraphDataTypes::String(profile_last_name.into().to_string()),
1007        ));
1008        self
1009    }
1010
1011    pub fn profile_username(mut self, profile_username: impl Into<&'static str>) -> Self {
1012        self.list.push((
1013            "profile:username",
1014            OpenGraphDataTypes::String(profile_username.into().to_string()),
1015        ));
1016        self
1017    }
1018
1019    pub fn profile_gender(mut self, profile_gender: Gender) -> Self {
1020        self.list.push((
1021            "profile:gender",
1022            OpenGraphDataTypes::String(profile_gender.to_string()),
1023        ));
1024        self
1025    }
1026}
1027
1028#[derive(Debug, Clone)]
1029pub struct OpenGraph {
1030    pub prefix: String,
1031    pub og_type: String,
1032    pub list: Box<[(String, String)]>,
1033}