fx-mistral 0.0.2

A library to leverage the Mistral API for OCR and data extraction from PDFs.
Documentation
use reqwest::multipart;
use serde::{Deserialize, Serialize};

use crate::{MistralClient, MistralError, MistralApiError};

#[derive(Serialize, Deserialize, Debug)]
pub struct FileData {
    pub id: String,
    object: String,
    bytes: u64,
    created_at: u64,
    filename: String,
    purpose: String,
    sample_type: String,
    num_lines: Option<u64>,
    source: String,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct SignedUrl {
    pub url: String,
}

pub struct FileClient<'a> {
    mistral_client: &'a MistralClient,
    files_path: String,
}

impl<'a> FileClient<'a> {
    pub fn new(mistral_client: &'a MistralClient) -> Self {
        FileClient {
            mistral_client,
            files_path: "files".to_string(),
        }
    }

    pub async fn upload_file(&self, file_data: Vec<u8>) -> Result<FileData, MistralError> {
        let part = multipart::Part::bytes(file_data).file_name("uploaded_file.pdf");
        let form = multipart::Form::new()
            .text("purpose", "ocr")
            .part("file", part);

        let response = self
            .mistral_client
            .client
            .post(&format!("{}/{}", self.mistral_client.base_url, self.files_path))
            .bearer_auth(&self.mistral_client.api_key)
            .multipart(form)
            .send()
            .await
            .map_err(MistralError::Network)?;

        let status = response.status();
        let response_text = response.text().await.map_err(MistralError::Network)?;

        if !status.is_success() {
            return match serde_json::from_str::<MistralApiError>(&response_text) {
                Ok(mut err) => {
                    err.description = crate::error_description(err.code).to_string();
                    Err(MistralError::Api(err))
                },
                Err(_) => Err(MistralError::Http(status)),
            };
        }

        serde_json::from_str(&response_text).map_err(MistralError::Parse)
    }

    pub async fn get_signed_url(&self, file_id: &str) -> Result<SignedUrl, MistralError> {
        let call_url = format!(
            "{}/{}/{}/url?expiry=24",
            self.mistral_client.base_url, self.files_path, file_id
        );

        let response = self
            .mistral_client
            .client
            .get(call_url)
            .bearer_auth(&self.mistral_client.api_key)
            .send()
            .await
            .map_err(MistralError::Network)?;

        let status = response.status();
        let response_text = response.text().await.map_err(MistralError::Network)?;

        if !status.is_success() {
            return match serde_json::from_str::<MistralApiError>(&response_text) {
                Ok(mut err) => {
                    err.description = crate::error_description(err.code).to_string();
                    Err(MistralError::Api(err))
                },
                Err(_) => Err(MistralError::Http(status)),
            };
        }

        serde_json::from_str(&response_text).map_err(MistralError::Parse)
    }
}