krystv's picture
Upload 107 files
3374e90 verified
//! 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<u8>` 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<u8> {
let data: Vec<HomeSectionData> = 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<u8> {
let items: Vec<MediaCardData> = 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<u8> {
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<u8> {
let data: Vec<ServerData> = 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<u8> {
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,
}
}