gemini_client_api/gemini/
utils.rs

1use super::types::request::*;
2use crate::utils::{self, MatchedFiles};
3use regex::Regex;
4use reqwest::header::HeaderMap;
5
6///Converts markdown to parts considering `![image](link)` means Gemini will be see the images too. `link` can be URL or file path.  
7pub struct MarkdownToParts<'a> {
8    base64s: Vec<MatchedFiles>,
9    markdown: &'a str,
10}
11impl<'a> MarkdownToParts<'a> {
12    ///# Panics `regex` must have a Regex with only 1 capture group with file URL as first capture
13    ///group, else it PANICS.
14    /// # Arguments
15    /// `guess_mime_type` is used to detect mimi_type of URL pointing to file system or web resource
16    /// with no "Content-Type" header.
17    /// `decice_download` is used to decide if to download. If it returns false, resource will not
18    /// be fetched and won't be in `parts`
19    /// # Example
20    /// ```ignore
21    /// from_regex("Your markdown string...", Regex::new(r"(?s)!\[.*?].?\((.*?)\)").unwrap(), |_| mime::IMAGE_PNG, |_| true)
22    /// ```
23    pub async fn from_regex_checked(
24        markdown: &'a str,
25        regex: Regex,
26        guess_mime_type: fn(url: &str) -> mime::Mime,
27        decice_download: fn(headers: &HeaderMap) -> bool,
28    ) -> Self {
29        Self {
30            base64s: utils::get_file_base64s(markdown, regex, guess_mime_type, decice_download)
31                .await,
32            markdown,
33        }
34    }
35    ///# Panics
36    /// `regex` must have a Regex with only 1 capture group with file URL as first capture group, else it PANICS.
37    /// # Arguments
38    /// `guess_mime_type` is used to detect mimi_type of URL pointing to file system or web resource
39    /// with no "Content-Type" header.
40    /// # Example
41    /// ```ignore
42    /// from_regex("Your markdown string...", Regex::new(r"(?s)!\[.*?].?\((.*?)\)").unwrap(), |_|
43    /// mime::IMAGE_PNG)
44    /// ```
45    pub async fn from_regex(
46        markdown: &'a str,
47        regex: Regex,
48        guess_mime_type: fn(url: &str) -> mime::Mime,
49    ) -> Self {
50        Self::from_regex_checked(markdown, regex, guess_mime_type, |_| true).await
51    }
52    /// # Arguments
53    /// `guess_mime_type` is used to detect mimi_type of URL pointing to file system or web resource
54    /// with no "Content-Type" header.
55    /// `decice_download` is used to decide if to download. If it returns false, resource will not
56    /// be fetched and won't be in `parts`
57    /// # Example
58    /// ```ignore
59    /// new("Your markdown string...", |_| mime::IMAGE_PNG, |_| true)
60    /// ```
61    pub async fn new_checked(
62        markdown: &'a str,
63        guess_mime_type: fn(url: &str) -> mime::Mime,
64        decice_download: fn(headers: &HeaderMap) -> bool,
65    ) -> Self {
66        let image_regex = Regex::new(r"(?s)!\[.*?].?\((.*?)\)").unwrap();
67        Self::from_regex_checked(markdown, image_regex, guess_mime_type, decice_download).await
68    }
69    /// # Arguments
70    /// `guess_mime_type` is used to detect mimi_type of URL pointing to file system or web resource
71    /// with no "Content-Type" header.
72    /// # Example
73    /// ```ignore
74    /// new("Your markdown string...", |_| mime::IMAGE_PNG)
75    /// ```
76    pub async fn new(markdown: &'a str, guess_mime_type: fn(url: &str) -> mime::Mime) -> Self {
77        Self::new_checked(markdown, guess_mime_type, |_| true).await
78    }
79    pub fn process(mut self) -> Vec<Part> {
80        let mut parts: Vec<Part> = Vec::new();
81        let mut removed_length = 0;
82        for file in self.base64s {
83            if let MatchedFiles {
84                index,
85                length,
86                mime_type: Some(mime_type),
87                base64: Some(base64),
88            } = file
89            {
90                let end = index + length - removed_length;
91                let text = &self.markdown[..end];
92                parts.push(Part::text(text.to_string()));
93                parts.push(Part::inline_data(InlineData::new(mime_type, base64)));
94
95                self.markdown = &self.markdown[end..];
96                removed_length += end;
97            }
98        }
99        if self.markdown.len() != 0 {
100            parts.push(Part::text(self.markdown.to_string()));
101        }
102        parts
103    }
104}