mistralrs_server/
util.rs

1use image::DynamicImage;
2use tokio::{
3    fs::{self, File},
4    io::AsyncReadExt,
5};
6
7pub async fn parse_image_url(url_unparsed: &str) -> Result<DynamicImage, anyhow::Error> {
8    let url = if let Ok(url) = url::Url::parse(url_unparsed) {
9        url
10    } else if File::open(url_unparsed).await.is_ok() {
11        url::Url::from_file_path(std::path::absolute(url_unparsed)?)
12            .map_err(|_| anyhow::anyhow!("Could not parse file path: {}", url_unparsed))?
13    } else {
14        url::Url::parse(&format!("data:image/png;base64,{}", url_unparsed))
15            .map_err(|_| anyhow::anyhow!("Could not parse as base64 data: {}", url_unparsed))?
16    };
17
18    let bytes = if url.scheme() == "http" || url.scheme() == "https" {
19        // Read from http
20        match reqwest::get(url.clone()).await {
21            Ok(http_resp) => http_resp.bytes().await?.to_vec(),
22            Err(e) => anyhow::bail!(e),
23        }
24    } else if url.scheme() == "file" {
25        let path = url
26            .to_file_path()
27            .map_err(|_| anyhow::anyhow!("Could not parse file path: {}", url))?;
28
29        if let Ok(mut f) = File::open(&path).await {
30            // Read from local file
31            let metadata = fs::metadata(&path).await?;
32            let mut buffer = vec![0; metadata.len() as usize];
33            f.read_exact(&mut buffer).await?;
34            buffer
35        } else {
36            anyhow::bail!("Could not open file at path: {}", url);
37        }
38    } else if url.scheme() == "data" {
39        // Decode with base64
40        let data_url = data_url::DataUrl::process(url.as_str())?;
41        data_url.decode_to_vec()?.0
42    } else {
43        anyhow::bail!("Unsupported URL scheme: {}", url.scheme());
44    };
45
46    Ok(image::load_from_memory(&bytes)?)
47}
48
49#[cfg(test)]
50mod tests {
51    use image::GenericImageView;
52
53    use super::*;
54
55    #[tokio::test]
56    async fn test_parse_image_url() {
57        // from URL
58        let url = "https://www.rust-lang.org/logos/rust-logo-32x32.png";
59        let image = parse_image_url(url).await.unwrap();
60        assert_eq!(image.dimensions(), (32, 32));
61
62        let url = "http://www.rust-lang.org/logos/rust-logo-32x32.png";
63        let image = parse_image_url(url).await.unwrap();
64        assert_eq!(image.dimensions(), (32, 32));
65
66        // from file path
67        let url = "resources/rust-logo-32x32.png";
68        let image = parse_image_url(url).await.unwrap();
69        assert_eq!(image.dimensions(), (32, 32));
70
71        // URL must be an absolute path
72        let absolute_path = std::path::absolute(url).unwrap();
73        let url = format!("file://{}", absolute_path.as_os_str().to_str().unwrap());
74        let image = parse_image_url(&url).await.unwrap();
75        assert_eq!(image.dimensions(), (32, 32));
76
77        // from base64 encoded image (rust-logo-32x32.png)
78        let url = "
79        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
114        ";
115        let image = parse_image_url(url).await.unwrap();
116        assert_eq!(image.dimensions(), (32, 32));
117
118        let url = format!("data:image/png;base64,{}", url);
119        let image = parse_image_url(&url).await.unwrap();
120        assert_eq!(image.dimensions(), (32, 32));
121    }
122}