gemini_client_api/
utils.rs1use actix_web::HttpMessage;
2use awc::Client;
3use base64::{Engine, engine::general_purpose::STANDARD};
4use futures::future::join_all;
5use regex::Regex;
6use std::time::Duration;
7
8const REQ_TIMEOUT: Duration = Duration::from_secs(10);
9
10pub struct MatchedFiles {
11 pub index: usize,
12 pub length: usize,
13 pub mime_type: String,
14 pub base64: String,
15}
16pub async fn get_file_base64s(
21 markdown: &str,
22 regex: Regex,
23 guess_mime_type: fn(url: &str) -> String,
24) -> Vec<Option<MatchedFiles>> {
25 let client = Client::builder().timeout(REQ_TIMEOUT).finish();
26 let mut tasks: Vec<_> = Vec::new();
27
28 for file in regex.captures_iter(&markdown) {
29 let url = file[1].to_string();
30 let capture = file.get(0).unwrap();
31 tasks.push((async |capture: regex::Match<'_>| {
32 let (mime_type, base64) = if url.starts_with("https://") || url.starts_with("http://") {
33 let response = client.get(&url).send().await;
34 let mut data = match response {
35 Err(_) => return None,
36 Ok(data) => data,
37 };
38 (
39 data.mime_type()
40 .ok()
41 .flatten()
42 .map(|mime| mime.to_string())
43 .unwrap_or_else(|| guess_mime_type(&url)),
44 data.body().await.ok().map(|bytes| STANDARD.encode(bytes)),
45 )
46 } else {
47 (
48 guess_mime_type(&url),
49 tokio::fs::read(url)
50 .await
51 .ok()
52 .map(|bytes| STANDARD.encode(&bytes)),
53 )
54 };
55 if let Some(base64) = base64 {
56 Some(MatchedFiles {
57 index: capture.start(),
58 length: capture.len(),
59 mime_type,
60 base64,
61 })
62 } else {
63 None
64 }
65 })(capture));
66 }
67 join_all(tasks).await
68}