use std::{fs, io};
use async_trait::async_trait;
use reqwest::{Client, ClientBuilder, Response};
use reqwest::header::HeaderMap;
use serde::de::DeserializeOwned;
use crate::api::data::{PinnedObject, PinByFile, PinByJson, PinByHash, PinByHashResult, UnPin};
use crate::data::StorageProvider;
use crate::errors::{ApiError, Error};
use crate::utils;
use serde::Deserialize;
use crate::utils::transform_file_to_form;
#[derive(Deserialize, Debug)]
pub(crate) struct PinataApiError {
error: String
}
impl PinataApiError {
pub fn message(&self) -> String {
self.error.clone()
}
}
#[derive(Debug)]
pub struct PinataProvider {
pub name: String,
pub api_url: String,
client: Client
}
impl PinataProvider {
pub fn new(api_key: Option<String>, secret_api_key: Option<String>) -> Result<PinataProvider, Error> {
let api_key = if let Some(api_key) = api_key { api_key } else { std::env::var("PINATA_API_KEY").expect("PINATA_API_KEY env required to run test") };
let secret_api_key = if let Some(secret_api_key) = secret_api_key { secret_api_key } else { std::env::var("PINATA_SECRET_API_KEY").expect("SECRET_API_KEY env required to run test") };
let mut default_headers = HeaderMap::new();
default_headers.insert("pinata_api_key", api_key.parse().unwrap());
default_headers.insert("pinata_secret_api_key", secret_api_key.parse().unwrap());
let client = ClientBuilder::new()
.default_headers(default_headers)
.build()?;
Ok(PinataProvider {
name: "Pinata Provider".to_string(),
api_url: "https://api.pinata.cloud".to_string(),
client
})
}
async fn parse_result<R>(&self, response: Response) -> Result<R, ApiError>
where R: DeserializeOwned
{
if response.status().is_success() {
let result = response.json::<R>().await?;
Ok(result)
} else {
let error = response.json::<PinataApiError>().await?;
println!("Error {:?}", error);
Err(ApiError::GenericError(error.message()))
}
}
async fn parse_ok_result(&self, response: Response) -> Result<(), ApiError>
{
if response.status().is_success() {
Ok(())
} else {
let error = response.json::<PinataApiError>().await?;
println!("Error {:?}", error);
Err(ApiError::GenericError(error.message()))
}
}
}
#[async_trait]
impl StorageProvider for PinataProvider {
fn name(&self) -> String {
"Pinata Provider".to_string()
}
fn init(&self) -> bool {
println!("Initializing Pinata");
let mut token: String = "".to_string();
println!("Enter your Pinata jwt key");
io::stdin()
.read_line(&mut token)
.expect("Please enter your jwt api key");
let token = utils::trim_newline(&mut token);
if token == String::from("") {
return false;
};
true
}
fn api_url(&self) -> String {
"https://api.pinata.cloud".to_string()
}
async fn pin_file(&self, pin_data: PinByFile) -> Result<PinnedObject, ApiError> {
let form = transform_file_to_form(&pin_data)?;
let response = self.client.post(format!("{}{}", &self.api_url, "/pinning/pinFileToIPFS"))
.multipart(form)
.send()
.await?;
self.parse_result(response).await
}
#[allow(unused_variables)]
async fn pin_json(&self, pin_data: PinByJson) -> Result<PinnedObject, ApiError> {
let file = fs::read_to_string(pin_data.file)?;
let data:serde_json::Value = serde_json::from_str(file.as_str()).expect("Could not parse json file");
let response = self.client.post(format!("{}{}", &self.api_url, "/pinning/pinJSONToIPFS"))
.json(&data)
.send()
.await?;
self.parse_result(response).await
}
#[allow(unused_variables)]
async fn pin_by_hash(&self, pin_data: PinByHash) -> Result<PinByHashResult, ApiError> {
let response = self.client.post(format!("{}{}", &self.api_url, "/pinning/pinByHash"))
.json(&pin_data)
.send()
.await?;
self.parse_result(response).await
}
#[allow(unused_variables)]
async fn pin_directory(&self) -> Result<(), ApiError> {
todo!()
}
#[allow(unused_variables)]
async fn unpin(&self, param: UnPin) -> Result<(), ApiError> {
let response = self.client.delete(format!("{}{}{}", &self.api_url, "/pinning/unpin/", ¶m.cid))
.send()
.await?;
self.parse_ok_result(response).await
}
}