use std::ffi::c_void;
use super::{RawObject, RawObjectTrait};
use crate::{baml_unreachable, proto::baml_cffi_v1::BamlObjectType};
use serde::{Deserialize, Serialize};
macro_rules! define_media_type {
(
$(#[$meta:meta])*
$name:ident => $object_type:ident
) => {
define_raw_object_wrapper! {
$(#[$meta])*
$name => $object_type
}
impl $name {
pub fn from_url(runtime: *const c_void, url: &str, mime_type: Option<&str>) -> Self {
let raw = RawObject::new(
runtime,
BamlObjectType::$object_type,
(("url", url), ("mime_type", mime_type)),
)
.unwrap_or_else(|e| baml_unreachable!("Failed to create {} from URL: {}", stringify!($name), e));
Self { raw }
}
pub fn from_base64(runtime: *const c_void, base64: &str, mime_type: Option<&str>) -> Self {
let raw = RawObject::new(
runtime,
BamlObjectType::$object_type,
(("base64", base64), ("mime_type", mime_type)),
)
.unwrap_or_else(|e| baml_unreachable!("Failed to create {} from base64: {}", stringify!($name), e));
Self { raw }
}
pub fn mime_type(&self) -> Option<String> {
self.raw.call_method("mime_type", ())
}
pub fn is_url(&self) -> bool {
self.raw.call_method("is_url", ())
}
pub fn is_base64(&self) -> bool {
self.raw.call_method("is_base64", ())
}
pub fn as_url(&self) -> Option<String> {
self.raw.call_method("as_url", ())
}
pub fn as_base64(&self) -> Option<String> {
self.raw.call_method("as_base64", ())
}
}
impl std::fmt::Debug for $name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct(stringify!($name)).finish_non_exhaustive()
}
}
impl Default for $name {
fn default() -> Self {
unreachable!("Media types cannot be default-constructed; they require a runtime")
}
}
impl $crate::BamlDecode for $name {
fn baml_decode(
_holder: &$crate::__internal::CffiValueHolder
) -> ::core::result::Result<Self, $crate::BamlError> {
Err($crate::BamlError::internal(
concat!("Cannot decode ", stringify!($name), " without runtime context")
))
}
}
impl serde::Serialize for $name {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let content = if let Some(base64) = self.as_base64() {
BamlMediaReprContent::Base64 { base64 }
} else if let Some(url) = self.as_url() {
BamlMediaReprContent::Url { url }
} else {
return Err(serde::ser::Error::custom(
"media cannot be serialized: was not URL or base64",
));
};
BamlMediaRepr {
mime_type: self.mime_type(),
content,
}
.serialize(serializer)
}
}
impl<'de> serde::Deserialize<'de> for $name {
fn deserialize<D: serde::Deserializer<'de>>(_deserializer: D) -> Result<Self, D::Error> {
Err(serde::de::Error::custom(format!("Cannot deserialize {} directly", stringify!($name))))
}
}
};
}
define_media_type! {
Image => ObjectMediaImage
}
define_media_type! {
Audio => ObjectMediaAudio
}
define_media_type! {
Pdf => ObjectMediaPdf
}
define_media_type! {
Video => ObjectMediaVideo
}
#[derive(Debug, Serialize, Deserialize)]
pub struct BamlMediaRepr {
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "media_type")]
pub mime_type: Option<String>,
#[serde(flatten)]
pub content: BamlMediaReprContent,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum BamlMediaReprContent {
Url { url: String },
Base64 { base64: String },
}