use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct ImageMime(pub String);
impl ImageMime {
#[must_use]
pub fn png() -> Self {
Self("image/png".into())
}
#[must_use]
pub fn jpeg() -> Self {
Self("image/jpeg".into())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ImageBlock {
pub mime_type: ImageMime,
pub data: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub media_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub filename: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub path: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
#[non_exhaustive]
pub enum ContentBlock {
Text {
text: String,
},
Image(ImageBlock),
}
impl ContentBlock {
#[must_use]
pub fn text(text: impl Into<String>) -> Self {
Self::Text { text: text.into() }
}
#[must_use]
pub fn as_text(&self) -> Option<&str> {
match self {
Self::Text { text } => Some(text.as_str()),
Self::Image(_) => None,
}
}
}
#[must_use]
pub fn join_text_blocks(blocks: &[ContentBlock]) -> String {
blocks
.iter()
.filter_map(ContentBlock::as_text)
.collect::<Vec<_>>()
.join("\n")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn serde_round_trip_text() {
let block = ContentBlock::text("hello");
let v = serde_json::to_value(&block).expect("ser");
let back: ContentBlock = serde_json::from_value(v).expect("de");
assert_eq!(back.as_text(), Some("hello"));
}
}