-
-
Save makvoid/c6c65034d95abe53f745429b3b027cb9 to your computer and use it in GitHub Desktop.
"How to use Algolia as a game engine debugging tool in Rust" Example #3
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use serde::{Deserialize, Serialize, Serializer}; | |
use std::time::{Duration, SystemTime}; | |
use urlencoding; | |
// The credentials data | |
const ALGOLIA_AGENT: &str = "Algolia DataSender for Rust Dev (0.0.1)"; | |
// The reqwest client type used by the library | |
type ClientType = reqwest::blocking::Client; | |
// Generates unique IDs to use as ObjectIds | |
pub struct IdGenerator { | |
prefix: String, | |
idx: i32, | |
} | |
impl IdGenerator { | |
pub fn new(prefix: String) -> Self { | |
IdGenerator { prefix: prefix, idx: 0 } | |
} | |
pub fn new_from_time() -> Self { | |
let now = SystemTime::now(); | |
let prefix = format!("{:?}", now.duration_since(std::time::UNIX_EPOCH).unwrap_or_default()); | |
Self::new(prefix) | |
} | |
pub fn next_i32(&mut self) -> i32 { | |
let idx = self.idx; | |
self.idx += 1; | |
idx | |
} | |
pub fn next_string(&mut self) -> String { | |
let next_id = self.next_i32(); | |
format!("{}_{}", self.prefix.as_str(), next_id) | |
} | |
} | |
pub struct AlgoliaSender { | |
app_id: String, | |
api_key: String, | |
index_name: String, | |
data_buffer: Vec<String>, | |
client: ClientType, | |
id_generator: IdGenerator, | |
} | |
impl AlgoliaSender { | |
pub fn new(app_id: &str, api_key: &str, index_name: &str) -> Self { | |
AlgoliaSender { | |
app_id: String::from(app_id), | |
api_key: String::from(api_key), | |
index_name: String::from(index_name), | |
data_buffer: vec![], | |
client: ClientType::new(), | |
id_generator: IdGenerator::new_from_time(), | |
} | |
} | |
pub fn add_item<T>(&mut self, v: &T) | |
where | |
T: Serialize, | |
{ | |
match serde_json::to_string(v) { | |
Ok(serialized) => { | |
self.data_buffer.push(serialized); | |
} | |
Err(_) => {} | |
} | |
} | |
// Sends items to the ingestion endpoint in a batch job | |
pub fn send_items(&mut self) { | |
let index_name = self.index_name.clone(); | |
self.send_items_to_index(index_name.as_str()); | |
} | |
// Sends items to the ingestion endpoint in a batch job | |
pub fn send_items_to_index(&mut self, index_name: &str) { | |
// convert a pre-serialized JSON object into a request object for a batch request | |
fn build_batch_request_entry(id: &String, data: &String) -> Option<String> { | |
if data.len() <= 2 { | |
return None; | |
} | |
// Skip the opening curly brace and inject our objectID into the already serialized object | |
let remaining_data:String = data.chars().skip(1).collect(); | |
Some(format!("{{\"action\":\"updateObject\",\"body\":{{\"objectID\":\"{}\",{}}}", id, remaining_data)) | |
} | |
// wrap the individual requests into a batch | |
fn wrap_batch_request_entry(rows: &Vec<String>) -> String { | |
format!("{{\"requests\":[{}]}}", rows.join(",")) | |
} | |
// wrap the data | |
let data = wrap_batch_request_entry( | |
&self | |
.data_buffer | |
.iter() | |
.filter_map(|entry: &String| -> Option<String> { build_batch_request_entry(&self.id_generator.next_string(), entry) }) | |
.collect(), | |
); | |
// get the URI for the index | |
let uri = self.uri_for_index(index_name); | |
let uri_with_client = format!("{}?x-algolia-agent={}", uri, ALGOLIA_AGENT); | |
let res = self | |
.client | |
.post(uri_with_client) | |
.header("x-algolia-api-key", self.api_key.as_str()) | |
.header("x-algolia-application-id", self.app_id.as_str()) | |
.body(data) | |
.send(); | |
match res { | |
Err(_) => {} | |
Ok(resp) => { | |
let status = resp.status(); | |
let body = resp.text().unwrap_or_default(); | |
if status != 200 { | |
// log the problem for now | |
println!("ERROR while sending to Algolia: {}\n {}", status, body); | |
} | |
} | |
} | |
// TODO: this clean is tricky to place, as we want to keep unsent data, but it may accumulate to very large amounts | |
self.data_buffer.clear(); | |
} | |
// returns a full URI for an index name and the current app | |
fn uri_for_index(&self, index_name: &str) -> String { | |
// build the URI for the batch | |
let host = format!("{}.algolia.net", self.app_id.to_lowercase()); | |
let path = format!("/1/indexes/{}/batch", urlencoding::encode(index_name)); | |
format!("https://{}{}", host, path) | |
} | |
} | |
#[derive(Serialize, Debug)] | |
struct Float3 { | |
x: f32, | |
y: f32, | |
z: f32, | |
} | |
fn main() { | |
// The credentials data | |
const APP_ID: &str = ""; | |
const INDEX_NAME: &str = ""; | |
const API_KEY: &str = ""; | |
let mut sender = AlgoliaSender::new(APP_ID, API_KEY, INDEX_NAME); | |
sender.add_item(&Float3 { | |
x: 5.1, | |
y: 5.2, | |
z: 5.3, | |
}); | |
sender.add_item(&Float3 { | |
x: 6.1, | |
y: 6.2, | |
z: 6.3, | |
}); | |
sender.send_items(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment