#![cfg(feature = "rpc")]
use serde::Serialize;
use serde_json::{json, Value};
use crate::errors::Result;
use crate::query::Query;
use crate::request::headers::HeadersTypes;
use crate::request::Headers;
use crate::success::handle_response;
use crate::SupabaseClient;
use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
use reqwest::Response;
#[derive(Debug)]
pub struct RpcBuilder {
client: SupabaseClient,
function_name: String,
params: Value,
query: Query,
}
impl RpcBuilder {
pub fn new<T: Serialize>(client: SupabaseClient, function_name: &str, params: T) -> Self {
Self {
client,
function_name: function_name.to_string(),
params: serde_json::to_value(params).unwrap_or(json!({})),
query: Query::new(),
}
}
pub async fn execute(self) -> Result<Vec<Value>> {
self.execute_internal(false).await
}
pub async fn execute_single(self) -> Result<Value> {
let results = self.execute_internal(true).await?;
if results.len() == 1 {
Ok(results.into_iter().next().unwrap())
} else {
Err(crate::errors::ErrorTypes::UnknownError)
}
}
pub async fn execute_void(self) -> Result<()> {
let response = self.execute_request(true).await?;
let status = response.status();
if status == 204 {
Ok(())
} else {
let _error_body = response.text().await.unwrap_or_default();
Err(crate::errors::ErrorTypes::UnknownError)
}
}
async fn execute_internal(self, single: bool) -> Result<Vec<Value>> {
let response = self.execute_request(single).await?;
let status = response.status();
if !status.is_success() {
let _error_body = response.text().await.unwrap_or_default();
return Err(crate::errors::ErrorTypes::UnknownError);
}
handle_response(response)
.await
.map_err(|_e| crate::errors::ErrorTypes::UnknownError)
}
async fn execute_request(self, single: bool) -> Result<Response> {
let url = self.client.rpc_endpoint(&self.function_name);
let query_string = self.query.build();
let endpoint = if query_string.is_empty() {
url
} else {
format!("{}?{}", url, query_string)
};
let mut headers = Headers::with_defaults(&self.client.api_key, &self.client.api_key);
headers.insert(HeadersTypes::ContentType.as_str(), "application/json");
if self.client.schema != "public" {
headers.insert(HeadersTypes::ContentProfile.as_str(), &self.client.schema);
headers.insert(HeadersTypes::AcceptProfile.as_str(), &self.client.schema);
}
if single {
headers.insert("Accept", "application/vnd.pgrst.object+json");
}
let mut header_map = HeaderMap::new();
for (key, value) in headers.get_headers() {
header_map.insert(
HeaderName::from_bytes(key.as_bytes())
.map_err(|_| crate::errors::ErrorTypes::UnknownError)?,
HeaderValue::from_str(&value)
.map_err(|_| crate::errors::ErrorTypes::UnknownError)?,
);
}
let response = self
.client
.client
.post(&endpoint)
.headers(header_map)
.json(&self.params)
.send()
.await
.map_err(crate::errors::ErrorTypes::ReqwestError)?;
Ok(response)
}
}
impl RpcBuilder {
pub fn eq(mut self, column: &str, value: &str) -> Self {
self.query.add_param(column, &format!("eq.{value}"));
self
}
pub fn neq(mut self, column: &str, value: &str) -> Self {
self.query.add_param(column, &format!("neq.{value}"));
self
}
pub fn gt(mut self, column: &str, value: &str) -> Self {
self.query.add_param(column, &format!("gt.{value}"));
self
}
pub fn lt(mut self, column: &str, value: &str) -> Self {
self.query.add_param(column, &format!("lt.{value}"));
self
}
pub fn gte(mut self, column: &str, value: &str) -> Self {
self.query.add_param(column, &format!("gte.{value}"));
self
}
pub fn lte(mut self, column: &str, value: &str) -> Self {
self.query.add_param(column, &format!("lte.{value}"));
self
}
pub fn in_<T>(mut self, column: &str, values: &[T]) -> Self
where
T: ToString,
{
let list = values
.iter()
.map(ToString::to_string)
.collect::<Vec<_>>()
.join(",");
self.query.add_param(column, &format!("in.({})", list));
self
}
pub fn text_search(mut self, column: &str, value: &str) -> Self {
self.query.add_param(column, &format!("fts.{value}"));
self
}
pub fn limit(mut self, limit: usize) -> Self {
self.query.add_param("limit", &limit.to_string());
self
}
pub fn offset(mut self, offset: usize) -> Self {
self.query.add_param("offset", &offset.to_string());
self
}
pub fn range(mut self, from: usize, to: usize) -> Self {
self.query.set_range(from, to);
self
}
pub fn order(mut self, column: &str, ascending: bool) -> Self {
let order_value: &str = if ascending { "asc" } else { "desc" };
self.query
.add_param("order", &format!("{column}.{order_value}"));
self
}
pub fn count(mut self) -> Self {
self.query.add_param("count", "exact");
self
}
pub fn columns(mut self, columns: Vec<&str>) -> Self {
let columns_str: String = columns.join(",");
self.query.add_param("select", &columns_str);
self
}
}