#[allow(non_snake_case)]
pub mod client_builder;
pub mod error;
use error::EasyAlgoliaError;
use secrecy::{
ExposeSecret,
Secret,
};
pub mod algoliaobject;
use crate::algoliaobject::AlgoliaObject;
use reqwest::Client as Rq;
pub struct Index {
index: String,
}
impl Index {
fn index(&self) -> &str {
&self.index
}
}
impl From<String> for Index {
fn from(s: String) -> Self {
Self { index: s }
}
}
impl From<&str> for Index {
fn from(s: &str) -> Self {
Self {
index: String::from(s),
}
}
}
#[derive(Debug, Default, serde::Serialize, serde::Deserialize)]
pub struct AlgoliaIndexSetting {
pub min_word_size_for_1_typo: u32,
pub min_word_size_for_2_typos: u32,
pub hits_per_page: u32,
pub max_values_per_facet: u32,
pub version: u32,
#[serde(skip_serializing_if = "Option::is_none")]
pub searchable_attributes: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub numeric_attributes_to_index: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub attributes_to_retrieve: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub unretrievable_attributes: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub optional_words: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub attributes_for_faceting: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub attributes_to_snippet: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub attributes_to_highlight: Option<Vec<String>>,
pub pagination_limited_to: u32,
pub attribute_for_distinct: Option<String>,
pub exact_on_single_word_query: String,
pub ranking: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub custom_ranking: Option<Vec<String>>,
pub separators_to_index: String,
pub remove_words_if_no_results: String,
pub query_type: String,
pub highlight_pre_tag: String,
pub highlight_post_tag: String,
pub alternatives_as_exact: Vec<String>,
}
#[derive(serde::Serialize)]
pub struct ObjectId {
obj_id: String,
}
impl From<String> for ObjectId {
fn from(s: String) -> Self {
Self { obj_id: s }
}
}
impl From<&str> for ObjectId {
fn from(s: &str) -> Self {
Self {
obj_id: String::from(s),
}
}
}
impl AlgoliaObject for ObjectId {
fn get_object_id(&self) -> String {
String::from(&self.obj_id)
}
}
pub struct Client {
api_key: Secret<String>,
application_id: Secret<String>,
client: Rq,
}
impl Client {
pub(crate) fn new(api_key: &str, application_id: &str) -> Self {
Self {
api_key: Secret::new(String::from(api_key)),
application_id: Secret::new(String::from(application_id)),
client: Rq::new(),
}
}
pub async fn put_document_async<T: AlgoliaObject>(
&self,
index: &Index,
document: &T,
) -> Result<(), EasyAlgoliaError>
where
T: serde::Serialize + AlgoliaObject,
{
let mut is_object_is_present = false;
let path = match document.get_object_id().as_str() {
"" => {
format!(
"https://{}.algolia.net/1/indexes/{}",
&self.application_id.expose_secret(),
index.index(),
)
}
_ => {
is_object_is_present = true;
format!(
"https://{}.algolia.net/1/indexes/{}/{}",
&self.application_id.expose_secret(),
index.index(),
document.get_object_id()
)
}
};
let mut client = match is_object_is_present {
true => self.client.put(path),
false => self.client.post(path),
};
client = client.header("X-Algolia-API-Key", self.api_key.expose_secret());
client = client.header(
"X-Algolia-Application-Id",
self.application_id.expose_secret(),
);
client = client.json(&document);
match client.send().await {
Ok(k) => {
if k.status() > reqwest::StatusCode::from_u16(200).unwrap()
|| k.status() < reqwest::StatusCode::from_u16(200).unwrap()
{
return Err(EasyAlgoliaError::new(
error::ErrorKind::RequestError,
Some(k.text().await.unwrap()),
));
}
Ok(())
}
Err(err) => Err(err.into()),
}
}
pub fn put_document<T: AlgoliaObject>(
&self,
index: &Index,
document: &T,
) -> Result<(), EasyAlgoliaError>
where
T: serde::Serialize + AlgoliaObject,
{
let mut is_object_is_present = false;
let path = match document.get_object_id().as_str() {
"" => {
format!(
"https://{}.algolia.net/1/indexes/{}",
&self.application_id.expose_secret(),
index.index(),
)
}
_ => {
is_object_is_present = true;
format!(
"https://{}.algolia.net/1/indexes/{}/{}",
&self.application_id.expose_secret(),
index.index(),
document.get_object_id()
)
}
};
let mut client = match is_object_is_present {
true => self.client.put(path),
false => self.client.post(path),
};
client = client.header("X-Algolia-API-Key", self.api_key.expose_secret());
client = client.header(
"X-Algolia-Application-Id",
self.application_id.expose_secret(),
);
client = client.json(&document);
match futures::executor::block_on(client.send()) {
Ok(k) => {
if k.status() > reqwest::StatusCode::from_u16(200).unwrap()
|| k.status() < reqwest::StatusCode::from_u16(200).unwrap()
{
return Err(EasyAlgoliaError::new(
error::ErrorKind::RequestError,
Some(futures::executor::block_on(k.text()).unwrap()),
));
}
Ok(())
}
Err(err) => Err(err.into()),
}
}
pub async fn delete_document_async<T>(
&self,
index: &Index,
document: T,
) -> Result<(), EasyAlgoliaError>
where
T: serde::Serialize + AlgoliaObject,
{
let path = match document.get_object_id().as_str() {
"" => {
return Err(EasyAlgoliaError::new(
error::ErrorKind::RequestError,
Some("object id must be present for document delete method".into()),
));
}
_ => {
format!(
"https://{}.algolia.net/1/indexes/{}/{}",
&self.application_id.expose_secret(),
index.index(),
document.get_object_id()
)
}
};
let mut client = self.client.delete(path);
client = client.header("X-Algolia-API-Key", self.api_key.expose_secret());
client = client.header(
"X-Algolia-Application-Id",
self.application_id.expose_secret(),
);
match client.send().await {
Ok(k) => {
if k.status() > reqwest::StatusCode::from_u16(200).unwrap()
|| k.status() < reqwest::StatusCode::from_u16(200).unwrap()
{
return Err(EasyAlgoliaError::new(
error::ErrorKind::RequestError,
Some(k.text().await.unwrap()),
));
}
Ok(())
}
Err(err) => Err(err.into()),
}
}
pub fn delete_document<T>(&self, index: &Index, document: T) -> Result<(), EasyAlgoliaError>
where
T: serde::Serialize + AlgoliaObject,
{
let path = match document.get_object_id().as_str() {
"" => {
return Err(EasyAlgoliaError::new(
error::ErrorKind::RequestError,
Some("object id must be present for document delete method".into()),
));
}
_ => {
format!(
"https://{}.algolia.net/1/indexes/{}/{}",
&self.application_id.expose_secret(),
index.index(),
document.get_object_id()
)
}
};
let mut client = self.client.delete(path);
client = client.header("X-Algolia-API-Key", self.api_key.expose_secret());
client = client.header(
"X-Algolia-Application-Id",
self.application_id.expose_secret(),
);
match futures::executor::block_on(client.send()) {
Ok(k) => {
if k.status() > reqwest::StatusCode::from_u16(200).unwrap()
|| k.status() < reqwest::StatusCode::from_u16(200).unwrap()
{
return Err(EasyAlgoliaError::new(
error::ErrorKind::RequestError,
Some(futures::executor::block_on(k.text()).unwrap()),
));
}
Ok(())
}
Err(err) => Err(err.into()),
}
}
pub async fn get_index_setting<T: AlgoliaObject>(
&self,
index: &Index,
) -> Result<AlgoliaIndexSetting, EasyAlgoliaError>
where
T: serde::Serialize + AlgoliaObject,
{
let path = format!(
"https://{}.algolia.net/1/indexes/{}/settings",
&self.application_id.expose_secret(),
index.index()
);
let mut client = self.client.get(path);
client = client.header("X-Algolia-API-Key", self.api_key.expose_secret());
client = client.header(
"X-Algolia-Application-Id",
self.application_id.expose_secret(),
);
match client.send().await {
Ok(k) => {
if k.status() > reqwest::StatusCode::from_u16(200).unwrap()
|| k.status() < reqwest::StatusCode::from_u16(200).unwrap()
{
return Err(EasyAlgoliaError::new(
error::ErrorKind::RequestError,
Some(k.text().await.unwrap()),
));
} else {
let setting: AlgoliaIndexSetting = k
.json::<AlgoliaIndexSetting>()
.await
.map_err(|_| EasyAlgoliaError::new(error::ErrorKind::RequestError, None))?;
Ok(setting)
}
}
Err(err) => Err(err.into()),
}
}
pub async fn update_index_setting<T: AlgoliaObject>(
&self,
index: &Index,
setting: AlgoliaIndexSetting
) -> Result<(), EasyAlgoliaError>
where
T: serde::Serialize + AlgoliaObject,
{
let path = format!(
"https://{}.algolia.net/1/indexes/{}/settings",
&self.application_id.expose_secret(),
index.index()
);
let mut client = self.client.put(path);
client = client.header("X-Algolia-API-Key", self.api_key.expose_secret());
client = client.header(
"X-Algolia-Application-Id",
self.application_id.expose_secret(),
);
client = client.json(&setting) ;
match client.send().await {
Ok(k) => {
if k.status() > reqwest::StatusCode::from_u16(200).unwrap()
|| k.status() < reqwest::StatusCode::from_u16(200).unwrap()
{
return Err(EasyAlgoliaError::new(
error::ErrorKind::RequestError,
Some(k.text().await.unwrap()),
));
} else {
Ok(())
}
}
Err(err) => Err(err.into()),
}
}
}