gemini_adk_rs/tool/
media.rs1use base64::Engine as _;
23
24pub const MEDIA_KEY: &str = "_media";
26
27#[derive(Debug, Clone, PartialEq, Eq)]
29pub struct MediaAttachment {
30 pub mime_type: String,
32 pub data_base64: String,
34}
35
36pub fn attach(result: &mut serde_json::Value, mime_type: &str, data: &[u8]) {
38 let encoded = base64::engine::general_purpose::STANDARD.encode(data);
39 let entry = serde_json::json!({"mime_type": mime_type, "data": encoded});
40 match result {
41 serde_json::Value::Object(map) => {
42 if let Some(list) = map
43 .entry(MEDIA_KEY)
44 .or_insert_with(|| serde_json::Value::Array(Vec::new()))
45 .as_array_mut()
46 {
47 list.push(entry);
48 }
49 }
50 other => {
51 let wrapped = serde_json::json!({"result": other.take(), MEDIA_KEY: [entry]});
53 *other = wrapped;
54 }
55 }
56}
57
58pub fn extract(result: &mut serde_json::Value) -> Vec<MediaAttachment> {
62 let Some(map) = result.as_object_mut() else {
63 return Vec::new();
64 };
65 let Some(raw) = map.remove(MEDIA_KEY) else {
66 return Vec::new();
67 };
68 raw.as_array()
69 .into_iter()
70 .flatten()
71 .filter_map(|entry| {
72 Some(MediaAttachment {
73 mime_type: entry.get("mime_type")?.as_str()?.to_string(),
74 data_base64: entry.get("data")?.as_str()?.to_string(),
75 })
76 })
77 .collect()
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83 use serde_json::json;
84
85 #[test]
86 fn attach_then_extract_round_trips() {
87 let mut result = json!({"ok": true});
88 attach(&mut result, "image/png", b"\x89PNG");
89 attach(&mut result, "image/jpeg", b"\xff\xd8");
90 let media = extract(&mut result);
91 assert_eq!(media.len(), 2);
92 assert_eq!(media[0].mime_type, "image/png");
93 assert_eq!(
94 media[0].data_base64,
95 base64::engine::general_purpose::STANDARD.encode(b"\x89PNG")
96 );
97 assert_eq!(result, json!({"ok": true}));
99 }
100
101 #[test]
102 fn non_object_results_are_wrapped() {
103 let mut result = json!("plain text");
104 attach(&mut result, "image/png", b"x");
105 assert_eq!(result["result"], json!("plain text"));
106 assert_eq!(extract(&mut result).len(), 1);
107 }
108
109 #[test]
110 fn extract_without_media_is_a_no_op() {
111 let mut result = json!({"ok": true});
112 assert!(extract(&mut result).is_empty());
113 assert_eq!(result, json!({"ok": true}));
114 }
115}