use crate::{
error::{json_error, process_error_response, reqwest_error},
request::{send_request, send_request_unprocessed},
utils::{build_header_map, create_client_with_project_id},
BlockfrostError, Integer, IpfsSettings, RetrySettings, IPFS_URL,
};
use blockfrost_openapi::models::_ipfs_pin_list__ipfs_path__get_200_response::IpfsPinListIpfsPathGet200Response;
use reqwest::{
multipart::{Form, Part},
ClientBuilder,
};
use serde::{Deserialize, Serialize};
use serde_json::from_str as json_from;
#[derive(Debug, Clone)]
pub struct BlockfrostIPFS {
pub base_url: String,
client: reqwest::Client,
pub settings: IpfsSettings,
}
impl BlockfrostIPFS {
pub fn new(project_id: &str, settings: IpfsSettings) -> Self {
let client = create_client_with_project_id(project_id, &settings.headers);
Self {
client,
settings,
base_url: IPFS_URL.to_string(),
}
}
pub fn new_with_client(
project_id: impl AsRef<str>, settings: IpfsSettings, client_builder: ClientBuilder,
) -> reqwest::Result<Self> {
client_builder
.default_headers(build_header_map(project_id.as_ref(), &settings.headers))
.build()
.map(|client| Self {
settings,
client,
base_url: IPFS_URL.to_string(),
})
}
pub async fn add(&self, file_contents: Vec<u8>) -> Result<IpfsAdd, BlockfrostError> {
let url = self.base_url.clone() + "/ipfs/add";
let part = Part::bytes(file_contents);
let form = Form::new().part("file", part);
let request = self.client.post(&url).multipart(form);
let (status, text) = send_request(request, self.settings.retry_settings)
.await
.map_err(|reason| reqwest_error(&url, reason))?;
if !status.is_success() {
return Err(process_error_response(&text, status, &url));
}
json_from(&text).map_err(|reason| json_error(url, text, reason))
}
pub async fn gateway(&self, ipfs_path: &str) -> Result<Vec<u8>, BlockfrostError> {
let url = self.base_url.clone() + &format!("/ipfs/gateway/{ipfs_path}");
let request = self.client.get(&url);
let response = send_request_unprocessed(request, self.retry_settings())
.await
.map_err(|reason| reqwest_error(&url, reason))?;
let status = response.status();
if !status.is_success() {
let text = response
.text()
.await
.map_err(|reason| reqwest_error(&url, reason))?;
Err(process_error_response(&text, status, &url))
} else {
let bytes = response
.bytes()
.await
.map_err(|reason| reqwest_error(&url, reason))?;
Ok(bytes.to_vec())
}
}
pub async fn pin_add(&self, ipfs_path: &str) -> Result<IpfsPinUpdate, BlockfrostError> {
let url = self.base_url.clone() + &format!("/ipfs/pin/add/{ipfs_path}");
let request = self.client.post(&url);
let (status, text) = send_request(request, self.settings.retry_settings)
.await
.map_err(|reason| reqwest_error(&url, reason))?;
if !status.is_success() {
return Err(process_error_response(&text, status, &url));
}
json_from(&text).map_err(|reason| json_error(url, text, reason))
}
pub async fn pin_list(
&self,
) -> Result<Vec<IpfsPinListIpfsPathGet200Response>, BlockfrostError> {
let url = self.base_url.clone() + "/ipfs/pin/list";
let request = self.client.get(&url);
let (status, text) = send_request(request, self.settings.retry_settings)
.await
.map_err(|reason| reqwest_error(&url, reason))?;
if !status.is_success() {
return Err(process_error_response(&text, status, &url));
}
json_from(&text).map_err(|reason| json_error(url, text, reason))
}
pub async fn pin_list_by_id(&self, ipfs_path: &str) -> Result<IpfsPinList, BlockfrostError> {
let url = self.base_url.clone() + &format!("/ipfs/pin/list/{ipfs_path}");
let request = self.client.get(&url);
let (status, text) = send_request(request, self.settings.retry_settings)
.await
.map_err(|reason| reqwest_error(&url, reason))?;
if !status.is_success() {
return Err(process_error_response(&text, status, &url));
}
json_from(&text).map_err(|reason| json_error(url, text, reason))
}
pub async fn pin_remove(&self, ipfs_path: &str) -> Result<IpfsPinUpdate, BlockfrostError> {
let url = self.base_url.clone() + &format!("/ipfs/pin/remove/{ipfs_path}");
let request = self.client.post(&url);
let (status, text) = send_request(request, self.settings.retry_settings)
.await
.map_err(|reason| reqwest_error(&url, reason))?;
if !status.is_success() {
return Err(process_error_response(&text, status, &url));
}
json_from(&text).map_err(|reason| json_error(url, text, reason))
}
pub(crate) fn retry_settings(&self) -> RetrySettings {
self.settings.retry_settings
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct IpfsAdd {
pub name: String,
pub ipfs_hash: String,
pub size: String,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct IpfsPinUpdate {
pub ipfs_hash: String,
pub state: IpfsPinState,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct IpfsPinList {
pub time_created: Integer,
pub time_pinned: Integer,
pub ipfs_hash: String,
pub size: String,
pub state: IpfsPinState,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub enum IpfsPinState {
Queued,
Pinned,
Unpinned,
Failed,
Gc,
}