rivalz_client 0.1.2

A Rust client for interacting with the Rivalz API, allowing file uploads and retrieval of IPFS hashes.
Documentation
use reqwest::blocking::{Client, multipart};
use reqwest::header::AUTHORIZATION;
use serde::Deserialize;
use std::env;
use std::fs::File;
use std::path::Path;

const RIVALZ_API_URL: &str = "https://be.rivalz.ai/api-v1";

#[derive(Deserialize)]
struct UploadResponse {
    data: UploadData,
}

#[derive(Deserialize)]
struct UploadData {
    uploadHash: String,
}

#[derive(Deserialize)]
struct DownloadResponse {
    data: DownloadData,
}

#[derive(Deserialize)]
struct DownloadData {
    file: FileData,
    name: String,
}

#[derive(Deserialize)]
struct FileData {
    data: String,
}

pub struct RivalzClient {
    secret: String,
    client: Client,
}

impl RivalzClient {
    pub fn new(secret: Option<String>) -> Self {
        let secret = secret.unwrap_or_else(|| env::var("SECRET_TOKEN").unwrap());
        let client = Client::new();
        RivalzClient { secret, client }
    }

    pub fn upload_file(&self, file_path: &str) -> Result<String, Box<dyn std::error::Error>> {
        // Ensure the file exists
        if !Path::new(file_path).exists() {
            return Err(format!("File not found: {}", file_path).into());
        }

        // Create a multipart form with the file
        let file = File::open(file_path)?;
        let file_part = multipart::Part::reader(file).file_name(file_path.to_string());
        let form = multipart::Form::new().part("file", file_part);

        // Perform the HTTP request to upload the file
        let res = self.client.post(format!("{}/ipfs/upload-file", RIVALZ_API_URL))
            .header(AUTHORIZATION, format!("Bearer {}", self.secret))
            .multipart(form)
            .send();

        // Handle network errors
        let res = match res {
            Ok(response) => response,
            Err(e) => return Err(format!("Network error: {}", e).into()),
        };

        // Handle HTTP status errors
        if res.status().is_client_error() || res.status().is_server_error() {
            return Err(format!("HTTP error: {}", res.status()).into());
        }

        // Parse the JSON response
        let upload_response: Result<UploadResponse, _> = res.json();
        let upload_response = match upload_response {
            Ok(response) => response,
            Err(e) => return Err(format!("Failed to parse JSON response: {}", e).into()),
        };

        // Retrieve the IPFS hash from the response
        match self.get_ipfs_hash(&upload_response.data.uploadHash) {
            Ok(hash) => Ok(hash),
            Err(e) => Err(format!("Failed to get IPFS hash: {}", e).into()),
        }
    }

    fn get_ipfs_hash(&self, upload_hash: &str) -> Result<String, Box<dyn std::error::Error>> {
        loop {
            let res = self.client.get(format!("{}/upload-history/{}", RIVALZ_API_URL, upload_hash))
                .header(AUTHORIZATION, format!("Bearer {}", self.secret))
                .send()?;

            let upload_info: UploadData = res.json()?;

            if !upload_info.uploadHash.is_empty() {
                return Ok(upload_info.uploadHash);
            }

            std::thread::sleep(std::time::Duration::from_secs(1));
        }
    }
}