//! Convert WIT bindgen types to FlatBuffer wire-format payloads. //! //! This module bridges the gap between the Wasmtime component-model bindgen //! types (from `bex_core::engine::bex::plugin::common::*`) and the FlatBuffer //! wire types (from `bex_wire`). Each function takes WIT types from a plugin //! call result and produces a `Vec` FlatBuffer payload suitable for //! inclusion in a `BexEvent`. use bex_core::common as wit; use bex_wire::data::*; use bex_wire::builders; /// Convert WIT `HomeSection` vector → FlatBuffer `HomeResult` payload. pub fn home_to_flatbuffer(sections: &[wit::HomeSection]) -> Vec { let data: Vec = sections.iter().map(home_section_to_data).collect(); builders::build_home_result(data) } /// Convert WIT `PagedResult` → FlatBuffer `SearchResult` payload. pub fn search_to_flatbuffer(r: &wit::PagedResult) -> Vec { let items: Vec = r.items.iter().map(media_card_to_data).collect(); builders::build_search_result(items, r.next_page.clone()) } /// Convert WIT `MediaInfo` → FlatBuffer `InfoResult` payload. pub fn info_to_flatbuffer(info: &wit::MediaInfo) -> Vec { let data = media_info_to_data(info); builders::build_info_result(data) } /// Convert WIT `Server` vector → FlatBuffer `ServersResult` payload. pub fn servers_to_flatbuffer(servers: &[wit::Server]) -> Vec { let data: Vec = servers.iter().map(server_to_data).collect(); builders::build_servers_result(data) } /// Convert WIT `StreamSource` → FlatBuffer `StreamResult` payload. pub fn stream_to_flatbuffer(source: &wit::StreamSource) -> Vec { let data = stream_source_to_data(source); builders::build_stream_result(data) } // ── WIT → Data conversion helpers ────────────────────────────────── fn home_section_to_data(s: &wit::HomeSection) -> HomeSectionData { HomeSectionData { id: s.id.clone(), title: s.title.clone(), subtitle: s.subtitle.clone(), items: s.items.iter().map(media_card_to_data).collect(), next_page: s.next_page.clone(), layout: Some(format!("{:?}", s.layout).to_lowercase()), show_rank: s.show_rank, categories: s.categories.iter().map(category_link_to_data).collect(), extra: s.extra.iter().map(attr_to_data).collect(), } } fn media_card_to_data(c: &wit::MediaCard) -> MediaCardData { MediaCardData { id: c.id.clone(), title: c.title.clone(), kind: c.kind.as_ref().map(media_kind_to_u8).unwrap_or(10), images: c.images.as_ref().map(image_set_to_data), original_title: c.original_title.clone(), tagline: c.tagline.clone(), year: c.year.clone(), score: c.score.unwrap_or(0), genres: c.genres.clone(), status: c.status.as_ref().map(status_to_u8).unwrap_or(0), content_rating: c.content_rating.clone(), url: c.url.clone(), ids: c.ids.iter().map(linked_id_to_data).collect(), extra: c.extra.iter().map(attr_to_data).collect(), } } fn media_info_to_data(m: &wit::MediaInfo) -> MediaInfoData { MediaInfoData { id: m.id.clone(), title: m.title.clone(), kind: media_kind_to_u8(&m.kind), images: m.images.as_ref().map(image_set_to_data), original_title: m.original_title.clone(), description: m.description.clone(), score: m.score.unwrap_or(0), scored_by: m.scored_by.unwrap_or(0), year: m.year.clone(), release_date: m.release_date.clone(), genres: m.genres.clone(), tags: m.tags.clone(), status: m.status.as_ref().map(status_to_u8).unwrap_or(0), content_rating: m.content_rating.clone(), seasons: m.seasons.iter().map(season_to_data).collect(), cast: m.cast.iter().map(person_to_data).collect(), crew: m.crew.iter().map(person_to_data).collect(), runtime_minutes: m.runtime_minutes.unwrap_or(0), trailer_url: m.trailer_url.clone(), ids: m.ids.iter().map(linked_id_to_data).collect(), studio: m.studio.clone(), country: m.country.clone(), language: m.language.clone(), url: m.url.clone(), extra: m.extra.iter().map(attr_to_data).collect(), } } fn season_to_data(s: &wit::Season) -> SeasonData { SeasonData { id: s.id.clone(), title: s.title.clone(), number: s.number.unwrap_or(0.0), year: s.year.unwrap_or(0), episodes: s.episodes.iter().map(episode_to_data).collect(), } } fn episode_to_data(e: &wit::Episode) -> EpisodeData { EpisodeData { id: e.id.clone(), title: e.title.clone(), number: e.number.unwrap_or(0.0), season: e.season.unwrap_or(0.0), images: e.images.as_ref().map(image_set_to_data), description: e.description.clone(), released: e.released.clone(), score: e.score.unwrap_or(0), url: e.url.clone(), tags: e.tags.clone(), extra: e.extra.iter().map(attr_to_data).collect(), } } fn person_to_data(p: &wit::Person) -> PersonData { PersonData { id: p.id.clone(), name: p.name.clone(), image: p.image.as_ref().map(image_set_to_data), role: p.role.clone(), url: p.url.clone(), } } fn server_to_data(s: &wit::Server) -> ServerData { ServerData { id: s.id.clone(), label: Some(s.label.clone()), url: Some(s.url.clone()), priority: s.priority, extra: s.extra.iter().map(attr_to_data).collect(), } } fn stream_source_to_data(s: &wit::StreamSource) -> StreamSourceData { StreamSourceData { id: s.id.clone(), label: Some(s.label.clone()), format: stream_format_to_u8(&s.format), manifest_url: s.manifest_url.clone(), videos: s.videos.iter().map(video_track_to_data).collect(), subtitles: s.subtitles.iter().map(subtitle_track_to_data).collect(), headers: s.headers.iter().map(attr_to_data).collect(), extra: s.extra.iter().map(attr_to_data).collect(), } } fn video_track_to_data(v: &wit::VideoTrack) -> VideoTrackData { let label = v.resolution.label.clone(); VideoTrackData { resolution: Some(VideoResolutionData { width: v.resolution.width, height: v.resolution.height, hdr: v.resolution.hdr, label: if label.is_empty() { None } else { Some(label) }, }), url: v.url.clone(), mime_type: v.mime_type.clone(), bitrate: v.bitrate.unwrap_or(0), codecs: v.codecs.clone(), } } fn subtitle_track_to_data(s: &wit::SubtitleTrack) -> SubtitleTrackData { SubtitleTrackData { label: Some(s.label.clone()), url: s.url.clone(), language: s.language.clone(), format: s.format.clone(), } } fn image_set_to_data(s: &wit::ImageSet) -> ImageSetData { ImageSetData { low: s.low.as_ref().map(image_to_data), medium: s.medium.as_ref().map(image_to_data), high: s.high.as_ref().map(image_to_data), backdrop: s.backdrop.as_ref().map(image_to_data), logo: s.logo.as_ref().map(image_to_data), } } fn image_to_data(i: &wit::Image) -> ImageData { ImageData { url: i.url.clone(), layout: format!("{:?}", i.layout).to_lowercase(), width: i.width.unwrap_or(0), height: i.height.unwrap_or(0), blurhash: i.blurhash.clone(), } } fn category_link_to_data(c: &wit::CategoryLink) -> CategoryLinkData { CategoryLinkData { id: c.id.clone(), title: c.title.clone(), subtitle: c.subtitle.clone(), image: c.image.as_ref().map(image_to_data), } } fn linked_id_to_data(id: &wit::LinkedId) -> LinkedIdData { LinkedIdData { source: id.source.clone(), id: id.id.clone(), } } fn attr_to_data(a: &wit::Attr) -> AttrData { AttrData { key: a.key.clone(), value: a.value.clone(), } } fn media_kind_to_u8(k: &wit::MediaKind) -> u8 { match k { wit::MediaKind::Movie => 0, wit::MediaKind::Series => 1, wit::MediaKind::Anime => 2, wit::MediaKind::Short => 3, wit::MediaKind::Special => 4, wit::MediaKind::Documentary => 5, wit::MediaKind::Music => 6, wit::MediaKind::Podcast => 7, wit::MediaKind::Book => 8, wit::MediaKind::Live => 9, wit::MediaKind::Unknown => 10, } } fn status_to_u8(s: &wit::Status) -> u8 { match s { wit::Status::Unknown => 0, wit::Status::Upcoming => 1, wit::Status::Ongoing => 2, wit::Status::Completed => 3, wit::Status::Cancelled => 4, wit::Status::Paused => 5, } } fn stream_format_to_u8(f: &wit::StreamFormat) -> u8 { match f { wit::StreamFormat::Hls => 0, wit::StreamFormat::Dash => 1, wit::StreamFormat::Progressive => 2, wit::StreamFormat::Unknown => 3, } }