mochi_rs/structs/
meta.rs

1extern crate alloc;
2
3use alloc::string::ToString;
4use alloc::vec;
5use alloc::{string::String, vec::Vec};
6
7use crate::imports::error::Result;
8use crate::imports::{PtrRef, ArrayRef};
9
10use super::conversion::optional_str_ptr;
11
12#[link(wasm_import_module = "structs_meta")]
13extern "C" {
14    fn create_search_filter_option(
15        option_id_ptr: i32,
16        option_id_len: i32,
17        name_ptr: i32, 
18        name_len: i32
19    ) -> i32;
20
21    fn create_search_filter(
22        id_ptr: i32, 
23        id_len: i32, 
24        name_ptr: i32, 
25        name_len: i32, 
26        options_arr_ref: i32,
27        multiselect: bool,
28        required: bool
29    ) -> i32;
30
31    fn create_paging(
32        id_ptr: i32,
33        id_len: i32,
34        previous_page_ptr: i32,
35        previous_page_len: i32,
36        next_page_ptr: i32,
37        next_page_len: i32,
38        items_ptr: i32
39    ) -> i32;
40
41    fn create_discover_listing(
42        title_ptr: i32,
43        title_len: i32,
44        lising_type: i32,
45        paging_ptr: i32
46    ) -> i32;
47
48    fn create_playlist(
49        id_ptr: i32,
50        id_len: i32,
51        title_ptr: i32,
52        title_len: i32,
53        poster_image_ptr: i32,
54        poster_image_len: i32,
55        banner_image_ptr: i32,
56        banner_image_len: i32,
57        playlist_type: PlaylistType
58    ) -> i32;
59
60    fn create_playlist_details(
61        description_ptr: i32,
62        description_len: i32,
63        alternative_titles_ptr: i32,
64        alternative_posters_ptr: i32,
65        alternative_banners_ptr: i32,
66        genres_ptr: i32,
67        year_released: i32,
68        ratings: i32,
69        previews_ptr: i32
70    ) -> i32;
71
72    fn create_playlist_preview(
73        title_ptr: i32,
74        title_len: i32,
75        description_ptr: i32,
76        description_len: i32,
77        thumbnail_ptr: i32,
78        thumbnail_len: i32,
79        link_ptr: i32,
80        link_len: i32,
81        preview_type: PlaylistPreviewType
82    ) -> i32;
83
84    fn create_playlist_item(
85        id_ptr: i32,
86        id_len: i32,
87        title_ptr: i32,
88        title_len: i32,
89        description_ptr: i32,
90        description_len: i32,
91        thumbnail_ptr: i32,
92        thumbnail_len: i32,
93        number: f64,
94        timestamp_ptr: i32,
95        timestamp_len: i32,
96        tags_ptr: i32
97    ) -> i32;
98
99    fn create_playlist_items_response(
100        contents_ptr: i32,
101        all_groups_ptr: i32
102    ) -> i32;
103
104    fn create_playlist_group(
105        id: f64,
106        display_title_ptr: i32,
107        display_title_len: i32
108    ) -> i32;
109
110    fn create_playlist_group_page(
111        id_ptr: i32,
112        id_len: i32,
113        display_name_ptr: i32,
114        display_name_len: i32
115    ) -> i32;
116
117    fn create_playlist_group_items(
118        group_id: f64,
119        pagings_ptr: i32,
120        all_pages_ptr: i32
121    ) -> i32;
122}
123
124pub trait Meta {
125    fn search_filters() -> SearchFilters;
126    fn search(search_query: SearchQuery) -> Result<Paging<Playlist>>;
127    fn discover_listings() -> Result<DiscoverListings>;
128    fn playlist_details(id: String) -> Result<PlaylistDetails>;
129}
130
131pub type PlaylistID = String;
132
133#[derive(Debug, Clone)]
134pub struct Playlist {
135    pub id: PlaylistID,
136    pub title: Option<String>,
137    /// Valid URL Image
138    pub poster_image: Option<String>,
139    /// Valid URL Image
140    pub banner_image: Option<String>,
141    /// Valid url string
142    pub url: String,
143    /// Indicates the status of the playlist
144    pub status: PlaylistStatus,
145    pub playlist_type: PlaylistType
146}
147
148#[repr(C)]
149#[derive(PartialEq, Eq, Debug, Clone, Copy)]
150pub enum PlaylistStatus {
151    Unknown,
152    Upcoming,
153    Ongoing,
154    Completed,
155    Paused,
156    Cancelled,
157}
158
159#[repr(C)]
160#[derive(PartialEq, Eq, Debug, Clone, Copy)]
161pub enum PlaylistType {
162    Video,
163    Image,
164    Text
165}
166
167#[derive(Debug, Clone)]
168pub struct PlaylistDetails {
169    pub description: Option<String>,
170    pub alternative_titles: Vec<String>,
171    /// array of valid urls
172    pub alternative_posters: Vec<String>,
173    /// array of valid urls
174    pub alternative_banners: Vec<String>,
175    pub genres: Vec<String>,
176    pub year_released: Option<i32>,
177    /// 0...10
178    pub ratings: Option<i32>,
179    pub previews: Vec<PlaylistPreview>
180}
181
182#[derive(Debug, Clone)]
183pub struct PlaylistPreview {
184    pub title: Option<String>,
185    pub description: Option<String>,
186    pub thumbnail: Option<String>,
187    /// Must be a valid url string
188    pub link: String,
189    pub preview_type: PlaylistPreviewType
190}
191
192#[repr(C)]
193#[derive(PartialEq, Eq, Debug, Clone, Copy)]
194pub enum PlaylistPreviewType {
195    Video,
196    Image
197}
198
199pub type PlaylistItemID = String;
200
201#[derive(Debug, Clone, PartialEq, PartialOrd)]
202pub struct PlaylistItem {
203    pub id: PlaylistItemID,
204    pub title: Option<String>,
205    pub description: Option<String>,
206    pub thumbnail: Option<String>,
207    pub number: f64,
208    pub timestamp: Option<String>,
209    pub tags: Vec<String>,
210}
211
212#[derive(Debug, Clone)]
213pub struct PlaylistItemsRequest {
214    pub playlist_id: PlaylistID,
215    pub group_id: Option<f64>,
216    pub page_id: Option<String>,
217    pub item_id: Option<String>
218}
219
220#[derive(Debug, Clone)]
221pub struct PlaylistItemsResponse {
222    pub contents: Vec<PlaylistGroupContent>,
223    pub all_groups: Vec<PlaylistGroup>
224}
225
226#[derive(Debug, Clone)]
227pub struct PlaylistGroup {
228    pub id: f64,
229    pub display_title: Option<String>
230}
231
232#[derive(Debug, Clone)]
233pub struct PlaylistGroupPage {
234    pub id: String,
235    pub display_name: String
236}
237
238#[derive(Debug, Clone)]
239pub struct PlaylistGroupContent {
240    pub group_id: f64,
241    pub pagings: Vec<Paging<PlaylistItem>>,
242    pub all_pages: Vec<PlaylistGroupPage>
243}
244
245#[derive(Debug, Clone)]
246pub struct Paging<T> {
247    pub id: String,
248    pub previous_page: Option<String>,
249    pub next_page: Option<String>,
250    pub items: Vec<T>
251}
252
253#[derive(Debug, Clone)]
254pub struct DiscoverListing {
255    pub title: String,
256    pub listing_type: DiscoverListingType,
257    pub paging: Paging<Playlist>
258}
259
260#[repr(C)]
261#[derive(PartialEq, Eq, Debug, Clone, Copy)]
262pub enum DiscoverListingType {
263    Default,
264    Rank,
265    Featured
266}
267
268#[derive(Debug, Clone)]
269pub struct DiscoverListings(pub Vec<DiscoverListing>);
270
271#[derive(Debug, Clone)]
272pub struct SearchQuery {
273    pub query: String,
274    pub filters: Vec<SearchQueryFilter>,
275    pub page: Option<String>
276}
277
278#[derive(Debug, Clone)]
279pub struct SearchQueryFilter {
280    pub filter_id: String,
281    pub option_id: String
282}
283
284#[derive(Debug, Clone)]
285pub struct SearchFilters {
286    pub filters: Vec<SearchFilter>
287}
288
289#[derive(Debug, Clone)]
290pub struct SearchFilter {
291    pub filter_id: String,
292    pub display_name: String,
293    pub options: Vec<SearchFilterOption>,
294    pub multiselect: bool,
295    pub required: bool
296}
297
298#[derive(Debug, Clone)]
299pub struct SearchFilterOption {
300    pub option_id: String,
301    pub display_name: String
302}
303
304impl From<SearchFilters> for PtrRef {
305    fn from(value: SearchFilters) -> PtrRef {
306        let array_ref = ArrayRef::from(value.filters);
307        let filters_array_ref_ptr = array_ref.ptr();
308        core::mem::forget(array_ref);
309        Self::new(filters_array_ref_ptr)
310    }
311}
312
313impl From<SearchFilter> for PtrRef {
314    fn from(value: SearchFilter) -> Self {
315        let array_ref = ArrayRef::from(value.options);
316        let options_array_ref = array_ref.ptr();
317        core::mem::forget(array_ref);
318
319        let ptr = unsafe {
320            create_search_filter(
321                value.filter_id.as_ptr() as i32,
322                value.filter_id.len() as i32,
323                value.display_name.as_ptr() as i32,
324                value.display_name.len() as i32,
325                options_array_ref,
326                value.multiselect,
327                value.required
328            )
329        };
330        Self::new(ptr)
331    }
332}
333
334impl From<SearchFilterOption> for PtrRef {
335    fn from(value: SearchFilterOption) -> Self {
336        Self::new(
337            unsafe {
338                create_search_filter_option(
339                    value.option_id.as_ptr() as i32,
340                    value.option_id.len() as i32,
341                    value.display_name.as_ptr() as i32,
342                    value.display_name.len() as i32
343                )
344            }    
345        )
346    }
347}
348
349impl Into<SearchQuery> for PtrRef {
350    fn into(self) -> SearchQuery {
351        if self.is_some() {
352            let reference = self.as_object();
353            if let Ok(search_query_ref) = reference {
354                let query = search_query_ref.get("query")
355                    .as_string()
356                    .unwrap_or_default();
357                let mut filters: Vec<SearchQueryFilter> = Vec::new();
358                if let Ok(filters_arr) = search_query_ref.get("filters").as_array() {
359                    for item in filters_arr {
360                        let filter_ref = match item.as_object() {
361                            Ok(filter_ref) => filter_ref,
362                            _ => continue,
363                        };
364                        let filter_id = match filter_ref.get("id").as_string() {
365                            Ok(name) => name,
366                            _ => continue,
367                        };
368                        let option_id = match filter_ref.get("optionId").as_string() {
369                            Ok(value) => value,
370                            _ => continue,
371                        };
372                        filters.push(
373                            SearchQueryFilter { 
374                                filter_id,
375                                option_id
376                            }
377                        )
378                    }
379                }
380                let page = search_query_ref.get("page").as_string().ok();
381                return SearchQuery {
382                    query,
383                    filters,
384                    page,
385                }
386            }
387        }
388        SearchQuery {
389            query: "".to_string(),
390            filters: vec![],
391            page: None,
392        }
393    }
394}
395
396impl<T> From<Paging<T>> for PtrRef where PtrRef: From<T> {
397    fn from(value: Paging<T>) -> Self {
398        let items = ArrayRef::from(value.items);
399        let items_ptr = items.ptr();
400        core::mem::forget(items);
401
402        let previous_page = optional_str_ptr(value.previous_page);
403        let next_page = optional_str_ptr(value.next_page);
404
405        let obj_ptr = unsafe {
406            create_paging(
407                value.id.as_ptr() as i32, 
408                value.id.len() as i32, 
409                previous_page.0, 
410                previous_page.1, 
411                next_page.0,
412                next_page.1,
413                items_ptr
414            ) 
415        };
416        Self::new(obj_ptr)
417    }
418}
419
420impl From<DiscoverListing> for PtrRef {
421    fn from(value: DiscoverListing) -> Self {
422        let title = value.title;
423        let listing_type = value.listing_type;
424        let paging: PtrRef = value.paging.into();
425        let paging_ptr = paging.pointer();
426        core::mem::forget(paging);
427
428        let obj_ptr = unsafe {
429            create_discover_listing(
430                title.as_ptr() as i32, 
431                title.len() as i32, 
432                listing_type as i32,
433                paging_ptr
434            )
435        };
436
437        Self::new(obj_ptr)
438    }
439}
440
441impl From<DiscoverListings> for PtrRef {
442    fn from(value: DiscoverListings) -> Self {
443        let array_ref = ArrayRef::from(value.0);
444        let array_ref_ptr = array_ref.ptr();
445        core::mem::forget(array_ref);
446        Self::new(array_ref_ptr)
447    }
448}
449
450impl From<Playlist> for PtrRef {
451    fn from(value: Playlist) -> Self {
452        let id = value.id;
453        let title = optional_str_ptr(value.title);
454        let poster_image = optional_str_ptr(value.poster_image);
455        let banner_image = optional_str_ptr(value.banner_image);
456        let host_ptr = unsafe {
457            create_playlist(
458                id.as_ptr() as i32, 
459                id.len() as i32,
460                title.0,
461                title.1,
462                poster_image.0,
463                poster_image.1,
464                banner_image.0,
465                banner_image.1,
466                value.playlist_type
467            )
468        };
469        Self::new(host_ptr)
470    }
471}
472
473impl From<PlaylistDetails> for PtrRef {
474    fn from(value: PlaylistDetails) -> Self {
475        let description = optional_str_ptr(value.description);
476
477        let alternative_titles = ArrayRef::from(value.alternative_titles);
478        let alternative_titles_ptr = alternative_titles.ptr();
479        core::mem::forget(alternative_titles);
480
481        let alternative_posters = ArrayRef::from(value.alternative_posters);
482        let alternative_posters_ptr = alternative_posters.ptr();
483        core::mem::forget(alternative_posters);
484
485        let alternative_banners = ArrayRef::from(value.alternative_banners);
486        let alternative_banners_ptr = alternative_banners.ptr();
487        core::mem::forget(alternative_banners);
488
489        let genres = ArrayRef::from(value.genres);
490        let genres_ptr = genres.ptr();
491        core::mem::forget(genres);
492
493        let previews = ArrayRef::from(value.previews);
494        let previews_ptr = previews.ptr();
495        core::mem::forget(previews);
496
497        let host_ptr = unsafe {
498            create_playlist_details(
499                description.0, 
500                description.1, 
501                alternative_titles_ptr, 
502                alternative_posters_ptr,
503                alternative_banners_ptr, 
504                genres_ptr, 
505                value.year_released.unwrap_or(-1), 
506                value.ratings.unwrap_or(-1), 
507                previews_ptr
508            )
509        };
510
511        Self::new(host_ptr)
512    }
513}
514
515impl From<PlaylistPreview> for PtrRef {
516    fn from(value: PlaylistPreview) -> Self {
517        let title = optional_str_ptr(value.title);
518        let description = optional_str_ptr(value.description);
519
520        let thumbnail = optional_str_ptr(value.thumbnail);
521
522        let host_ptr = unsafe {
523            create_playlist_preview(
524                title.0, 
525                title.1, 
526                description.0, 
527                description.1, 
528                thumbnail.0, 
529                thumbnail.1,
530                value.link.as_ptr() as i32,
531                value.link.len() as i32, 
532                value.preview_type
533            )
534        };
535        PtrRef::from(host_ptr)
536    }
537}
538
539impl From<PlaylistItem> for PtrRef {
540    fn from(value: PlaylistItem) -> Self {
541        let title = optional_str_ptr(value.title);
542        let description = optional_str_ptr(value.description);
543        let thumbnail = optional_str_ptr(value.thumbnail);
544        let timestamp = optional_str_ptr(value.timestamp);
545
546        let tags = ArrayRef::from(value.tags);
547        let tags_ptr = tags.ptr();
548        core::mem::forget(tags);
549
550        let host_ptr = unsafe {
551            create_playlist_item(
552                value.id.as_ptr() as i32,
553                value.id.len() as i32,
554                title.0,
555                title.1,
556                description.0,
557                description.1,
558                thumbnail.0,
559                thumbnail.1,
560                value.number, 
561                timestamp.0, 
562                timestamp.1, 
563                tags_ptr
564            )
565        };
566        Self::from(host_ptr)
567    }
568}
569
570impl Into<PlaylistItemsRequest> for PtrRef {
571    fn into(self) -> PlaylistItemsRequest {
572        if let Ok(reference) = self.as_object() {
573            let playlist_id = reference.get("playlistId")
574                .as_string()
575                .unwrap_or_default();
576
577            let group_id = reference.get("groupId").as_float().ok();
578            let page_id = reference.get("pageId").as_string().ok();
579            let item_id = reference.get("itemId").as_string().ok();
580
581            PlaylistItemsRequest {
582                playlist_id,
583                group_id,
584                page_id,
585                item_id,
586            }
587        } else {
588            PlaylistItemsRequest {
589                playlist_id: "".to_string(),
590                group_id: None,
591                page_id: None,
592                item_id: None,
593            }
594        }
595    }
596}
597
598impl From<PlaylistItemsResponse> for PtrRef {
599    fn from(value: PlaylistItemsResponse) -> Self {
600        let contents = ArrayRef::from(value.contents);
601        let contents_ptr = contents.ptr();
602        core::mem::forget(contents);
603
604        let all_groups = ArrayRef::from(value.all_groups);
605        let all_groups_ptr = all_groups.ptr();
606        core::mem::forget(all_groups);
607
608        Self::new(unsafe { create_playlist_items_response(contents_ptr, all_groups_ptr) } )
609    }
610}
611
612impl From<PlaylistGroup> for PtrRef {
613    fn from(value: PlaylistGroup) -> Self {
614        let display_title = optional_str_ptr(value.display_title);
615        Self::new(
616            unsafe {
617                create_playlist_group(
618                    value.id, 
619                    display_title.0,
620                    display_title.1
621                )
622            }
623        )
624    }
625}
626
627impl From<PlaylistGroupPage> for PtrRef {
628    fn from(value: PlaylistGroupPage) -> Self {
629        Self::new(
630            unsafe {
631                create_playlist_group_page(
632                    value.id.as_ptr() as i32,
633                    value.id.len() as i32,
634                    value.display_name.as_ptr() as i32,
635                    value.display_name.len() as i32
636                )
637            }
638        )
639    }
640}
641
642impl From<PlaylistGroupContent> for PtrRef {
643    fn from(value: PlaylistGroupContent) -> Self {
644        let pagings = ArrayRef::from(value.pagings);
645        let pagings_ptr = pagings.ptr();
646        core::mem::forget(pagings);
647
648        let all_pages = ArrayRef::from(value.all_pages);
649        let all_pages_ptr = all_pages.ptr();
650        core::mem::forget(all_pages);
651
652        Self::new(
653            unsafe {
654                create_playlist_group_items(
655                    value.group_id, 
656                    pagings_ptr,
657                    all_pages_ptr
658                )
659            }
660        )
661    }
662}